From f0e754855e06c3b8eb04ee09e6b1ea8a1bd9af7c Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Mon, 26 Nov 2018 14:39:24 +0100 Subject: [PATCH 19/52] ARM-stm32mp1-r0-rc2-SOUND --- include/linux/mfd/wm8994/pdata.h | 6 + sound/soc/codecs/Kconfig | 4 +- sound/soc/codecs/cs42l51-i2c.c | 6 + sound/soc/codecs/cs42l51.c | 155 +++++++++++++++--- sound/soc/codecs/cs42l51.h | 1 + sound/soc/codecs/wm8994.c | 79 ++++++++- sound/soc/stm/Kconfig | 1 + sound/soc/stm/stm32_adfsdm.c | 17 +- sound/soc/stm/stm32_i2s.c | 172 +++++++++++++------- sound/soc/stm/stm32_sai.c | 67 +++++++- sound/soc/stm/stm32_sai.h | 57 +++++-- sound/soc/stm/stm32_sai_sub.c | 335 +++++++++++++++++++++++++++++++++------ sound/soc/stm/stm32_spdifrx.c | 81 +++++++++- 13 files changed, 819 insertions(+), 162 deletions(-) diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index b19c370..cdaf3f6 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -239,6 +239,12 @@ struct wm8994_pdata { * GPIO for the IRQ pin if host only supports edge triggering */ int irq_gpio; + + /* MCLK1 clock provider */ + struct clk *mclk1; + + /* MCLK2 clock provider */ + struct clk *mclk2; }; #endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efb095d..58161d1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -575,7 +575,7 @@ config SND_SOC_DA9055 tristate config SND_SOC_DMIC - tristate + tristate "Generic DMIC codec" config SND_SOC_HDMI_CODEC tristate @@ -1227,7 +1227,7 @@ config SND_SOC_WM8993 tristate config SND_SOC_WM8994 - tristate + tristate "Wolfson Microelectronics WM8994 codec" config SND_SOC_WM8995 tristate diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 4b5731a..8333dbf 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -35,12 +35,18 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config)); } +static int cs42l51_i2c_remove(struct i2c_client *i2c) +{ + return cs42l51_remove(&i2c->dev); +} + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", .of_match_table = cs42l51_of_match, }, .probe = cs42l51_i2c_probe, + .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 5080d7a3..fceee59 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -21,6 +21,7 @@ * - master mode *NOT* supported */ +#include #include #include #include @@ -29,7 +30,9 @@ #include #include #include +#include #include +#include #include "cs42l51.h" @@ -39,10 +42,21 @@ enum master_slave_mode { MODE_MASTER, }; +#define CS42L51_NUM_SUPPLIES 4 +static const char *cs42l51_supply_names[CS42L51_NUM_SUPPLIES] = { + "VL", + "VD", + "VA", + "VAHP", +}; + struct cs42l51_private { unsigned int mclk; + struct clk *mclk_handle; unsigned int audio_mode; /* The mode (I2S or left-justified) */ enum master_slave_mode func; + struct regulator_bulk_data supplies[CS42L51_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; }; #define CS42L51_FORMATS ( \ @@ -165,6 +179,7 @@ static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMD: snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, CS42L51_POWER_CTL1_PDN, 0); + msleep(20); break; } @@ -193,7 +208,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { - SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1), + SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, @@ -237,6 +253,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { &cs42l51_adcr_mux_controls), }; +static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { + SND_SOC_DAPM_CLOCK_SUPPLY("MCLK") +}; + static const struct snd_soc_dapm_route cs42l51_routes[] = { {"HPL", NULL, "Left DAC"}, {"HPR", NULL, "Right DAC"}, @@ -323,6 +343,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = { { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, }; +/* + * Master mode mclk/fs ratios. + * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges + * The table below provides support of following ratios: + * 128: SSM (%128) with div2 disabled + * 256: SSM (%128) with div2 enabled + * In both cases, if sampling rate is above 50kHz, SSM is overridden + * with DSM (%128) configuration + */ +static struct cs42l51_ratios master_ratios[] = { + { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 }, +}; + static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -345,11 +378,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, unsigned int ratio; struct cs42l51_ratios *ratios = NULL; int nr_ratios = 0; - int intf_ctl, power_ctl, fmt; + int intf_ctl, power_ctl, fmt, mode; switch (cs42l51->func) { case MODE_MASTER: - return -EINVAL; + ratios = master_ratios; + nr_ratios = ARRAY_SIZE(master_ratios); + break; case MODE_SLAVE: ratios = slave_ratios; nr_ratios = ARRAY_SIZE(slave_ratios); @@ -385,7 +420,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, switch (cs42l51->func) { case MODE_MASTER: intf_ctl |= CS42L51_INTF_CTL_MASTER; - power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); + mode = ratios[i].speed_mode; + /* Force DSM mode if sampling rate is above 50kHz */ + if (rate > 50000) + mode = CS42L51_DSM_MODE; + power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode); + /* + * Auto detect mode is not applicable for master mode and has to + * be disabled. Otherwise SPEED[1:0] bits will be ignored. + */ + power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO; break; case MODE_SLAVE: power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); @@ -465,28 +509,42 @@ static const struct snd_soc_dai_ops cs42l51_dai_ops = { .digital_mute = cs42l51_dai_mute, }; -static struct snd_soc_dai_driver cs42l51_dai = { - .name = "cs42l51-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = CS42L51_FORMATS, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = CS42L51_FORMATS, +static struct snd_soc_dai_driver cs42l51_dai[] = { + { + .name = "cs42l51-hifi0", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L51_FORMATS, + }, + .ops = &cs42l51_dai_ops, }, - .ops = &cs42l51_dai_ops, + { + .name = "cs42l51-hifi1", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L51_FORMATS, + }, + .ops = &cs42l51_dai_ops, + } }; static int cs42l51_component_probe(struct snd_soc_component *component) { int ret, reg; + struct snd_soc_dapm_context *dapm; + struct cs42l51_private *cs42l51; + + cs42l51 = snd_soc_component_get_drvdata(component); + dapm = snd_soc_component_get_dapm(component); + + if (cs42l51->mclk_handle) + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); /* * DAC configuration @@ -528,7 +586,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) { struct cs42l51_private *cs42l51; unsigned int val; - int ret; + int ret, i; if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -540,6 +598,41 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); + cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) { + if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) + return PTR_ERR(cs42l51->mclk_handle); + cs42l51->mclk_handle = NULL; + } + + for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) + cs42l51->supplies[i].supply = cs42l51_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs42l51->reset_gpio)) + return PTR_ERR(cs42l51->reset_gpio); + + if (cs42l51->reset_gpio) { + dev_dbg(dev, "Release reset gpio\n"); + gpiod_set_value_cansleep(cs42l51->reset_gpio, 0); + mdelay(2); + } + /* Verify that we have a CS42L51 */ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { @@ -557,12 +650,30 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) val & CS42L51_CHIP_REV_MASK); ret = devm_snd_soc_register_component(dev, - &soc_component_device_cs42l51, &cs42l51_dai, 1); + &soc_component_device_cs42l51, cs42l51_dai, 2); + if (ret < 0) + goto error; + + return 0; + error: + regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); return ret; } EXPORT_SYMBOL_GPL(cs42l51_probe); +int cs42l51_remove(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); + + return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); +} +EXPORT_SYMBOL_GPL(cs42l51_remove); + const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", }, { } diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h index 0ca8054..aef0ede 100644 --- a/sound/soc/codecs/cs42l51.h +++ b/sound/soc/codecs/cs42l51.h @@ -22,6 +22,7 @@ struct device; extern const struct regmap_config cs42l51_regmap; int cs42l51_probe(struct device *dev, struct regmap *regmap); +int cs42l51_remove(struct device *dev); extern const struct of_device_id cs42l51_of_match[]; #define CS42L51_CHIP_ID 0x1B diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 14f1b0c..eb222da 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -839,6 +840,42 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, return 0; } +static int mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(comp); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + struct clk *mclk = pdata->mclk1; + int ret, mclk_id = 0; + + if (!strncmp(w->name, "MCLK2", 5)) { + mclk_id = 1; + mclk = pdata->mclk2; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(comp->dev, "Enable master clock %s\n", + mclk_id ? "MCLK2" : "MCLK1"); + + ret = clk_prepare_enable(mclk); + if (ret < 0) { + dev_err(comp->dev, "Failed to enable clock: %d\n", ret); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(comp->dev, "Disable master clock %s\n", + mclk_id ? "MCLK1" : "MCLK2"); + clk_disable_unprepare(mclk); + break; + } + + return 0; +} + static void vmid_reference(struct snd_soc_component *component) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); @@ -1775,6 +1812,16 @@ static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = { SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), }; +static const struct snd_soc_dapm_widget wm8994_mclk1_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("MCLK1", SND_SOC_NOPM, 0, 0, mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_widget wm8994_mclk2_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("MCLK2", SND_SOC_NOPM, 0, 0, mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0), SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), @@ -1999,10 +2046,10 @@ static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { }; static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { - { "AIF1DACDAT", NULL, "AIF2DACDAT" }, - { "AIF2DACDAT", NULL, "AIF1DACDAT" }, - { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, - { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, +// { "AIF1DACDAT", NULL, "AIF2DACDAT" }, +// { "AIF2DACDAT", NULL, "AIF1DACDAT" }, +// { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, +// { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, { "MICBIAS1", NULL, "CLK_SYS" }, { "MICBIAS1", NULL, "MICBIAS Supply" }, { "MICBIAS2", NULL, "CLK_SYS" }, @@ -2376,11 +2423,26 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, { struct snd_soc_component *component = dai->component; struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; int i; + /* + * Simple card provides unconditionnaly clock_id = 0. + * Workaround to select master clock for aif1/2 + */ switch (dai->id) { case 1: + if (pdata->mclk1) + clk_id = WM8994_SYSCLK_MCLK1; + else if (pdata->mclk2) + clk_id = WM8994_SYSCLK_MCLK2; + break; case 2: + if (pdata->mclk2) + clk_id = WM8994_SYSCLK_MCLK2; + else if (pdata->mclk1) + clk_id = WM8994_SYSCLK_MCLK1; break; default: @@ -3990,6 +4052,7 @@ static int wm8994_component_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct wm8994 *control = dev_get_drvdata(component->dev->parent); + struct wm8994_pdata *pdata = &control->pdata; struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); unsigned int reg; int ret, i; @@ -4271,6 +4334,14 @@ static int wm8994_component_probe(struct snd_soc_component *component) case WM8994: snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, ARRAY_SIZE(wm8994_specific_dapm_widgets)); + if (pdata->mclk1) + snd_soc_dapm_new_controls(dapm, wm8994_mclk1_dapm_widgets, + ARRAY_SIZE(wm8994_mclk1_dapm_widgets)); + + if (pdata->mclk2) + snd_soc_dapm_new_controls(dapm, wm8994_mclk2_dapm_widgets, + ARRAY_SIZE(wm8994_mclk2_dapm_widgets)); + if (control->revision < 4) { snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, ARRAY_SIZE(wm8994_lateclk_revd_widgets)); diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 9b26813..c66ffa7 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -3,6 +3,7 @@ menu "STMicroelectronics STM32 SOC audio support" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" depends on (ARCH_STM32 && OF) || COMPILE_TEST + depends on COMMON_CLK depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 706ff00..71d341b 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -37,6 +38,8 @@ struct stm32_adfsdm_priv { /* PCM buffer */ unsigned char *pcm_buff; unsigned int pos; + + struct mutex lock; /* protect against race condition on iio state */ }; static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = { @@ -62,10 +65,12 @@ static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream, { struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + mutex_lock(&priv->lock); if (priv->iio_active) { iio_channel_stop_all_cb(priv->iio_cb); priv->iio_active = false; } + mutex_unlock(&priv->lock); } static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, @@ -74,13 +79,19 @@ static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); int ret; + mutex_lock(&priv->lock); + if (priv->iio_active) { + iio_channel_stop_all_cb(priv->iio_cb); + priv->iio_active = false; + } + ret = iio_write_channel_attribute(priv->iio_ch, substream->runtime->rate, 0, IIO_CHAN_INFO_SAMP_FREQ); if (ret < 0) { dev_err(dai->dev, "%s: Failed to set %d sampling rate\n", __func__, substream->runtime->rate); - return ret; + goto out; } if (!priv->iio_active) { @@ -92,6 +103,9 @@ static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, __func__, ret); } +out: + mutex_unlock(&priv->lock); + return ret; } @@ -298,6 +312,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->dai_drv = stm32_adfsdm_dai; + mutex_init(&priv->lock); dev_set_drvdata(&pdev->dev, priv); diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6d0bf78..9dcf946 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -16,6 +16,7 @@ * details. */ +#include #include #include #include @@ -37,6 +38,10 @@ #define STM32_I2S_TXDR_REG 0X20 #define STM32_I2S_RXDR_REG 0x30 #define STM32_I2S_CGFR_REG 0X50 +#define STM32_I2S_HWCFGR_REG 0x3F0 +#define STM32_I2S_VERR_REG 0x3F4 +#define STM32_I2S_IPIDR_REG 0x3F8 +#define STM32_I2S_SIDR_REG 0x3FC /* Bit definition for SPI2S_CR1 register */ #define I2S_CR1_SPE BIT(0) @@ -143,6 +148,23 @@ #define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) #define I2S_CGFR_MCKOE BIT(25) +/* Registers below apply to I2S version 1.1 and more */ + +/* Bit definition for SPI_HWCFGR register */ +#define I2S_HWCFGR_I2S_SUPPORT_MASK GENMASK(15, 12) + +/* Bit definition for SPI_VERR register */ +#define I2S_VERR_MIN_MASK GENMASK(3, 0) +#define I2S_VERR_MAJ_MASK GENMASK(7, 4) + +/* Bit definition for SPI_IPIDR register */ +#define I2S_IPIDR_ID_MASK GENMASK(31, 0) + +/* Bit definition for SPI_SIDR register */ +#define I2S_SIDR_ID_MASK GENMASK(31, 0) + +#define I2S_IPIDR_NUMBER 0x00130022 + enum i2s_master_mode { I2S_MS_NOT_SET, I2S_MS_MASTER, @@ -179,7 +201,6 @@ enum i2s_datlen { I2S_I2SMOD_DATLEN_32, }; -#define STM32_I2S_DAI_NAME_SIZE 20 #define STM32_I2S_FIFO_SIZE 16 #define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) @@ -200,7 +221,6 @@ enum i2s_datlen { * @base: mmio register base virtual address * @phys_addr: I2S registers physical base address * @lock_fd: lock to manage race conditions in full duplex mode - * @dais_name: DAI name * @mclk_rate: master clock frequency (Hz) * @fmt: DAI protocol * @refcount: keep count of opened streams on I2S @@ -221,7 +241,6 @@ struct stm32_i2s_data { void __iomem *base; dma_addr_t phys_addr; spinlock_t lock_fd; /* Manage race conditions for full duplex */ - char dais_name[STM32_I2S_DAI_NAME_SIZE]; unsigned int mclk_rate; unsigned int fmt; int refcount; @@ -246,8 +265,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) return IRQ_NONE; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, flags); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, flags); if (flags & I2S_SR_OVR) { dev_dbg(&pdev->dev, "Overrun\n"); @@ -276,10 +295,13 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) case STM32_I2S_CFG2_REG: case STM32_I2S_IER_REG: case STM32_I2S_SR_REG: - case STM32_I2S_IFCR_REG: case STM32_I2S_TXDR_REG: case STM32_I2S_RXDR_REG: case STM32_I2S_CGFR_REG: + case STM32_I2S_HWCFGR_REG: + case STM32_I2S_VERR_REG: + case STM32_I2S_IPIDR_REG: + case STM32_I2S_SIDR_REG: return true; default: return false; @@ -488,7 +510,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); int format = params_width(params); - u32 cfgr, cfgr_mask, cfg1, cfg1_mask; + u32 cfgr, cfgr_mask, cfg1; unsigned int fthlv; int ret; @@ -501,7 +523,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, switch (format) { case 16: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); - cfgr_mask = I2S_CGFR_DATLEN_MASK; + cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; break; case 32: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | @@ -529,30 +551,29 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, if (ret < 0) return ret; - cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; - cfg1_mask = cfg1; - fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; - cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); - cfg1_mask |= I2S_CFG1_FTHVL_MASK; + cfg1 = I2S_CFG1_FTHVL_SET(fthlv - 1); return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, - cfg1_mask, cfg1); + I2S_CFG1_FTHVL_MASK, cfg1); } static int stm32_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + int ret; i2s->substream = substream; - spin_lock(&i2s->lock_fd); - i2s->refcount++; - spin_unlock(&i2s->lock_fd); + ret = clk_prepare_enable(i2s->i2sclk); + if (ret < 0) { + dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); + return ret; + } - return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); } static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, @@ -587,7 +608,12 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* Enable i2s */ - dev_dbg(cpu_dai->dev, "start I2S\n"); + dev_dbg(cpu_dai->dev, "start I2S %s\n", + playback_flg ? "playback" : "capture"); + + cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, + cfg1_mask, cfg1_mask); ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, I2S_CR1_SPE, I2S_CR1_SPE); @@ -596,28 +622,29 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } - ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, - I2S_CR1_CSTART, I2S_CR1_CSTART); + ret = regmap_write_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_CSTART, I2S_CR1_CSTART); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); return ret; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); + spin_lock(&i2s->lock_fd); + i2s->refcount++; if (playback_flg) { ier = I2S_IER_UDRIE; } else { ier = I2S_IER_OVRIE; - spin_lock(&i2s->lock_fd); if (i2s->refcount == 1) /* dummy write to trigger capture */ regmap_write(i2s->regmap, STM32_I2S_TXDR_REG, 0); - spin_unlock(&i2s->lock_fd); } + spin_unlock(&i2s->lock_fd); if (STM32_I2S_IS_SLAVE(i2s)) ier |= I2S_IER_TIFREIE; @@ -627,6 +654,9 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev_dbg(cpu_dai->dev, "stop I2S %s\n", + playback_flg ? "playback" : "capture"); + if (playback_flg) regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, I2S_IER_UDRIE, @@ -642,16 +672,15 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, spin_unlock(&i2s->lock_fd); break; } - spin_unlock(&i2s->lock_fd); - - dev_dbg(cpu_dai->dev, "stop I2S\n"); ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, I2S_CR1_SPE, 0); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); + spin_unlock(&i2s->lock_fd); return ret; } + spin_unlock(&i2s->lock_fd); cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, @@ -673,6 +702,8 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); + + clk_disable_unprepare(i2s->i2sclk); } static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) @@ -698,11 +729,12 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = STM32_I2S_CGFR_REG, + .max_register = STM32_I2S_SIDR_REG, .readable_reg = stm32_i2s_readable_reg, .volatile_reg = stm32_i2s_volatile_reg, .writeable_reg = stm32_i2s_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { @@ -717,7 +749,8 @@ static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { static const struct snd_pcm_hardware stm32_i2s_pcm_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, .buffer_bytes_max = 8 * PAGE_SIZE, - .period_bytes_max = 2048, + .period_bytes_min = 1024, /* 5ms at 48kHz */ + .period_bytes_max = PAGE_SIZE, .periods_min = 2, .periods_max = 8, }; @@ -753,12 +786,8 @@ static int stm32_i2s_dais_init(struct platform_device *pdev, if (!dai_ptr) return -ENOMEM; - snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE, - "%s", dev_name(&pdev->dev)); - dai_ptr->probe = stm32_i2s_dai_probe; dai_ptr->ops = &stm32_i2s_pcm_dai_ops; - dai_ptr->name = i2s->dais_name; dai_ptr->id = 1; stm32_i2s_dai_init(&dai_ptr->playback, "playback"); stm32_i2s_dai_init(&dai_ptr->capture, "capture"); @@ -853,6 +882,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, static int stm32_i2s_probe(struct platform_device *pdev) { struct stm32_i2s_data *i2s; + u32 val; int ret; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); @@ -872,47 +902,50 @@ static int stm32_i2s_probe(struct platform_device *pdev) if (ret) return ret; - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, - i2s->regmap_conf); + i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", + i2s->base, i2s->regmap_conf); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); return PTR_ERR(i2s->regmap); } - ret = clk_prepare_enable(i2s->pclk); - if (ret) { - dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(i2s->i2sclk); - if (ret) { - dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); - goto err_pclk_disable; - } - ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); if (ret) - goto err_clocks_disable; + return ret; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) - goto err_clocks_disable; + return ret; /* Set SPI/I2S in i2s mode */ ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); if (ret) - goto err_clocks_disable; + return ret; - return ret; + ret = regmap_read(i2s->regmap, STM32_I2S_IPIDR_REG, &val); + if (ret) + return ret; -err_clocks_disable: - clk_disable_unprepare(i2s->i2sclk); -err_pclk_disable: - clk_disable_unprepare(i2s->pclk); + if (val == I2S_IPIDR_NUMBER) { + ret = regmap_read(i2s->regmap, STM32_I2S_HWCFGR_REG, &val); + if (ret) + return ret; + + if (!FIELD_GET(I2S_HWCFGR_I2S_SUPPORT_MASK, val)) { + dev_err(&pdev->dev, + "Device does not support i2s mode\n"); + return ret; + } + + ret = regmap_read(i2s->regmap, STM32_I2S_VERR_REG, &val); + + dev_dbg(&pdev->dev, "I2S version: %lu.%lu registered\n", + FIELD_GET(I2S_VERR_MAJ_MASK, val), + FIELD_GET(I2S_VERR_MIN_MASK, val)); + } return ret; } @@ -929,10 +962,35 @@ static int stm32_i2s_remove(struct platform_device *pdev) 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); + + regcache_cache_only(i2s->regmap, true); + regcache_mark_dirty(i2s->regmap); + + return 0; +} + +static int stm32_i2s_resume(struct device *dev) +{ + struct stm32_i2s_data *i2s = dev_get_drvdata(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) +}; + static struct platform_driver stm32_i2s_driver = { .driver = { .name = "st,stm32-i2s", .of_match_table = stm32_i2s_ids, + .pm = &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 f226542..1426d90 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -29,13 +30,20 @@ #include "stm32_sai.h" static const struct stm32_sai_conf stm32_sai_conf_f4 = { - .version = SAI_STM32F4, - .has_spdif = false, + .version = STM_SAI_STM32F4, + .fifo_size = 8, + .has_spdif_pdm = false, }; +/* + * Default settings for stm32 H7 socs and next. + * These default settings will be overridden if the soc provides + * support of hardware configuration registers. + */ static const struct stm32_sai_conf stm32_sai_conf_h7 = { - .version = SAI_STM32H7, - .has_spdif = true, + .version = STM_SAI_STM32H7, + .fifo_size = 8, + .has_spdif_pdm = true, }; static const struct of_device_id stm32_sai_ids[] = { @@ -130,6 +138,8 @@ static int stm32_sai_probe(struct platform_device *pdev) struct reset_control *rst; struct resource *res; const struct of_device_id *of_id; + u32 val; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -142,7 +152,8 @@ static int stm32_sai_probe(struct platform_device *pdev) of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) - sai->conf = (struct stm32_sai_conf *)of_id->data; + memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data, + sizeof(struct stm32_sai_conf)); else return -EINVAL; @@ -181,6 +192,30 @@ static int stm32_sai_probe(struct platform_device *pdev) reset_control_deassert(rst); } + /* Enable peripheral clock to allow register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + val = FIELD_GET(SAI_IDR_ID_MASK, + readl_relaxed(sai->base + STM_SAI_IDR)); + if (val == SAI_IPIDR_NUMBER) { + val = readl_relaxed(sai->base + STM_SAI_HWCFGR); + sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val); + sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM, + val); + + val = readl_relaxed(sai->base + STM_SAI_VERR); + sai->conf.version = val; + + dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n", + FIELD_GET(SAI_VERR_MAJ_MASK, val), + FIELD_GET(SAI_VERR_MIN_MASK, val)); + } + clk_disable_unprepare(sai->pclk); + sai->pdev = pdev; sai->set_sync = &stm32_sai_set_sync; platform_set_drvdata(pdev, sai); @@ -188,12 +223,34 @@ static int stm32_sai_probe(struct platform_device *pdev) 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. + * These pins are managed in suspend/resume callbacks. + */ +static int stm32_sai_suspend(struct device *dev) +{ + return pinctrl_pm_select_sleep_state(dev); +} + +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) +}; + MODULE_DEVICE_TABLE(of, stm32_sai_ids); static struct platform_driver stm32_sai_driver = { .driver = { .name = "st,stm32-sai", .of_match_table = stm32_sai_ids, + .pm = &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 f254221..042f94f 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -37,6 +37,12 @@ #define STM_SAI_PDMCR_REGX 0x40 #define STM_SAI_PDMLY_REGX 0x44 +/* Hardware configuration registers */ +#define STM_SAI_HWCFGR 0x3F0 +#define STM_SAI_VERR 0x3F4 +#define STM_SAI_IDR 0x3F8 +#define STM_SAI_SIDR 0x3FC + /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 #define SAI_GCR_SYNCIN_WDTH 2 @@ -82,7 +88,7 @@ #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) #define SAI_XCR1_MCKDIV_SHIFT 20 -#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6) +#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == STM_SAI_STM32F4) ? 4 : 6) #define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\ SAI_XCR1_MCKDIV_SHIFT) #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) @@ -91,6 +97,9 @@ #define SAI_XCR1_OSR_SHIFT 26 #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) +#define SAI_XCR1_MCKEN_SHIFT 27 +#define SAI_XCR1_MCKEN BIT(SAI_XCR1_MCKEN_SHIFT) + /******************* Bit definition for SAI_XCR2 register *******************/ #define SAI_XCR2_FTH_SHIFT 0 #define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT) @@ -231,8 +240,33 @@ #define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT) #define SAI_PDMDLY_4R_WIDTH 3 -#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) -#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) +/* Registers below apply to SAI version 2.1 and more */ + +/* Bit definition for SAI_HWCFGR register */ +#define SAI_HWCFGR_FIFO_SIZE GENMASK(7, 0) +#define SAI_HWCFGR_SPDIF_PDM GENMASK(11, 8) +#define SAI_HWCFGR_REGOUT GENMASK(19, 12) + +/* Bit definition for SAI_VERR register */ +#define SAI_VERR_MIN_MASK GENMASK(3, 0) +#define SAI_VERR_MAJ_MASK GENMASK(7, 4) + +/* Bit definition for SAI_IDR register */ +#define SAI_IDR_ID_MASK GENMASK(31, 0) + +/* Bit definition for SAI_SIDR register */ +#define SAI_SIDR_ID_MASK GENMASK(31, 0) + +#define SAI_IPIDR_NUMBER 0x00130031 + +/* SAI version numbers are 1.x for F4. Major version number set to 1 for F4 */ +#define STM_SAI_STM32F4 BIT(4) +/* Dummy version number for H7 socs and next */ +#define STM_SAI_STM32H7 0x0 + +#define STM_SAI_IS_F4(ip) ((ip)->conf.version == STM_SAI_STM32F4) +#define STM_SAI_HAS_SPDIF_PDM(ip)\ + ((ip)->pdata->conf.has_spdif_pdm) enum stm32_sai_syncout { STM_SAI_SYNC_OUT_NONE, @@ -240,19 +274,16 @@ enum stm32_sai_syncout { STM_SAI_SYNC_OUT_B, }; -enum stm32_sai_version { - SAI_STM32F4, - SAI_STM32H7 -}; - /** * struct stm32_sai_conf - SAI configuration * @version: SAI version - * @has_spdif: SAI S/PDIF support flag + * @fifo_size: SAI fifo size as words number + * @has_spdif_pdm: SAI S/PDIF and PDM features support flag */ struct stm32_sai_conf { - int version; - bool has_spdif; + u32 version; + u32 fifo_size; + bool has_spdif_pdm; }; /** @@ -262,7 +293,7 @@ struct stm32_sai_conf { * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz - * @version: SOC version + * @conf: SAI hardware capabitilites * @irq: SAI interrupt line * @set_sync: pointer to synchro mode configuration callback */ @@ -272,7 +303,7 @@ struct stm32_sai_data { struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; - struct stm32_sai_conf *conf; + struct stm32_sai_conf conf; int irq; int (*set_sync)(struct stm32_sai_data *sai, struct device_node *np_provider, int synco, int synci); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 06fba96..1f23ca4 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -44,7 +45,6 @@ #define SAI_DATASIZE_24 0x6 #define SAI_DATASIZE_32 0x7 -#define STM_SAI_FIFO_SIZE 8 #define STM_SAI_DAI_NAME_SIZE 15 #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -62,12 +62,15 @@ #define SAI_SYNC_EXTERNAL 0x2 #define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) -#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf->has_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 SAI_IEC60958_BLOCK_FRAMES 192 #define SAI_IEC60958_STATUS_BYTES 24 +#define SAI_MCLK_NAME_LEN 32 + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -80,6 +83,7 @@ * @pdata: SAI block parent data pointer * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator + * @sai_mclk: master clock from SAI mclk provider * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init * @id: SAI sub block id corresponding to sub-block A or B @@ -110,6 +114,7 @@ struct stm32_sai_sub_data { struct stm32_sai_data *pdata; struct device_node *np_sync_provider; struct clk *sai_ck; + struct clk *sai_mclk; dma_addr_t phys_addr; unsigned int mclk_rate; unsigned int id; @@ -151,6 +156,10 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) case STM_SAI_DR_REGX: case STM_SAI_PDMCR_REGX: case STM_SAI_PDMLY_REGX: + case STM_SAI_HWCFGR: + case STM_SAI_VERR: + case STM_SAI_IDR: + case STM_SAI_SIDR: return true; default: return false; @@ -161,6 +170,7 @@ static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case STM_SAI_DR_REGX: + case STM_SAI_SR_REGX: return true; default: return false; @@ -175,7 +185,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) case STM_SAI_FRCR_REGX: case STM_SAI_SLOTR_REGX: case STM_SAI_IMR_REGX: - case STM_SAI_SR_REGX: case STM_SAI_CLRFR_REGX: case STM_SAI_DR_REGX: case STM_SAI_PDMCR_REGX: @@ -195,6 +204,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .volatile_reg = stm32_sai_sub_volatile_reg, .writeable_reg = stm32_sai_sub_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { @@ -206,6 +216,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { .volatile_reg = stm32_sai_sub_volatile_reg, .writeable_reg = stm32_sai_sub_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, @@ -251,6 +262,175 @@ static const struct snd_kcontrol_new iec958_ctls = { .put = snd_pcm_iec958_put, }; +struct stm32_sai_mclk_data { + struct clk_hw hw; + unsigned long freq; + struct stm32_sai_sub_data *sai_data; +}; + +#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw) +#define STM32_SAI_MAX_CLKS 1 + +static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, + unsigned long input_rate, + unsigned long output_rate) +{ + int version = sai->pdata->conf.version; + int div; + + div = DIV_ROUND_CLOSEST(input_rate, output_rate); + if (div > SAI_XCR1_MCKDIV_MAX(version)) { + dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); + return -EINVAL; + } + dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div); + + if (input_rate % div) + dev_dbg(&sai->pdev->dev, + "Rate not accurate. requested (%ld), actual (%ld)\n", + output_rate, input_rate / div); + + return div; +} + +static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, + unsigned int div) +{ + int version = sai->pdata->conf.version; + int ret, cr1, mask; + + if (div > SAI_XCR1_MCKDIV_MAX(version)) { + dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); + return -EINVAL; + } + + mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); + cr1 = SAI_XCR1_MCKDIV_SET(div); + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); + if (ret < 0) + dev_err(&sai->pdev->dev, "Failed to update CR1 register\n"); + + return ret; +} + +static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + 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; + + mclk->freq = *prate / div; + + return mclk->freq; +} + +static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + + return mclk->freq; +} + +static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + struct stm32_sai_sub_data *sai = mclk->sai_data; + int div, ret; + + div = stm32_sai_get_clk_div(sai, parent_rate, rate); + if (div < 0) + return div; + + ret = stm32_sai_set_clk_div(sai, div); + if (ret) + return ret; + + mclk->freq = rate; + + return 0; +} + +static int stm32_sai_mclk_enable(struct clk_hw *hw) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + struct stm32_sai_sub_data *sai = mclk->sai_data; + + dev_dbg(&sai->pdev->dev, "Enable master clock\n"); + + return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, + SAI_XCR1_MCKEN, SAI_XCR1_MCKEN); +} + +static void stm32_sai_mclk_disable(struct clk_hw *hw) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + struct stm32_sai_sub_data *sai = mclk->sai_data; + + dev_dbg(&sai->pdev->dev, "Disable master clock\n"); + + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0); +} + +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, + .set_rate = stm32_sai_mclk_set_rate, +}; + +static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai) +{ + struct clk_hw *hw; + struct stm32_sai_mclk_data *mclk; + struct device *dev = &sai->pdev->dev; + const char *pname = __clk_get_name(sai->sai_ck); + char *mclk_name, *p, *s = (char *)pname; + int ret, i = 0; + + mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL); + if (!mclk) + return -ENOMEM; + + mclk_name = devm_kcalloc(dev, sizeof(char), + SAI_MCLK_NAME_LEN, GFP_KERNEL); + if (!mclk_name) + return -ENOMEM; + + /* + * Forge mclk clock name from parent clock name and suffix. + * String after "_" char is stripped in parent name. + */ + p = mclk_name; + while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 7))) { + *p++ = *s++; + i++; + } + STM_SAI_IS_SUB_A(sai) ? strcat(p, "a_mclk") : strcat(p, "b_mclk"); + + mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0); + mclk->sai_data = sai; + hw = &mclk->hw; + + dev_dbg(dev, "Register master clock %s\n", mclk_name); + ret = devm_clk_hw_register(&sai->pdev->dev, hw); + if (ret) { + dev_err(dev, "mclk register returned %d\n", ret); + return ret; + } + sai->sai_mclk = hw->clk; + + /* register mclk provider */ + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; @@ -265,8 +445,8 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) if (!flags) return IRQ_NONE; - regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, - SAI_XCLRFR_MASK); + regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, + SAI_XCLRFR_MASK); if (!sai->substream) { dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); @@ -312,15 +492,25 @@ 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; - if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { + if (dir == SND_SOC_CLOCK_OUT) { ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, (unsigned int)~SAI_XCR1_NODIV); if (ret < 0) return ret; - sai->mclk_rate = freq; dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); + sai->mclk_rate = freq; + + if (sai->sai_mclk) { + ret = clk_set_rate_exclusive(sai->sai_mclk, + sai->mclk_rate); + if (ret) { + dev_err(cpu_dai->dev, + "Could not set mclk rate\n"); + return ret; + } + } } return 0; @@ -505,9 +695,8 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, } /* Enable ITs */ - - regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, - SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); + regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, + SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); imr = SAI_XIMR_OVRUDRIE; if (STM_SAI_IS_CAPTURE(sai)) { @@ -539,10 +728,10 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, * SAI fifo threshold is set to half fifo, to keep enough space * for DMA incoming bursts. */ - regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, - SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, - SAI_XCR2_FFLUSH | - SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); + regmap_write_bits(sai->regmap, STM_SAI_CR2_REGX, + SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, + SAI_XCR2_FFLUSH | + SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); /* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/ if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { @@ -715,15 +904,9 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int cr1, mask, div = 0; - int sai_clk_rate, mclk_ratio, den, ret; - int version = sai->pdata->conf->version; + int sai_clk_rate, mclk_ratio, den; unsigned int rate = params_rate(params); - if (!sai->mclk_rate) { - dev_err(cpu_dai->dev, "Mclk rate is null\n"); - return -EINVAL; - } - if (!(rate % 11025)) clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); else @@ -731,14 +914,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, sai_clk_rate = clk_get_rate(sai->sai_ck); if (STM_SAI_IS_F4(sai->pdata)) { - /* - * mclk_rate = 256 * fs - * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate - * MCKDIV = sai_ck / (2 * mclk_rate) otherwise + /* mclk on (NODIV=0) + * mclk_rate = 256 * fs + * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate + * MCKDIV = sai_ck / (2 * mclk_rate) otherwise + * mclk off (NODIV=1) + * MCKDIV ignored. sck = sai_ck */ - if (2 * sai_clk_rate >= 3 * sai->mclk_rate) - div = DIV_ROUND_CLOSEST(sai_clk_rate, - 2 * sai->mclk_rate); + if (!sai->mclk_rate) + return 0; + + if (2 * sai_clk_rate >= 3 * sai->mclk_rate) { + div = stm32_sai_get_clk_div(sai, sai_clk_rate, + 2 * sai->mclk_rate); + if (div < 0) + return div; + } } else { /* * TDM mode : @@ -750,8 +941,10 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, * Note: NOMCK/NODIV correspond to same bit. */ if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { - div = DIV_ROUND_CLOSEST(sai_clk_rate, - (params_rate(params) * 128)); + div = stm32_sai_get_clk_div(sai, sai_clk_rate, + rate * 128); + if (div < 0) + return div; } else { if (sai->mclk_rate) { mclk_ratio = sai->mclk_rate / rate; @@ -764,31 +957,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, mclk_ratio); return -EINVAL; } - div = DIV_ROUND_CLOSEST(sai_clk_rate, - sai->mclk_rate); + div = stm32_sai_get_clk_div(sai, sai_clk_rate, + sai->mclk_rate); + if (div < 0) + return div; } else { /* mclk-fs not set, master clock not active */ den = sai->fs_length * params_rate(params); - div = DIV_ROUND_CLOSEST(sai_clk_rate, den); + div = stm32_sai_get_clk_div(sai, sai_clk_rate, + den); + if (div < 0) + return div; } } } - if (div > SAI_XCR1_MCKDIV_MAX(version)) { - dev_err(cpu_dai->dev, "Divider %d out of range\n", div); - return -EINVAL; - } - dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); - - mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); - cr1 = SAI_XCR1_MCKDIV_SET(div); - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); - if (ret < 0) { - dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); - return ret; - } - - return 0; + return stm32_sai_set_clk_div(sai, div); } static int stm32_sai_hw_params(struct snd_pcm_substream *substream, @@ -881,6 +1065,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, SAI_XCR1_NODIV); clk_disable_unprepare(sai->sai_ck); + + clk_rate_exclusive_put(sai->sai_mclk); + sai->substream = NULL; } @@ -888,11 +1075,12 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + struct snd_kcontrol_new knew = iec958_ctls; if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); - return snd_ctl_add(rtd->pcm->card, - snd_ctl_new1(&iec958_ctls, sai)); + knew.device = rtd->pcm->device; + return snd_ctl_add(rtd->pcm->card, snd_ctl_new1(&knew, sai)); } return 0; @@ -903,6 +1091,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); int cr1 = 0, cr1_mask; + sai->cpu_dai = cpu_dai; + sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); /* * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, @@ -910,6 +1100,8 @@ 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) + sai->dma_params.maxburst = 1; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -1080,7 +1272,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, sai->regmap_config = &stm32_sai_sub_regmap_config_f4; /* Note: PDM registers not available for H7 sub-block B */ - if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) + if (STM_SAI_HAS_PDM(sai) && STM_SAI_IS_SUB_A(sai)) sai->regmap_config = &stm32_sai_sub_regmap_config_h7; sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", @@ -1182,6 +1374,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return PTR_ERR(sai->sai_ck); } + if (STM_SAI_IS_F4(sai->pdata)) + return 0; + + /* Register mclk provider if requested */ + if (of_find_property(np, "#clock-cells", NULL)) { + ret = stm32_sai_add_mclk_provider(sai); + if (ret < 0) + return ret; + } else { + sai->sai_mclk = devm_clk_get(&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; + } + } + return 0; } @@ -1266,10 +1475,34 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int stm32_sai_sub_suspend(struct device *dev) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, true); + regcache_mark_dirty(sai->regmap); + return 0; +} + +static int stm32_sai_sub_resume(struct device *dev) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, false); + return regcache_sync(sai->regmap); +} +#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) +}; + 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, }, .probe = stm32_sai_sub_probe, }; diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 373df4f..693e30d 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -16,11 +16,13 @@ * details. */ +#include #include #include #include #include #include +#include #include #include @@ -35,6 +37,9 @@ #define STM32_SPDIFRX_DR 0x10 #define STM32_SPDIFRX_CSR 0x14 #define STM32_SPDIFRX_DIR 0x18 +#define STM32_SPDIFRX_VERR 0x3F4 +#define STM32_SPDIFRX_IDR 0x3F8 +#define STM32_SPDIFRX_SIDR 0x3FC /* Bit definition for SPDIF_CR register */ #define SPDIFRX_CR_SPDIFEN_SHIFT 0 @@ -168,6 +173,18 @@ #define SPDIFRX_SPDIFEN_SYNC 0x1 #define SPDIFRX_SPDIFEN_ENABLE 0x3 +/* Bit definition for SPDIFRX_VERR register */ +#define SPDIFRX_VERR_MIN_MASK GENMASK(3, 0) +#define SPDIFRX_VERR_MAJ_MASK GENMASK(7, 4) + +/* Bit definition for SPDIFRX_IDR register */ +#define SPDIFRX_IDR_ID_MASK GENMASK(31, 0) + +/* Bit definition for SPDIFRX_SIDR register */ +#define SPDIFRX_SIDR_SID_MASK GENMASK(31, 0) + +#define SPDIFRX_IPIDR_NUMBER 0x00130041 + #define SPDIFRX_IN1 0x1 #define SPDIFRX_IN2 0x2 #define SPDIFRX_IN3 0x3 @@ -471,6 +488,8 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB); memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB); + pinctrl_pm_select_default_state(&spdifrx->pdev->dev); + ret = stm32_spdifrx_dma_ctrl_start(spdifrx); if (ret < 0) return ret; @@ -493,7 +512,7 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) if (wait_for_completion_interruptible_timeout(&spdifrx->cs_completion, msecs_to_jiffies(100)) <= 0) { - dev_err(&spdifrx->pdev->dev, "Failed to get control data\n"); + dev_dbg(&spdifrx->pdev->dev, "Failed to get control data\n"); ret = -EAGAIN; } @@ -502,6 +521,7 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) end: clk_disable_unprepare(spdifrx->kclk); + pinctrl_pm_select_sleep_state(&spdifrx->pdev->dev); return ret; } @@ -603,6 +623,9 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) case STM32_SPDIFRX_DR: case STM32_SPDIFRX_CSR: case STM32_SPDIFRX_DIR: + case STM32_SPDIFRX_VERR: + case STM32_SPDIFRX_IDR: + case STM32_SPDIFRX_SIDR: return true; default: return false; @@ -611,10 +634,15 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) static bool stm32_spdifrx_volatile_reg(struct device *dev, unsigned int reg) { - if (reg == STM32_SPDIFRX_DR) + switch (reg) { + case STM32_SPDIFRX_DR: + case STM32_SPDIFRX_CSR: + case STM32_SPDIFRX_SR: + case STM32_SPDIFRX_DIR: return true; - - return false; + default: + return false; + } } static bool stm32_spdifrx_writeable_reg(struct device *dev, unsigned int reg) @@ -633,11 +661,12 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = STM32_SPDIFRX_DIR, + .max_register = STM32_SPDIFRX_SIDR, .readable_reg = stm32_spdifrx_readable_reg, .volatile_reg = stm32_spdifrx_volatile_reg, .writeable_reg = stm32_spdifrx_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) @@ -835,7 +864,8 @@ static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { static const struct snd_pcm_hardware stm32_spdifrx_pcm_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, .buffer_bytes_max = 8 * PAGE_SIZE, - .period_bytes_max = 2048, /* MDMA constraint */ + .period_bytes_min = 1024, /* 5ms at 48kHz */ + .period_bytes_max = PAGE_SIZE, .periods_min = 2, .periods_max = 8, }; @@ -901,6 +931,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) struct stm32_spdifrx_data *spdifrx; struct reset_control *rst; const struct snd_dmaengine_pcm_config *pcm_config = NULL; + u32 ver, idr; int ret; spdifrx = devm_kzalloc(&pdev->dev, sizeof(*spdifrx), GFP_KERNEL); @@ -957,7 +988,19 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) goto error; } - return 0; + ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_IDR, &idr); + if (ret) + goto error; + + if (idr == SPDIFRX_IPIDR_NUMBER) { + ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_VERR, &ver); + + dev_dbg(&pdev->dev, "SPDIFRX version: %lu.%lu registered\n", + FIELD_GET(SPDIFRX_VERR_MAJ_MASK, ver), + FIELD_GET(SPDIFRX_VERR_MIN_MASK, ver)); + } + + return ret; error: if (!IS_ERR(spdifrx->ctrl_chan)) @@ -983,10 +1026,34 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) 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); + + regcache_cache_only(spdifrx->regmap, true); + regcache_mark_dirty(spdifrx->regmap); + return 0; +} + +static int stm32_spdifrx_resume(struct device *dev) +{ + struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev); + + regcache_cache_only(spdifrx->regmap, false); + 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) +}; + static struct platform_driver stm32_spdifrx_driver = { .driver = { .name = "st,stm32-spdifrx", .of_match_table = stm32_spdifrx_ids, + .pm = &stm32_spdifrx_pm_ops, }, .probe = stm32_spdifrx_probe, .remove = stm32_spdifrx_remove, -- 2.7.4