Unverified Commit 125d0f69 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: stm32: i2s: add stm32mp25 support

Merge series from Olivier Moysan <olivier.moysan@foss.st.com>:

Update STM32 I2S driver and binding to support STM32MP25 SoCs.
parents b3ea5bec 20bf873d
Loading
Loading
Loading
Loading
+33 −3
Original line number Diff line number Diff line
@@ -13,13 +13,11 @@ description:
  The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
  Only some SPI instances support I2S.

allOf:
  - $ref: dai-common.yaml#

properties:
  compatible:
    enum:
      - st,stm32h7-i2s
      - st,stm32mp25-i2s

  "#sound-dai-cells":
    const: 0
@@ -33,6 +31,7 @@ properties:
      - description: clock feeding the internal clock generator.
      - description: I2S parent clock for sampling rates multiple of 8kHz.
      - description: I2S parent clock for sampling rates multiple of 11.025kHz.
    minItems: 2

  clock-names:
    items:
@@ -40,6 +39,7 @@ properties:
      - const: i2sclk
      - const: x8k
      - const: x11k
    minItems: 2

  interrupts:
    maxItems: 1
@@ -79,6 +79,36 @@ required:
  - dmas
  - dma-names

allOf:
  - $ref: dai-common.yaml#
  - if:
      properties:
        compatible:
          contains:
            const: st,stm32h7-i2s

    then:
      properties:
        clocks:
          minItems: 4

        clock-names:
          minItems: 4

  - if:
      properties:
        compatible:
          contains:
            const: st,stm32mp25-i2s

    then:
      properties:
        clocks:
          maxItems: 2

        clock-names:
          maxItems: 2

unevaluatedProperties: false

