sound/soc/stm/stm32_i2s.c

Source file repositories/reference/linux-study-clean/sound/soc/stm/stm32_i2s.c

File Facts

System
Linux kernel
Corpus path
sound/soc/stm/stm32_i2s.c
Extension
.c
Size
36295 bytes
Lines
1388
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 stm32_i2s_data {
	const struct stm32_i2s_conf *conf;
	struct regmap *regmap;
	struct platform_device *pdev;
	struct snd_soc_dai_driver *dai_drv;
	struct snd_dmaengine_dai_dma_data dma_data_tx;
	struct snd_dmaengine_dai_dma_data dma_data_rx;
	struct snd_pcm_substream *substream;
	struct clk *i2sclk;
	struct clk *i2smclk;
	struct clk *pclk;
	struct clk *x8kclk;
	struct clk *x11kclk;
	void __iomem *base;
	dma_addr_t phys_addr;
	spinlock_t lock_fd; /* Manage race conditions for full duplex */
	spinlock_t irq_lock; /* used to prevent race condition with IRQ */
	unsigned int mclk_rate;
	unsigned int fmt;
	unsigned int divider;
	unsigned int div;
	bool odd;
	bool i2s_clk_flg;
	int refcount;
	int ms_flg;
	int (*set_i2s_clk_rate)(struct stm32_i2s_data *i2s, unsigned int rate);
	void (*put_i2s_clk_rate)(struct stm32_i2s_data *i2s);
};

/**
 * struct stm32_i2s_conf - I2S configuration
 * @regmap_conf: regmap configuration pointer
 * @get_i2s_clk_parent: get parent clock of I2S kernel clock
 */
struct stm32_i2s_conf {
	const struct regmap_config *regmap_conf;
	int (*get_i2s_clk_parent)(struct stm32_i2s_data *i2s);
};

struct stm32_i2smclk_data {
	struct clk_hw hw;
	unsigned long freq;
	struct stm32_i2s_data *i2s_data;
};

#define to_mclk_data(_hw) container_of(_hw, struct stm32_i2smclk_data, hw)

static int stm32_i2s_get_parent_clk(struct stm32_i2s_data *i2s);

static int stm32_i2s_calc_clk_div(struct stm32_i2s_data *i2s,
				  unsigned long input_rate,
				  unsigned long output_rate)
{
	unsigned int ratio, div, divider = 1;
	bool odd;

	ratio = DIV_ROUND_CLOSEST(input_rate, output_rate);

	/* Check the parity of the divider */
	odd = ratio & 0x1;

	/* Compute the div prescaler */
	div = ratio >> 1;

	/* If div is 0 actual divider is 1 */
	if (div) {
		divider = ((2 * div) + odd);
		dev_dbg(&i2s->pdev->dev, "Divider: 2*%d(div)+%d(odd) = %d\n",
			div, odd, divider);
	}

	/* Division by three is not allowed by I2S prescaler */
	if ((div == 1 && odd) || div > I2S_CGFR_I2SDIV_MAX) {
		dev_err(&i2s->pdev->dev, "Wrong divider setting\n");
		return -EINVAL;
	}

	if (input_rate % divider)
		dev_dbg(&i2s->pdev->dev,
			"Rate not accurate. requested (%ld), actual (%ld)\n",
			output_rate, input_rate / divider);

	i2s->div = div;
	i2s->odd = odd;
	i2s->divider = divider;

	return 0;
}

static int stm32_i2s_set_clk_div(struct stm32_i2s_data *i2s)

Annotation

Implementation Notes