diff options
Diffstat (limited to 'sound/soc/stm')
| -rw-r--r-- | sound/soc/stm/Kconfig | 2 | ||||
| -rw-r--r-- | sound/soc/stm/Makefile | 8 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_adfsdm.c | 34 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_i2s.c | 246 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_sai.c | 78 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_sai.h | 6 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 282 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_spdifrx.c | 22 |
8 files changed, 514 insertions, 164 deletions
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index da1f7a16605b..2753d6c2a826 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "STMicroelectronics STM32 SOC audio support" +menu "STMicroelectronics STM32" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index 3143c0b47042..3372432faa09 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -1,17 +1,17 @@ # SPDX-License-Identifier: GPL-2.0 # SAI -snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o +snd-soc-stm32-sai-sub-y := stm32_sai_sub.o obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o -snd-soc-stm32-sai-objs := stm32_sai.o +snd-soc-stm32-sai-y := stm32_sai.o obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai.o # I2S -snd-soc-stm32-i2s-objs := stm32_i2s.o +snd-soc-stm32-i2s-y := stm32_i2s.o obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o # SPDIFRX -snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o +snd-soc-stm32-spdifrx-y := stm32_spdifrx.o obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o #DFSDM diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 837c1848d9bf..1797a91fea7a 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -142,7 +142,7 @@ static const struct snd_soc_dai_driver stm32_adfsdm_dai = { SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &stm32_adfsdm_dai_ops, }; @@ -167,7 +167,7 @@ static void stm32_memcpy_32to16(void *dest, const void *src, size_t n) static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) { struct stm32_adfsdm_priv *priv = private; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(priv->substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(priv->substream); u8 *pcm_buff = priv->pcm_buff; u8 *src_buff = (u8 *)data; unsigned int old_pos = priv->pos; @@ -180,7 +180,7 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) src_size >>= 1; cur_size = src_size; - dev_dbg(rtd->dev, "%s: buff_add :%pK, pos = %d, size = %zu\n", + dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n", __func__, &pcm_buff[priv->pos], priv->pos, src_size); if ((priv->pos + src_size) > buff_size) { @@ -212,9 +212,9 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) static int stm32_adfsdm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -233,8 +233,8 @@ static int stm32_adfsdm_trigger(struct snd_soc_component *component, static int stm32_adfsdm_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); int ret; ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw); @@ -247,9 +247,9 @@ static int stm32_adfsdm_pcm_open(struct snd_soc_component *component, static int stm32_adfsdm_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); priv->substream = NULL; @@ -260,9 +260,9 @@ static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer( struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); return bytes_to_frames(substream->runtime, priv->pos); } @@ -271,9 +271,9 @@ static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); priv->pcm_buff = substream->runtime->dma_area; @@ -286,7 +286,7 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, { struct snd_pcm *pcm = rtd->pcm; struct stm32_adfsdm_priv *priv = - snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -309,7 +309,7 @@ static void stm32_adfsdm_cleanup(void *data) iio_channel_release_all_cb(data); } -static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { +static const struct snd_soc_component_driver stm32_adfsdm_soc_platform = { .open = stm32_adfsdm_pcm_open, .close = stm32_adfsdm_pcm_close, .hw_params = stm32_adfsdm_pcm_hw_params, @@ -386,12 +386,10 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) return ret; } -static int stm32_adfsdm_remove(struct platform_device *pdev) +static void stm32_adfsdm_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static struct platform_driver stm32_adfsdm_driver = { diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index f3dd9f8e621c..6ca21780f21d 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -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,20 +380,103 @@ static int stm32_i2s_set_parent_clock(struct stm32_i2s_data *i2s, return ret; } -static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +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 int stm32_i2smclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_i2smclk_data *mclk = to_mclk_data(hw); struct stm32_i2s_data *i2s = mclk->i2s_data; int ret; - ret = stm32_i2s_calc_clk_div(i2s, *prate, rate); + ret = stm32_i2s_calc_clk_div(i2s, req->best_parent_rate, req->rate); if (ret) return ret; - mclk->freq = *prate / i2s->divider; + mclk->freq = req->best_parent_rate / i2s->divider; - return mclk->freq; + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_i2smclk_recalc_rate(struct clk_hw *hw, @@ -401,7 +532,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_i2smclk_enable, .disable = stm32_i2smclk_disable, .recalc_rate = stm32_i2smclk_recalc_rate, - .round_rate = stm32_i2smclk_round_rate, + .determine_rate = stm32_i2smclk_determine_rate, .set_rate = stm32_i2smclk_set_rate, }; @@ -635,12 +766,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 +802,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); /* @@ -823,7 +959,7 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* Enable i2s */ dev_dbg(cpu_dai->dev, "start I2S %s\n", - playback_flg ? "playback" : "capture"); + snd_pcm_direction_name(substream->stream)); cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, @@ -869,7 +1005,7 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dev_dbg(cpu_dai->dev, "stop I2S %s\n", - playback_flg ? "playback" : "capture"); + snd_pcm_direction_name(substream->stream)); if (playback_flg) regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, @@ -915,6 +1051,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); @@ -953,6 +1097,7 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { + .probe = stm32_i2s_dai_probe, .set_sysclk = stm32_i2s_set_sysclk, .set_fmt = stm32_i2s_set_dai_fmt, .startup = stm32_i2s_startup, @@ -1002,7 +1147,6 @@ static int stm32_i2s_dais_init(struct platform_device *pdev, if (!dai_ptr) return -ENOMEM; - dai_ptr->probe = stm32_i2s_dai_probe; dai_ptr->ops = &stm32_i2s_pcm_dai_ops; dai_ptr->id = 1; stm32_i2s_dai_init(&dai_ptr->playback, "playback"); @@ -1012,19 +1156,40 @@ 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) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id; struct reset_control *rst; struct resource *res; int irq, ret; @@ -1032,10 +1197,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, if (!np) return -ENODEV; - of_id = of_match_device(stm32_i2s_ids, &pdev->dev); - if (of_id) - i2s->regmap_conf = (const struct regmap_config *)of_id->data; - else + i2s->conf = device_get_match_data(&pdev->dev); + if (!i2s->conf) return -EINVAL; i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -1055,18 +1218,21 @@ 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_find_property(np, "#clock-cells", NULL)) { + if (of_property_present(np, "#clock-cells")) { ret = stm32_i2s_add_mclk_provider(i2s); if (ret < 0) return ret; @@ -1097,13 +1263,11 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, return 0; } -static int stm32_i2s_remove(struct platform_device *pdev) +static void stm32_i2s_remove(struct platform_device *pdev) { snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static int stm32_i2s_probe(struct platform_device *pdev) @@ -1131,7 +1295,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"); @@ -1190,7 +1354,6 @@ error: MODULE_DEVICE_TABLE(of, stm32_i2s_ids); -#ifdef CONFIG_PM_SLEEP static int stm32_i2s_suspend(struct device *dev) { struct stm32_i2s_data *i2s = dev_get_drvdata(dev); @@ -1208,17 +1371,16 @@ static int stm32_i2s_resume(struct device *dev) regcache_cache_only(i2s->regmap, false); return regcache_sync(i2s->regmap); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_i2s_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) + SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) }; static struct platform_driver stm32_i2s_driver = { .driver = { .name = "st,stm32-i2s", .of_match_table = stm32_i2s_ids, - .pm = &stm32_i2s_pm_ops, + .pm = pm_ptr(&stm32_i2s_pm_ops), }, .probe = stm32_i2s_probe, .remove = stm32_i2s_remove, diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 8e21e6f886fc..00cf24ceca2d 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -19,26 +19,42 @@ #include "stm32_sai.h" +static int stm32_sai_get_parent_clk(struct stm32_sai_data *sai); + static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = STM_SAI_STM32F4, .fifo_size = 8, .has_spdif_pdm = false, + .get_sai_ck_parent = stm32_sai_get_parent_clk, }; /* - * Default settings for stm32 H7 socs and next. + * Default settings for STM32H7x socs and STM32MP1x. * These default settings will be overridden if the soc provides * support of hardware configuration registers. + * - STM32H7: rely on default settings + * - STM32MP1: retrieve settings from registers */ static const struct stm32_sai_conf stm32_sai_conf_h7 = { .version = STM_SAI_STM32H7, .fifo_size = 8, .has_spdif_pdm = true, + .get_sai_ck_parent = stm32_sai_get_parent_clk, +}; + +/* + * STM32MP2x: + * - do not use SAI parent clock source selection + * - do not use DMA burst mode + */ +static const struct stm32_sai_conf stm32_sai_conf_mp25 = { + .no_dma_burst = true, }; static const struct of_device_id stm32_sai_ids[] = { { .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 }, { .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 }, + { .compatible = "st,stm32mp25-sai", .data = (void *)&stm32_sai_conf_mp25 }, {} }; @@ -122,37 +138,48 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, if (!pdev) { dev_err(&sai_client->pdev->dev, "Device not found for node %pOFn\n", np_provider); - of_node_put(np_provider); return -ENODEV; } sai_provider = platform_get_drvdata(pdev); + put_device(&pdev->dev); if (!sai_provider) { dev_err(&sai_client->pdev->dev, "SAI sync provider data not found\n"); - ret = -EINVAL; - goto error; + return -EINVAL; } /* Configure sync client */ ret = stm32_sai_sync_conf_client(sai_client, synci); if (ret < 0) - goto error; + return ret; /* Configure sync provider */ - ret = stm32_sai_sync_conf_provider(sai_provider, synco); + return stm32_sai_sync_conf_provider(sai_provider, synco); +} -error: - put_device(&pdev->dev); - of_node_put(np_provider); - return ret; +static int stm32_sai_get_parent_clk(struct stm32_sai_data *sai) +{ + struct device *dev = &sai->pdev->dev; + + sai->clk_x8k = devm_clk_get(dev, "x8k"); + if (IS_ERR(sai->clk_x8k)) + return dev_err_probe(dev, PTR_ERR(sai->clk_x8k), + "missing x8k parent clock\n"); + + sai->clk_x11k = devm_clk_get(dev, "x11k"); + if (IS_ERR(sai->clk_x11k)) + return dev_err_probe(dev, PTR_ERR(sai->clk_x11k), + "missing x11k parent clock\n"); + + return 0; } static int stm32_sai_probe(struct platform_device *pdev) { struct stm32_sai_data *sai; + const struct stm32_sai_conf *conf; struct reset_control *rst; - const struct of_device_id *of_id; u32 val; int ret; @@ -160,13 +187,15 @@ static int stm32_sai_probe(struct platform_device *pdev) if (!sai) return -ENOMEM; + sai->pdev = pdev; + sai->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sai->base)) return PTR_ERR(sai->base); - of_id = of_match_device(stm32_sai_ids, &pdev->dev); - if (of_id) - memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data, + conf = device_get_match_data(&pdev->dev); + if (conf) + memcpy(&sai->conf, (const struct stm32_sai_conf *)conf, sizeof(struct stm32_sai_conf)); else return -EINVAL; @@ -178,15 +207,11 @@ static int stm32_sai_probe(struct platform_device *pdev) "missing bus clock pclk\n"); } - sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(sai->clk_x8k)) - return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k), - "missing x8k parent clock\n"); - - sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(sai->clk_x11k)) - return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k), - "missing x11k parent clock\n"); + if (sai->conf.get_sai_ck_parent) { + ret = sai->conf.get_sai_ck_parent(sai); + if (ret) + return ret; + } /* init irqs */ sai->irq = platform_get_irq(pdev, 0); @@ -227,14 +252,12 @@ static int stm32_sai_probe(struct platform_device *pdev) } clk_disable_unprepare(sai->pclk); - sai->pdev = pdev; sai->set_sync = &stm32_sai_set_sync; platform_set_drvdata(pdev, sai); return devm_of_platform_populate(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP /* * When pins are shared by two sai sub instances, pins have to be defined * in sai parent node. In this case, pins state is not managed by alsa fw. @@ -269,10 +292,9 @@ static int stm32_sai_resume(struct device *dev) return pinctrl_pm_select_default_state(dev); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_sai_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) + SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) }; MODULE_DEVICE_TABLE(of, stm32_sai_ids); @@ -281,7 +303,7 @@ static struct platform_driver stm32_sai_driver = { .driver = { .name = "st,stm32-sai", .of_match_table = stm32_sai_ids, - .pm = &stm32_sai_pm_ops, + .pm = pm_ptr(&stm32_sai_pm_ops), }, .probe = stm32_sai_probe, }; diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 33e4bff8c2f5..07b71133db2a 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -264,16 +264,22 @@ enum stm32_sai_syncout { STM_SAI_SYNC_OUT_B, }; +struct stm32_sai_data; + /** * struct stm32_sai_conf - SAI configuration + * @get_sai_ck_parent: get parent clock of SAI kernel clock * @version: SAI version * @fifo_size: SAI fifo size as words number * @has_spdif_pdm: SAI S/PDIF and PDM features support flag + * @no_dma_burst: Support only DMA single transfers if set */ struct stm32_sai_conf { + int (*get_sai_ck_parent)(struct stm32_sai_data *sai); u32 version; u32 fifo_size; bool has_spdif_pdm; + bool no_dma_burst; }; /** diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index eb31b49e6597..450e1585edee 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -53,13 +53,16 @@ #define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) #define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf.has_spdif_pdm) #define STM_SAI_HAS_PDM(x) ((x)->pdata->conf.has_spdif_pdm) -#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) +#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4((x)->pdata)) #define SAI_IEC60958_BLOCK_FRAMES 192 #define SAI_IEC60958_STATUS_BYTES 24 #define SAI_MCLK_NAME_LEN 32 #define SAI_RATE_11K 11025 +#define SAI_MAX_SAMPLE_RATE_8K 192000 +#define SAI_MAX_SAMPLE_RATE_11K 176400 +#define SAI_CK_RATE_TOLERANCE 1000 /* ppm */ /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) @@ -80,6 +83,7 @@ * @dir: SAI block direction (playback or capture). set at init * @master: SAI block mode flag. (true=master, false=slave) set at init * @spdif: SAI S/PDIF iec60958 mode flag. set at init + * @sai_ck_used: flag set while exclusivity on SAI kernel clock is active * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) @@ -93,6 +97,8 @@ * @iec958: iec958 data * @ctrl_lock: control lock * @irq_lock: prevent race condition with IRQ + * @set_sai_ck_rate: set SAI kernel clock rate + * @put_sai_ck_rate: put SAI kernel clock rate */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -112,6 +118,7 @@ struct stm32_sai_sub_data { int dir; bool master; bool spdif; + bool sai_ck_used; int fmt; int sync; int synco; @@ -125,6 +132,8 @@ struct stm32_sai_sub_data { struct snd_aes_iec958 iec958; struct mutex ctrl_lock; /* protect resources accessed by controls */ spinlock_t irq_lock; /* used to prevent race condition with IRQ */ + int (*set_sai_ck_rate)(struct stm32_sai_sub_data *sai, unsigned int rate); + void (*put_sai_ck_rate)(struct stm32_sai_sub_data *sai); }; enum stm32_sai_fifo_th { @@ -317,7 +326,7 @@ static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, int div; div = DIV_ROUND_CLOSEST(input_rate, output_rate); - if (div > SAI_XCR1_MCKDIV_MAX(version)) { + if (div > SAI_XCR1_MCKDIV_MAX(version) || div <= 0) { dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); return -EINVAL; } @@ -351,8 +360,26 @@ static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, return ret; } -static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, - unsigned int rate) +static bool stm32_sai_rate_accurate(unsigned int max_rate, unsigned int rate) +{ + u64 delta, dividend; + int ratio; + + 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 <= SAI_CK_RATE_TOLERANCE) + return true; + + return false; +} + +static int stm32_sai_set_parent_clk(struct stm32_sai_sub_data *sai, + unsigned int rate) { struct platform_device *pdev = sai->pdev; struct clk *parent_clk = sai->pdata->clk_x8k; @@ -370,20 +397,114 @@ static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, return ret; } -static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static void stm32_sai_put_parent_rate(struct stm32_sai_sub_data *sai) +{ + if (sai->sai_ck_used) { + sai->sai_ck_used = false; + clk_rate_exclusive_put(sai->sai_ck); + } +} + +static int stm32_sai_set_parent_rate(struct stm32_sai_sub_data *sai, + unsigned int rate) +{ + struct platform_device *pdev = sai->pdev; + unsigned int sai_ck_rate, sai_ck_max_rate, sai_ck_min_rate, sai_curr_rate, sai_new_rate; + int div, ret; + + /* + * Set minimum and maximum expected kernel clock frequency + * - mclk on or spdif: + * f_sai_ck = MCKDIV * mclk-fs * fs + * Here typical 256 ratio is assumed for mclk-fs + * - mclk off: + * f_sai_ck = MCKDIV * FRL * fs + * Where FRL=[8..256], MCKDIV=[1..n] (n depends on SAI version) + * Set constraint MCKDIV * FRL <= 256, to ensure MCKDIV is in available range + * f_sai_ck = sai_ck_max_rate * pow_of_two(FRL) / 256 + */ + sai_ck_min_rate = rate * 256; + if (!(rate % SAI_RATE_11K)) + sai_ck_max_rate = SAI_MAX_SAMPLE_RATE_11K * 256; + else + sai_ck_max_rate = SAI_MAX_SAMPLE_RATE_8K * 256; + + if (!sai->sai_mclk && !STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + sai_ck_min_rate = rate * sai->fs_length; + sai_ck_max_rate /= DIV_ROUND_CLOSEST(256, roundup_pow_of_two(sai->fs_length)); + } + + /* + * Request exclusivity, as the clock is shared by SAI sub-blocks and by + * some SAI instances. This allows to ensure that the rate cannot be + * changed while one or more SAIs are using the clock. + */ + clk_rate_exclusive_get(sai->sai_ck); + sai->sai_ck_used = true; + + /* + * Check current kernel clock rate. If it gives the expected accuracy + * return immediately. + */ + sai_curr_rate = clk_get_rate(sai->sai_ck); + dev_dbg(&pdev->dev, "kernel clock rate: min [%u], max [%u], current [%u]", + sai_ck_min_rate, sai_ck_max_rate, sai_curr_rate); + if (stm32_sai_rate_accurate(sai_ck_max_rate, sai_curr_rate) && + sai_curr_rate >= sai_ck_min_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. + */ + sai_ck_rate = sai_ck_max_rate; + div = 1; + do { + /* Check new rate accuracy. Return if ok */ + sai_new_rate = clk_round_rate(sai->sai_ck, sai_ck_rate); + if (stm32_sai_rate_accurate(sai_ck_rate, sai_new_rate)) { + ret = clk_set_rate(sai->sai_ck, sai_ck_rate); + if (ret) { + dev_err(&pdev->dev, "Error %d setting sai_ck rate. %s", + ret, ret == -EBUSY ? + "Active stream rates may be in conflict\n" : "\n"); + goto err; + } + + return 0; + } + + /* Try a lower frequency */ + div++; + sai_ck_rate = sai_ck_max_rate / div; + } while (sai_ck_rate >= sai_ck_min_rate); + + /* No accurate rate found */ + dev_err(&pdev->dev, "Failed to find an accurate rate"); + +err: + stm32_sai_put_parent_rate(sai); + + return -EINVAL; +} + +static int stm32_sai_mclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); struct stm32_sai_sub_data *sai = mclk->sai_data; int div; - div = stm32_sai_get_clk_div(sai, *prate, rate); - if (div < 0) - return div; + div = stm32_sai_get_clk_div(sai, req->best_parent_rate, req->rate); + if (div <= 0) + return -EINVAL; - mclk->freq = *prate / div; + mclk->freq = req->best_parent_rate / div; - return mclk->freq; + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, @@ -439,7 +560,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_sai_mclk_enable, .disable = stm32_sai_mclk_disable, .recalc_rate = stm32_sai_mclk_recalc_rate, - .round_rate = stm32_sai_mclk_round_rate, + .determine_rate = stm32_sai_mclk_determine_rate, .set_rate = stm32_sai_mclk_set_rate, }; @@ -551,6 +672,14 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; + /* + * The mclk rate is determined at runtime from the audio stream rate. + * Skip calls to the set_sysclk callback that are not relevant during the + * initialization phase. + */ + if (!snd_soc_card_is_instantiated(cpu_dai->component->card)) + return 0; + if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, @@ -565,11 +694,15 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, clk_rate_exclusive_put(sai->sai_mclk); sai->mclk_rate = 0; } + + if (sai->put_sai_ck_rate) + sai->put_sai_ck_rate(sai); + return 0; } - /* If master clock is used, set parent clock now */ - ret = stm32_sai_set_parent_clock(sai, freq); + /* If master clock is used, configure SAI kernel clock now */ + ret = sai->set_sai_ck_rate(sai, freq); if (ret) return ret; @@ -993,7 +1126,7 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, int ret; if (!sai->sai_mclk) { - ret = stm32_sai_set_parent_clock(sai, rate); + ret = sai->set_sai_ck_rate(sai, rate); if (ret) return ret; } @@ -1154,6 +1287,14 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, clk_disable_unprepare(sai->sai_ck); + /* + * 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 (!sai->sai_mclk && sai->put_sai_ck_rate) + sai->put_sai_ck_rate(sai); + spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = NULL; spin_unlock_irqrestore(&sai->irq_lock, flags); @@ -1188,7 +1329,7 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) * constraints). */ sai->dma_params.maxburst = 4; - if (sai->pdata->conf.fifo_size < 8) + if (sai->pdata->conf.fifo_size < 8 || sai->pdata->conf.no_dma_burst) sai->dma_params.maxburst = 1; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -1222,6 +1363,19 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { + .probe = stm32_sai_dai_probe, + .set_sysclk = stm32_sai_set_sysclk, + .set_fmt = stm32_sai_set_dai_fmt, + .set_tdm_slot = stm32_sai_set_dai_tdm_slot, + .startup = stm32_sai_startup, + .hw_params = stm32_sai_hw_params, + .trigger = stm32_sai_trigger, + .shutdown = stm32_sai_shutdown, + .pcm_new = stm32_sai_pcm_new, +}; + +static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = { + .probe = stm32_sai_dai_probe, .set_sysclk = stm32_sai_set_sysclk, .set_fmt = stm32_sai_set_dai_fmt, .set_tdm_slot = stm32_sai_set_dai_tdm_slot, @@ -1233,11 +1387,11 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); int *ptr = (int *)(runtime->dma_area + hwoff + channel * (runtime->dma_bytes / runtime->channels)); @@ -1287,8 +1441,6 @@ static const struct snd_pcm_hardware stm32_sai_pcm_hw = { }; static struct snd_soc_dai_driver stm32_sai_playback_dai = { - .probe = stm32_sai_dai_probe, - .pcm_new = stm32_sai_pcm_new, .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, @@ -1306,7 +1458,6 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { }; static struct snd_soc_dai_driver stm32_sai_capture_dai = { - .probe = stm32_sai_dai_probe, .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, @@ -1320,7 +1471,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, - .ops = &stm32_sai_pcm_dai_ops, + .ops = &stm32_sai_pcm_dai_ops2, }; static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { @@ -1394,7 +1545,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, /* Get spdif iec60958 property */ sai->spdif = false; - if (of_get_property(np, "st,iec60958", NULL)) { + if (of_property_present(np, "st,iec60958")) { if (!STM_SAI_HAS_SPDIF(sai) || sai->dir == SNDRV_PCM_STREAM_CAPTURE) { dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); @@ -1435,7 +1586,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, dev_err(&pdev->dev, "External synchro not supported\n"); of_node_put(args.np); - return -EINVAL; + ret = -EINVAL; + goto err_put_sync_provider; } sai->sync = SAI_SYNC_EXTERNAL; @@ -1444,7 +1596,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { dev_err(&pdev->dev, "Wrong SAI index\n"); of_node_put(args.np); - return -EINVAL; + ret = -EINVAL; + goto err_put_sync_provider; } if (of_property_match_string(args.np, "compatible", @@ -1458,7 +1611,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, if (!sai->synco) { dev_err(&pdev->dev, "Unknown SAI sub-block\n"); of_node_put(args.np); - return -EINVAL; + ret = -EINVAL; + goto err_put_sync_provider; } } @@ -1468,38 +1622,45 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); - if (IS_ERR(sai->sai_ck)) - return dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck), - "Missing kernel clock sai_ck\n"); + if (IS_ERR(sai->sai_ck)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck), + "Missing kernel clock sai_ck\n"); + goto err_put_sync_provider; + } ret = clk_prepare(sai->pdata->pclk); if (ret < 0) - return ret; + goto err_put_sync_provider; if (STM_SAI_IS_F4(sai->pdata)) return 0; /* Register mclk provider if requested */ - if (of_find_property(np, "#clock-cells", NULL)) { + if (of_property_present(np, "#clock-cells")) { ret = stm32_sai_add_mclk_provider(sai); if (ret < 0) - return ret; + goto err_unprepare_pclk; } else { - sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK"); + sai->sai_mclk = devm_clk_get_optional(&pdev->dev, "MCLK"); if (IS_ERR(sai->sai_mclk)) { - if (PTR_ERR(sai->sai_mclk) != -ENOENT) - return PTR_ERR(sai->sai_mclk); - sai->sai_mclk = NULL; + ret = PTR_ERR(sai->sai_mclk); + goto err_unprepare_pclk; } } return 0; + +err_unprepare_pclk: + clk_unprepare(sai->pdata->pclk); +err_put_sync_provider: + of_node_put(sai->np_sync_provider); + + return ret; } static int stm32_sai_sub_probe(struct platform_device *pdev) { struct stm32_sai_sub_data *sai; - const struct of_device_id *of_id; const struct snd_dmaengine_pcm_config *conf = &stm32_sai_pcm_config; int ret; @@ -1507,10 +1668,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) if (!sai) return -ENOMEM; - of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev); - if (!of_id) - return -EINVAL; - sai->id = (uintptr_t)of_id->data; + sai->id = (uintptr_t)device_get_match_data(&pdev->dev); sai->pdev = pdev; mutex_init(&sai->ctrl_lock); @@ -1523,6 +1681,13 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) return -EINVAL; } + if (sai->pdata->conf.get_sai_ck_parent) { + sai->set_sai_ck_rate = stm32_sai_set_parent_clk; + } else { + sai->set_sai_ck_rate = stm32_sai_set_parent_rate; + sai->put_sai_ck_rate = stm32_sai_put_parent_rate; + } + ret = stm32_sai_sub_parse_of(pdev, sai); if (ret) return ret; @@ -1537,29 +1702,37 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) IRQF_SHARED, dev_name(&pdev->dev), sai); if (ret) { dev_err(&pdev->dev, "IRQ request returned %d\n", ret); - return ret; + goto err_unprepare_pclk; } if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) - return dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); + goto err_unprepare_pclk; + } ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); - if (ret) { - snd_dmaengine_pcm_unregister(&pdev->dev); - return ret; - } + if (ret) + goto err_deregister_pcm_dma; pm_runtime_enable(&pdev->dev); return 0; + +err_deregister_pcm_dma: + snd_dmaengine_pcm_unregister(&pdev->dev); +err_unprepare_pclk: + clk_unprepare(sai->pdata->pclk); + of_node_put(sai->np_sync_provider); + + return ret; } -static int stm32_sai_sub_remove(struct platform_device *pdev) +static void stm32_sai_sub_remove(struct platform_device *pdev) { struct stm32_sai_sub_data *sai = dev_get_drvdata(&pdev->dev); @@ -1567,11 +1740,9 @@ static int stm32_sai_sub_remove(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; + of_node_put(sai->np_sync_provider); } -#ifdef CONFIG_PM_SLEEP static int stm32_sai_sub_suspend(struct device *dev) { struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); @@ -1605,17 +1776,16 @@ static int stm32_sai_sub_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_sai_sub_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) + SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) }; static struct platform_driver stm32_sai_sub_driver = { .driver = { .name = "st,stm32-sai-sub", .of_match_table = stm32_sai_sub_ids, - .pm = &stm32_sai_sub_pm_ops, + .pm = pm_ptr(&stm32_sai_sub_pm_ops), }, .probe = stm32_sai_sub_probe, .remove = stm32_sai_sub_remove, diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index d399c906bb92..57b711c44278 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -856,6 +856,7 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { + .probe = stm32_spdifrx_dai_probe, .startup = stm32_spdifrx_startup, .hw_params = stm32_spdifrx_hw_params, .trigger = stm32_spdifrx_trigger, @@ -864,7 +865,6 @@ static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { { - .probe = stm32_spdifrx_dai_probe, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, @@ -908,17 +908,13 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, struct stm32_spdifrx_data *spdifrx) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id; struct resource *res; if (!np) return -ENODEV; - of_id = of_match_device(stm32_spdifrx_ids, &pdev->dev); - if (of_id) - spdifrx->regmap_conf = - (const struct regmap_config *)of_id->data; - else + spdifrx->regmap_conf = device_get_match_data(&pdev->dev); + if (!spdifrx->regmap_conf) return -EINVAL; spdifrx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -939,11 +935,11 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, return 0; } -static int stm32_spdifrx_remove(struct platform_device *pdev) +static void stm32_spdifrx_remove(struct platform_device *pdev) { struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev); - if (spdifrx->ctrl_chan) + if (!IS_ERR(spdifrx->ctrl_chan)) dma_release_channel(spdifrx->ctrl_chan); if (spdifrx->dmab) @@ -952,8 +948,6 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static int stm32_spdifrx_probe(struct platform_device *pdev) @@ -1046,7 +1040,6 @@ error: MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids); -#ifdef CONFIG_PM_SLEEP static int stm32_spdifrx_suspend(struct device *dev) { struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev); @@ -1065,17 +1058,16 @@ static int stm32_spdifrx_resume(struct device *dev) return regcache_sync(spdifrx->regmap); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_spdifrx_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume) + SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume) }; static struct platform_driver stm32_spdifrx_driver = { .driver = { .name = "st,stm32-spdifrx", .of_match_table = stm32_spdifrx_ids, - .pm = &stm32_spdifrx_pm_ops, + .pm = pm_ptr(&stm32_spdifrx_pm_ops), }, .probe = stm32_spdifrx_probe, .remove = stm32_spdifrx_remove, |
