sound/soc/xilinx/xlnx_i2s.c

Source file repositories/reference/linux-study-clean/sound/soc/xilinx/xlnx_i2s.c

File Facts

System
Linux kernel
Corpus path
sound/soc/xilinx/xlnx_i2s.c
Extension
.c
Size
7107 bytes
Lines
260
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

struct xlnx_i2s_drv_data {
	struct snd_soc_dai_driver dai_drv;
	void __iomem *base;
	unsigned int sysclk;
	u32 data_width;
	u32 channels;
	bool is_32bit_lrclk;
	struct snd_ratnum ratnum;
	struct snd_pcm_hw_constraint_ratnums rate_constraints;
};

static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai,
				    int div_id, int div)
{
	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(cpu_dai);

	if (!div || (div & ~I2S_I2STIM_VALID_MASK))
		return -EINVAL;

	drv_data->sysclk = 0;

	writel(div, drv_data->base + I2S_I2STIM_OFFSET);

	return 0;
}

static int xlnx_i2s_set_sysclk(struct snd_soc_dai *dai,
			       int clk_id, unsigned int freq, int dir)
{
	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(dai);

	drv_data->sysclk = freq;
	if (freq) {
		unsigned int bits_per_sample;

		if (drv_data->is_32bit_lrclk)
			bits_per_sample = 32;
		else
			bits_per_sample = drv_data->data_width;

		drv_data->ratnum.num = freq / (bits_per_sample * drv_data->channels) / 2;
		drv_data->ratnum.den_step = 1;
		drv_data->ratnum.den_min = 1;
		drv_data->ratnum.den_max = 255;
		drv_data->rate_constraints.rats = &drv_data->ratnum;
		drv_data->rate_constraints.nrats = 1;
	}
	return 0;
}

static int xlnx_i2s_startup(struct snd_pcm_substream *substream,
			    struct snd_soc_dai *dai)
{
	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(dai);

	if (drv_data->sysclk)
		return snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
						     SNDRV_PCM_HW_PARAM_RATE,
						     &drv_data->rate_constraints);

	return 0;
}

static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream,
			      struct snd_pcm_hw_params *params,
			      struct snd_soc_dai *i2s_dai)
{
	u32 reg_off, chan_id;
	struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai);

	if (drv_data->sysclk) {
		unsigned int bits_per_sample, sclk, sclk_div;

		if (drv_data->is_32bit_lrclk)
			bits_per_sample = 32;
		else
			bits_per_sample = drv_data->data_width;

		sclk = params_rate(params) * bits_per_sample * params_channels(params);
		sclk_div = drv_data->sysclk / sclk / 2;

		if ((drv_data->sysclk % sclk != 0) ||
		    !sclk_div || (sclk_div & ~I2S_I2STIM_VALID_MASK)) {
			dev_warn(i2s_dai->dev, "invalid SCLK divisor for sysclk %u and sclk %u\n",
				 drv_data->sysclk, sclk);
			return -EINVAL;
		}
		writel(sclk_div, drv_data->base + I2S_I2STIM_OFFSET);
	}

Annotation

Implementation Notes