examples:
+189 −22
Original line number Diff line number Diff line
@@ -200,10 +200,13 @@ enum i2s_datlen {

#define STM32_I2S_NAME_LEN		32
#define STM32_I2S_RATE_11K		11025
#define STM32_I2S_MAX_SAMPLE_RATE_8K	192000
#define STM32_I2S_MAX_SAMPLE_RATE_11K	176400
#define STM32_I2S_CLK_RATE_TOLERANCE	1000 /* ppm */

/**
 * struct stm32_i2s_data - private data of I2S
 * @regmap_conf: I2S register map configuration pointer
 * @conf: I2S configuration pointer
 * @regmap: I2S register map pointer
 * @pdev: device data pointer
 * @dai_drv: DAI driver pointer
@@ -224,11 +227,14 @@ enum i2s_datlen {
 * @divider: prescaler division ratio
 * @div: prescaler div field
 * @odd: prescaler odd field
 * @i2s_clk_flg: flag set while exclusivity on I2S kernel clock is active
 * @refcount: keep count of opened streams on I2S
 * @ms_flg: master mode flag.
 * @set_i2s_clk_rate: set I2S kernel clock rate
 * @put_i2s_clk_rate: put I2S kernel clock rate
 */
struct stm32_i2s_data {
	const struct regmap_config *regmap_conf;
	const struct stm32_i2s_conf *conf;
	struct regmap *regmap;
	struct platform_device *pdev;
	struct snd_soc_dai_driver *dai_drv;
@@ -249,8 +255,21 @@ struct stm32_i2s_data {
	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 {
@@ -261,6 +280,8 @@ struct stm32_i2smclk_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)
@@ -312,6 +333,33 @@ static int stm32_i2s_set_clk_div(struct stm32_i2s_data *i2s)
				  cgfr_mask, cgfr);
}

static bool stm32_i2s_rate_accurate(struct stm32_i2s_data *i2s,
				    unsigned int max_rate, unsigned int rate)
{
	struct platform_device *pdev = i2s->pdev;
	u64 delta, dividend;
	int ratio;

	if (!rate) {
		dev_err(&pdev->dev, "Unexpected null rate\n");
		return false;
	}

	ratio = DIV_ROUND_CLOSEST(max_rate, rate);
	if (!ratio)
		return false;

	dividend = mul_u32_u32(1000000, abs(max_rate - (ratio * rate)));
	delta = div_u64(dividend, max_rate);

	if (delta <= STM32_I2S_CLK_RATE_TOLERANCE)
		return true;

	dev_dbg(&pdev->dev, "Rate [%u] not accurate\n", rate);

	return false;
}

static int stm32_i2s_set_parent_clock(struct stm32_i2s_data *i2s,
				      unsigned int rate)
{
@@ -332,6 +380,87 @@ static int stm32_i2s_set_parent_clock(struct stm32_i2s_data *i2s,
	return ret;
}

static void stm32_i2s_put_parent_rate(struct stm32_i2s_data *i2s)
{
	if (i2s->i2s_clk_flg) {
		i2s->i2s_clk_flg = false;
		clk_rate_exclusive_put(i2s->i2sclk);
	}
}

static int stm32_i2s_set_parent_rate(struct stm32_i2s_data *i2s,
				     unsigned int rate)
{
	struct platform_device *pdev = i2s->pdev;
	unsigned int i2s_clk_rate, i2s_clk_max_rate, i2s_curr_rate, i2s_new_rate;
	int ret, div;

	/*
	 * Set maximum expected kernel clock frequency
	 * - mclk on:
	 *   f_i2s_ck = MCKDIV * mclk-fs * fs
	 *   Here typical 256 ratio is assumed for mclk-fs
	 * - mclk off:
	 *   f_i2s_ck = MCKDIV * FRL * fs
	 *   Where FRL=[16,32], MCKDIV=[1..256]
	 *   f_i2s_ck = i2s_clk_max_rate * 32 / 256
	 */
	if (!(rate % STM32_I2S_RATE_11K))
		i2s_clk_max_rate = STM32_I2S_MAX_SAMPLE_RATE_11K * 256;
	else
		i2s_clk_max_rate = STM32_I2S_MAX_SAMPLE_RATE_8K * 256;

	if (!i2s->i2smclk)
		i2s_clk_max_rate /= 8;

	/* Request exclusivity, as the clock may be shared by I2S instances */
	clk_rate_exclusive_get(i2s->i2sclk);
	i2s->i2s_clk_flg = true;

	/*
	 * Check current kernel clock rate. If it gives the expected accuracy
	 * return immediately.
	 */
	i2s_curr_rate = clk_get_rate(i2s->i2sclk);
	if (stm32_i2s_rate_accurate(i2s, i2s_clk_max_rate, i2s_curr_rate))
		return 0;

	/*
	 * Otherwise try to set the maximum rate and check the new actual rate.
	 * If the new rate does not give the expected accuracy, try to set
	 * lower rates for the kernel clock.
	 */
	i2s_clk_rate = i2s_clk_max_rate;
	div = 1;
	do {
		/* Check new rate accuracy. Return if ok */
		i2s_new_rate = clk_round_rate(i2s->i2sclk, i2s_clk_rate);
		if (stm32_i2s_rate_accurate(i2s, i2s_clk_rate, i2s_new_rate)) {
			ret = clk_set_rate(i2s->i2sclk, i2s_clk_rate);
			if (ret) {
				dev_err(&pdev->dev, "Error %d setting i2s_clk_rate rate. %s",
					ret, ret == -EBUSY ?
					"Active stream rates may be in conflict\n" : "\n");
				goto err;
			}

			return 0;
		}

		/* Try a lower frequency */
		div++;
		i2s_clk_rate = i2s_clk_max_rate / div;
	} while (i2s_clk_rate > rate);

	/* no accurate rate found */
	dev_err(&pdev->dev, "Failed to find an accurate rate");

err:
	stm32_i2s_put_parent_rate(i2s);

	return -EINVAL;
}

static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate,
				     unsigned long *prate)
{
@@ -635,12 +764,16 @@ static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
				clk_rate_exclusive_put(i2s->i2smclk);
				i2s->mclk_rate = 0;
			}

			if (i2s->put_i2s_clk_rate)
				i2s->put_i2s_clk_rate(i2s);

			return regmap_update_bits(i2s->regmap,
						  STM32_I2S_CGFR_REG,
						  I2S_CGFR_MCKOE, 0);
		}
		/* If master clock is used, set parent clock now */
		ret = stm32_i2s_set_parent_clock(i2s, freq);
		ret = i2s->set_i2s_clk_rate(i2s, freq);
		if (ret)
			return ret;
		ret = clk_set_rate_exclusive(i2s->i2smclk, freq);
