sound/soc/fsl/fsl_sai.c

Source file repositories/reference/linux-study-clean/sound/soc/fsl/fsl_sai.c

File Facts

System
Linux kernel
Corpus path
sound/soc/fsl/fsl_sai.c
Extension
.c
Size
55513 bytes
Lines
2003
Domain
Driver Families
Bucket
sound/soc
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (diff < bestdiff) {
			savediv = ratio;
			sai->mclk_id[tx] = id;
			bestdiff = diff;
		}

		if (diff == 0)
			break;
	}

	if (savediv == 0) {
		dev_err(dai->dev, "failed to derive required %cx rate: %d\n",
				tx ? 'T' : 'R', freq);
		return -EINVAL;
	}

	dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
			sai->mclk_id[tx], savediv, bestdiff);

	/*
	 * 1) For Asynchronous mode, we must set RCR2 register for capture, and
	 *    set TCR2 register for playback.
	 * 2) For Tx sync with Rx clock, we must set RCR2 register for playback
	 *    and capture.
	 * 3) For Rx sync with Tx clock, we must set TCR2 register for playback
	 *    and capture.
	 * 4) For Tx and Rx are both Synchronous with another SAI, we just
	 *    ignore it.
	 */
	if (fsl_sai_dir_is_synced(sai, adir))
		reg = FSL_SAI_xCR2(!tx, ofs);
	else if (!sai->synchronous[dir])
		reg = FSL_SAI_xCR2(tx, ofs);
	else
		return 0;

	regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
			   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));

	if (savediv == 1) {
		regmap_update_bits(sai->regmap, reg,
				   FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
				   FSL_SAI_CR2_BYP);
		if (fsl_sai_dir_is_synced(sai, adir))
			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
					   FSL_SAI_CR2_BCI, FSL_SAI_CR2_BCI);
		else
			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
					   FSL_SAI_CR2_BCI, 0);
	} else {
		regmap_update_bits(sai->regmap, reg,
				   FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
				   savediv / 2 - 1);
	}

	return 0;
}

static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
		struct snd_pcm_hw_params *params,
		struct snd_soc_dai *cpu_dai)
{
	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
	unsigned int ofs = sai->soc_data->reg_offset;
	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
	unsigned int channels = params_channels(params);
	struct snd_dmaengine_dai_dma_data *dma_params;
	struct fsl_sai_dl_cfg *dl_cfg = sai->dl_cfg;
	u32 word_width = params_width(params);
	int trce_mask = 0, dl_cfg_idx = 0;
	int dl_cfg_cnt = sai->dl_cfg_cnt;
	u32 dl_type = FSL_SAI_DL_I2S;
	u32 val_cr4 = 0, val_cr5 = 0;
	u32 slots = (channels == 1) ? 2 : channels;
	u32 slot_width = word_width;
	int adir = tx ? RX : TX;
	u32 pins, bclk;
	u32 watermark;
	int ret, i;

	if (sai->slot_width[tx])
		slot_width = sai->slot_width[tx];

	if (sai->slots[tx])
		slots = sai->slots[tx];
	else if (sai->bclk_ratio)
		slots = sai->bclk_ratio / slot_width;

	pins = DIV_ROUND_UP(channels, slots);

Annotation

Implementation Notes