@@ -667,10 +800,11 @@ static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai,
	u32 cgfr;
	int ret;

	if (!(rate % 11025))
		clk_set_parent(i2s->i2sclk, i2s->x11kclk);
	else
		clk_set_parent(i2s->i2sclk, i2s->x8kclk);
	if (!i2s->mclk_rate) {
		ret = i2s->set_i2s_clk_rate(i2s, rate);
		if (ret)
			return ret;
	}
	i2s_clock_rate = clk_get_rate(i2s->i2sclk);

	/*
@@ -915,6 +1049,14 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream,

	clk_disable_unprepare(i2s->i2sclk);

	/*
	 * Release kernel clock if following conditions are fulfilled
	 * - Master clock is not used. Kernel clock won't be released trough sysclk
	 * - Put handler is defined. Involve that clock is managed exclusively
	 */
	if (!i2s->i2smclk && i2s->put_i2s_clk_rate)
		i2s->put_i2s_clk_rate(i2s);

	spin_lock_irqsave(&i2s->irq_lock, flags);
	i2s->substream = NULL;
	spin_unlock_irqrestore(&i2s->irq_lock, flags);
@@ -1012,14 +1154,36 @@ static int stm32_i2s_dais_init(struct platform_device *pdev,
	return 0;
}

static const struct stm32_i2s_conf stm32_i2s_conf_h7 = {
	.regmap_conf = &stm32_h7_i2s_regmap_conf,
	.get_i2s_clk_parent = stm32_i2s_get_parent_clk,
};

static const struct stm32_i2s_conf stm32_i2s_conf_mp25 = {
	.regmap_conf = &stm32_h7_i2s_regmap_conf
};

static const struct of_device_id stm32_i2s_ids[] = {
	{
		.compatible = "st,stm32h7-i2s",
		.data = &stm32_h7_i2s_regmap_conf
	},
	{ .compatible = "st,stm32h7-i2s", .data = &stm32_i2s_conf_h7 },
	{ .compatible = "st,stm32mp25-i2s", .data = &stm32_i2s_conf_mp25 },
	{},
};

static int stm32_i2s_get_parent_clk(struct stm32_i2s_data *i2s)
{
	struct device *dev = &i2s->pdev->dev;

	i2s->x8kclk = devm_clk_get(dev, "x8k");
	if (IS_ERR(i2s->x8kclk))
		return dev_err_probe(dev, PTR_ERR(i2s->x8kclk), "Cannot get x8k parent clock\n");

	i2s->x11kclk = devm_clk_get(dev, "x11k");
	if (IS_ERR(i2s->x11kclk))
		return dev_err_probe(dev, PTR_ERR(i2s->x11kclk), "Cannot get x11k parent clock\n");

	return 0;
}

static int stm32_i2s_parse_dt(struct platform_device *pdev,
			      struct stm32_i2s_data *i2s)
{
@@ -1031,8 +1195,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
	if (!np)
		return -ENODEV;

	i2s->regmap_conf = device_get_match_data(&pdev->dev);
	if (!i2s->regmap_conf)
	i2s->conf = device_get_match_data(&pdev->dev);
	if (!i2s->conf)
		return -EINVAL;

	i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
@@ -1052,15 +1216,18 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
		return dev_err_probe(&pdev->dev, PTR_ERR(i2s->i2sclk),
				     "Could not get i2sclk\n");

	i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k");
	if (IS_ERR(i2s->x8kclk))
		return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x8kclk),
				     "Could not get x8k parent clock\n");
	if (i2s->conf->get_i2s_clk_parent) {
		i2s->set_i2s_clk_rate = stm32_i2s_set_parent_clock;
	} else {
		i2s->set_i2s_clk_rate = stm32_i2s_set_parent_rate;
		i2s->put_i2s_clk_rate = stm32_i2s_put_parent_rate;
	}

	i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k");
	if (IS_ERR(i2s->x11kclk))
		return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x11kclk),
				     "Could not get x11k parent clock\n");
	if (i2s->conf->get_i2s_clk_parent) {
		ret = i2s->conf->get_i2s_clk_parent(i2s);
		if (ret)
			return ret;
	}

	/* Register mclk provider if requested */
	if (of_property_present(np, "#clock-cells")) {
@@ -1126,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev)
		return ret;

	i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk",
						i2s->base, i2s->regmap_conf);
						i2s->base, i2s->conf->regmap_conf);
	if (IS_ERR(i2s->regmap))
		return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap),
				     "Regmap init error\n");