From f3a6e1e545c6a1810b2560ea821a3ed61b751d60 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Mon, 20 Jan 2020 18:09:29 +0100 Subject: [PATCH 28/31] ARM stm32mp1 r3 SOUND --- sound/soc/codecs/Kconfig | 4 +- sound/soc/codecs/cs42l51-i2c.c | 34 ++- sound/soc/codecs/cs42l51.c | 231 +++++++++++++++-- sound/soc/codecs/cs42l51.h | 21 ++ sound/soc/codecs/wm8994.c | 80 +++++- sound/soc/stm/Kconfig | 1 + sound/soc/stm/stm32_adfsdm.c | 52 +++- sound/soc/stm/stm32_i2s.c | 186 +++++++++----- sound/soc/stm/stm32_sai.c | 134 ++++++++-- sound/soc/stm/stm32_sai.h | 59 ++++- sound/soc/stm/stm32_sai_sub.c | 562 +++++++++++++++++++++++++++++++++-------- sound/soc/stm/stm32_spdifrx.c | 132 ++++++++-- 12 files changed, 1245 insertions(+), 251 deletions(-) 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..9abbd98 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -29,18 +29,48 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, struct regmap_config config; config = cs42l51_regmap; - config.val_bits = 8; - config.reg_bits = 8; 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); +} + +#ifdef CONFIG_PM_SLEEP +static int cs42l51_suspend(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l51->regmap, true); + regcache_mark_dirty(cs42l51->regmap); + + return 0; +} + +static int cs42l51_resume(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l51->regmap, false); + + return regcache_sync(cs42l51->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops cs42l51_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume) +}; + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", .of_match_table = cs42l51_of_match, + .pm = &cs42l51_pm_ops, }, .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..83313ec 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,20 +30,14 @@ #include #include #include -#include - +#include #include "cs42l51.h" -enum master_slave_mode { - MODE_SLAVE, - MODE_SLAVE_AUTO, - MODE_MASTER, -}; - -struct cs42l51_private { - unsigned int mclk; - unsigned int audio_mode; /* The mode (I2S or left-justified) */ - enum master_slave_mode func; +static const char *cs42l51_supply_names[CS42L51_NUM_SUPPLIES] = { + "VL", + "VD", + "VA", + "VAHP", }; #define CS42L51_FORMATS ( \ @@ -109,6 +104,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); +static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0); static const char *chan_mix[] = { "L R", "L+R", @@ -137,6 +133,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = { SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0), SOC_DOUBLE_TLV("Mic Boost Volume", CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), + SOC_DOUBLE_TLV("ADC Boost Volume", + CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv), SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), SOC_ENUM_EXT("PCM channel mixer", @@ -165,6 +163,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 +192,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 +237,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 +327,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 +362,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 +404,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); @@ -458,6 +486,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute) return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg); } +static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + /* return dai id 0, whatever the endpoint index */ + return 0; +} + static const struct snd_soc_dai_ops cs42l51_dai_ops = { .hw_params = cs42l51_hw_params, .set_sysclk = cs42l51_set_dai_sysclk, @@ -487,6 +522,14 @@ static struct snd_soc_dai_driver cs42l51_dai = { 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 @@ -512,13 +555,113 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = { .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), .dapm_routes = cs42l51_routes, .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), + .of_xlate_dai_id = cs42l51_of_xlate_dai_id, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, .non_legacy_dai_naming = 1, }; +static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + +static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_STATUS: + return true; + default: + return false; + } +} + +static bool cs42l51_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_CHIP_REV_ID: + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_STATUS: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + const struct regmap_config cs42l51_regmap = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .use_single_rw = true, + .readable_reg = cs42l51_readable_reg, + .volatile_reg = cs42l51_volatile_reg, + .writeable_reg = cs42l51_writeable_reg, .max_register = CS42L51_CHARGE_FREQ, .cache_type = REGCACHE_RBTREE, }; @@ -528,7 +671,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); @@ -539,6 +682,42 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; dev_set_drvdata(dev, cs42l51); + cs42l51->regmap = regmap; + + 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); @@ -558,11 +737,29 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_component_device_cs42l51, &cs42l51_dai, 1); + 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..a086f7e 100644 --- a/sound/soc/codecs/cs42l51.h +++ b/sound/soc/codecs/cs42l51.h @@ -18,10 +18,14 @@ #ifndef _CS42L51_H #define _CS42L51_H +#include +#include + 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 @@ -164,5 +168,22 @@ extern const struct of_device_id cs42l51_of_match[]; */ #define CS42L51_LASTREG 0x20 #define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1) +#define CS42L51_NUM_SUPPLIES 4 + +enum master_slave_mode { + MODE_SLAVE, + MODE_SLAVE_AUTO, + MODE_MASTER, +}; + +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; + struct regmap *regmap; +}; #endif diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 01acb8d..c902ac7 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 @@ -844,6 +845,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 ? "MCLK2" : "MCLK1"); + 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); @@ -1161,7 +1198,6 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w, else adc = WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA; - val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_2); if ((val & WM8994_AIF2DACL_SRC) && (val & WM8994_AIF2DACR_SRC)) @@ -1780,6 +1816,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), @@ -2004,10 +2050,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" }, @@ -2381,11 +2427,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: @@ -3995,6 +4056,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; @@ -4278,6 +4340,14 @@ static int wm8994_component_probe(struct snd_soc_component *component) ARRAY_SIZE(wm8994_snd_controls)); 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 24948b95..33de130 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -18,6 +18,7 @@ #include #include +#include #include #define STM32_ADFSDM_DRV_NAME "stm32-adfsdm" @@ -44,8 +45,8 @@ struct stm32_adfsdm_priv { static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S32_LE, + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rate_min = 8000, .rate_max = 32000, @@ -141,7 +142,8 @@ static const struct snd_soc_dai_driver stm32_adfsdm_dai = { .capture = { .channels_min = 1, .channels_max = 1, - .formats = SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000), }, @@ -152,30 +154,58 @@ static const struct snd_soc_component_driver stm32_adfsdm_dai_component = { .name = "stm32_dfsdm_audio", }; +void stm32_memcpy_32to16(void *dest, const void *src, size_t n) +{ + unsigned int i = 0; + u16 *d = (u16 *)dest, *s = (u16 *)src; + + s++; + for (i = n >> 1; i > 0; i--) { + *d++ = *s++; + s++; + } +} + 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 = priv->substream->private_data; u8 *pcm_buff = priv->pcm_buff; u8 *src_buff = (u8 *)data; - unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream); - unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream); unsigned int old_pos = priv->pos; - unsigned int cur_size = size; + size_t buff_size = snd_pcm_lib_buffer_bytes(priv->substream); + size_t period_size = snd_pcm_lib_period_bytes(priv->substream); + size_t cur_size, src_size = size; + snd_pcm_format_t format = priv->substream->runtime->format; + + if (format == SNDRV_PCM_FORMAT_S16_LE) + src_size >>= 1; + cur_size = src_size; dev_dbg(rtd->dev, "%s: buff_add :%pK, pos = %d, size = %zu\n", - __func__, &pcm_buff[priv->pos], priv->pos, size); + __func__, &pcm_buff[priv->pos], priv->pos, src_size); - if ((priv->pos + size) > buff_size) { - memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos); + if ((priv->pos + src_size) > buff_size) { + if (format == SNDRV_PCM_FORMAT_S16_LE) + stm32_memcpy_32to16(&pcm_buff[priv->pos], src_buff, + buff_size - priv->pos); + else + memcpy(&pcm_buff[priv->pos], src_buff, + buff_size - priv->pos); cur_size -= buff_size - priv->pos; priv->pos = 0; } - memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size); + if (format == SNDRV_PCM_FORMAT_S16_LE) + stm32_memcpy_32to16(&pcm_buff[priv->pos], + &src_buff[src_size - cur_size], cur_size); + else + memcpy(&pcm_buff[priv->pos], &src_buff[src_size - cur_size], + cur_size); + priv->pos = (priv->pos + cur_size) % buff_size; - if (cur_size != size || (old_pos && (old_pos % period_size < size))) + if (cur_size != src_size || (old_pos && (old_pos % period_size < size))) snd_pcm_period_elapsed(priv->substream); return 0; diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index aa2b119..490352c 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,7 @@ 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 + * @irq_lock_t: prevent race condition with IRQ * @mclk_rate: master clock frequency (Hz) * @fmt: DAI protocol * @refcount: keep count of opened streams on I2S @@ -221,7 +242,7 @@ 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]; + spinlock_t irq_lock; /* used to prevent race condition with IRQ */ unsigned int mclk_rate; unsigned int fmt; int refcount; @@ -232,8 +253,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) { struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid; struct platform_device *pdev = i2s->pdev; - u32 sr, ier; unsigned long flags; + u32 sr, ier; int err = 0; regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr); @@ -262,8 +283,10 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) if (flags & I2S_SR_TIFRE) dev_dbg(&pdev->dev, "Frame error\n"); - if (err) + spin_lock(&i2s->irq_lock); + if (err && i2s->substream) snd_pcm_stop_xrun(i2s->substream); + spin_unlock(&i2s->irq_lock); return IRQ_HANDLED; } @@ -276,9 +299,12 @@ 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_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; @@ -288,7 +314,7 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { - case STM32_I2S_TXDR_REG: + case STM32_I2S_SR_REG: case STM32_I2S_RXDR_REG: return true; default: @@ -491,12 +517,6 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, unsigned int fthlv; int ret; - if ((params_channels(params) == 1) && - ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) { - dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n"); - return -EINVAL; - } - switch (format) { case 16: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); @@ -539,12 +559,22 @@ 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); + unsigned long flags; + int ret; + spin_lock_irqsave(&i2s->irq_lock, flags); i2s->substream = substream; + spin_unlock_irqrestore(&i2s->irq_lock, flags); - spin_lock(&i2s->lock_fd); - i2s->refcount++; - spin_unlock(&i2s->lock_fd); + if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A) + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + + 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_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, I2S_IFCR_MASK, I2S_IFCR_MASK); @@ -582,7 +612,8 @@ 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, @@ -595,8 +626,8 @@ 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; @@ -605,18 +636,19 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 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 */ + if (STM32_I2S_IS_MASTER(i2s) && (i2s->refcount == 1)) + /* dummy write to gate bus clocks */ 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; @@ -626,6 +658,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, @@ -641,16 +676,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, @@ -667,11 +701,16 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); - - i2s->substream = NULL; + unsigned long flags; regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); + + clk_disable_unprepare(i2s->i2sclk); + + spin_lock_irqsave(&i2s->irq_lock, flags); + i2s->substream = NULL; + spin_unlock_irqrestore(&i2s->irq_lock, flags); } static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) @@ -697,11 +736,13 @@ 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, + .num_reg_defaults_raw = STM32_I2S_SIDR_REG / sizeof(u32) + 1, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { @@ -716,7 +757,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, }; @@ -752,12 +794,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"); @@ -827,8 +865,9 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Get irqs */ irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); - return -ENOENT; + if (irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + return irq; } ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT, @@ -852,6 +891,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); @@ -865,76 +905,94 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->pdev = pdev; i2s->ms_flg = I2S_MS_NOT_SET; spin_lock_init(&i2s->lock_fd); + spin_lock_init(&i2s->irq_lock); platform_set_drvdata(pdev, i2s); ret = stm32_i2s_dais_init(pdev, i2s); 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; } -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 = platform_get_drvdata(pdev); + struct stm32_i2s_data *i2s = dev_get_drvdata(dev); - clk_disable_unprepare(i2s->i2sclk); - clk_disable_unprepare(i2s->pclk); + regcache_cache_only(i2s->regmap, true); + regcache_mark_dirty(i2s->regmap); return 0; } -MODULE_DEVICE_TABLE(of, stm32_i2s_ids); +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, }; module_platform_driver(stm32_i2s_driver); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 540c4a0..d6763a1 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -29,13 +31,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[] = { @@ -44,20 +53,39 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; -static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +static int stm32_sai_pclk_disable(struct device *dev) { + struct stm32_sai_data *sai = dev_get_drvdata(dev); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_pclk_enable(struct device *dev) +{ + struct stm32_sai_data *sai = dev_get_drvdata(dev); int ret; - /* Enable peripheral clock to allow GCR register access */ ret = clk_prepare_enable(sai->pclk); - if (ret) { + if (ret) dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + + return ret; +} + +static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +{ + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = stm32_sai_pclk_enable(&sai->pdev->dev); + if (ret) return ret; - } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); - clk_disable_unprepare(sai->pclk); + stm32_sai_pclk_disable(&sai->pdev->dev); return 0; } @@ -68,11 +96,9 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) int ret; /* Enable peripheral clock to allow GCR register access */ - ret = clk_prepare_enable(sai->pclk); - if (ret) { - dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + ret = stm32_sai_pclk_enable(&sai->pdev->dev); + if (ret) return ret; - } dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", sai->pdev->dev.of_node->name, @@ -83,13 +109,13 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", sai->pdev->dev.of_node->name, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); - clk_disable_unprepare(sai->pclk); + stm32_sai_pclk_disable(&sai->pdev->dev); return -EINVAL; } writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); - clk_disable_unprepare(sai->pclk); + stm32_sai_pclk_disable(&sai->pdev->dev); return 0; } @@ -113,19 +139,20 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, dev_err(&sai_client->pdev->dev, "SAI sync provider data not found\n"); ret = -EINVAL; - goto out_put_dev; + goto error; } /* Configure sync client */ ret = stm32_sai_sync_conf_client(sai_client, synci); if (ret < 0) - goto out_put_dev; + goto error; /* Configure sync provider */ ret = stm32_sai_sync_conf_provider(sai_provider, synco); -out_put_dev: +error: put_device(&pdev->dev); + of_node_put(np_provider); return ret; } @@ -135,6 +162,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) @@ -147,7 +176,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; @@ -186,6 +216,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); @@ -193,12 +247,54 @@ 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) +{ + struct stm32_sai_data *sai = dev_get_drvdata(dev); + int ret; + + ret = stm32_sai_pclk_enable(dev); + if (ret) + return ret; + + sai->gcr = readl_relaxed(sai->base); + stm32_sai_pclk_disable(dev); + + return pinctrl_pm_select_sleep_state(dev); +} + +static int stm32_sai_resume(struct device *dev) +{ + struct stm32_sai_data *sai = dev_get_drvdata(dev); + int ret; + + ret = stm32_sai_pclk_enable(dev); + if (ret) + return ret; + + writel_relaxed(sai->gcr, sai->base); + stm32_sai_pclk_disable(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..158c73f 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,9 +293,10 @@ 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 + * @gcr: SAI Global Configuration Register */ struct stm32_sai_data { struct platform_device *pdev; @@ -272,8 +304,9 @@ 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); + u32 gcr; }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 6c2e69e..8225665 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 @@ -98,6 +102,7 @@ * @spdif_frm_cnt: S/PDIF playback frame counter * @iec958: iec958 data * @ctrl_lock: control lock + * @spinlock_t: prevent race condition with IRQ */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -110,6 +115,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; @@ -128,6 +134,7 @@ struct stm32_sai_sub_data { unsigned int spdif_frm_cnt; 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 */ }; enum stm32_sai_fifo_th { @@ -151,6 +158,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 +172,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 +187,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: @@ -186,6 +197,56 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) } } +static int stm32_sai_sub_reg_up(struct stm32_sai_sub_data *sai, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + int ret; + + ret = clk_enable(sai->pdata->pclk); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sai->regmap, reg, mask, val); + + clk_disable(sai->pdata->pclk); + + return ret; +} + +static int stm32_sai_sub_reg_wr(struct stm32_sai_sub_data *sai, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + int ret; + + ret = clk_enable(sai->pdata->pclk); + if (ret < 0) + return ret; + + ret = regmap_write_bits(sai->regmap, reg, mask, val); + + clk_disable(sai->pdata->pclk); + + return ret; +} + +static int stm32_sai_sub_reg_rd(struct stm32_sai_sub_data *sai, + unsigned int reg, unsigned int *val) +{ + int ret; + + ret = clk_enable(sai->pdata->pclk); + if (ret < 0) + return ret; + + ret = regmap_read(sai->regmap, reg, val); + + clk_disable(sai->pdata->pclk); + + return ret; +} + static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, @@ -195,6 +256,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 +268,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 +314,194 @@ 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 = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, mask, cr1); + if (ret < 0) + dev_err(&sai->pdev->dev, "Failed to update CR1 register\n"); + + return ret; +} + +static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, + unsigned int rate) +{ + struct platform_device *pdev = sai->pdev; + struct clk *parent_clk = sai->pdata->clk_x8k; + int ret; + + if (!(rate % 11025)) + parent_clk = sai->pdata->clk_x11k; + + ret = clk_set_parent(sai->sai_ck, parent_clk); + if (ret) + dev_err(&pdev->dev, " Error %d setting sai_ck parent clock. %s", + ret, ret == -EBUSY ? + "Active stream rates conflict\n" : "\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 stm32_sai_sub_reg_up(sai, 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"); + + stm32_sai_sub_reg_up(sai, 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; @@ -258,15 +509,15 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) unsigned int sr, imr, flags; snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; - regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr); - regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr); + stm32_sai_sub_reg_rd(sai, STM_SAI_IMR_REGX, &imr); + stm32_sai_sub_reg_rd(sai, STM_SAI_SR_REGX, &sr); flags = sr & imr; if (!flags) return IRQ_NONE; - regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, - SAI_XCLRFR_MASK); + stm32_sai_sub_reg_wr(sai, 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); @@ -300,8 +551,10 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) status = SNDRV_PCM_STATE_XRUN; } - if (status != SNDRV_PCM_STATE_RUNNING) + spin_lock(&sai->irq_lock); + if (status != SNDRV_PCM_STATE_RUNNING && sai->substream) snd_pcm_stop_xrun(sai->substream); + spin_unlock(&sai->irq_lock); return IRQ_HANDLED; } @@ -312,15 +565,29 @@ 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) { - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_NODIV, - (unsigned int)~SAI_XCR1_NODIV); + if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { + ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, + SAI_XCR1_NODIV, + (unsigned int)~SAI_XCR1_NODIV); if (ret < 0) return ret; - sai->mclk_rate = freq; + /* If master clock is used, set parent clock now */ + ret = stm32_sai_set_parent_clock(sai, freq); + if (ret) + return ret; + + ret = clk_set_rate_exclusive(sai->sai_mclk, freq); + if (ret) { + dev_err(cpu_dai->dev, + ret == -EBUSY ? + "Active streams have incompatible rates" : + "Could not set mclk rate\n"); + return ret; + } + dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); + sai->mclk_rate = freq; } return 0; @@ -369,7 +636,7 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, slotr_mask |= SAI_XSLOTR_SLOTEN_MASK; - regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr); + stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, slotr_mask, slotr); sai->slot_width = slot_width; sai->slots = slots; @@ -451,7 +718,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) cr1_mask |= SAI_XCR1_CKSTR; frcr_mask |= SAI_XFRCR_FSPOL; - regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); + stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr); /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -479,7 +746,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) cr1_mask |= SAI_XCR1_SLAVE; conf_update: - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); + ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); if (ret < 0) { dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); return ret; @@ -495,8 +762,11 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int imr, cr2, ret; + unsigned long flags; + spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = substream; + spin_unlock_irqrestore(&sai->irq_lock, flags); if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { snd_pcm_hw_constraint_mask64(substream->runtime, @@ -513,13 +783,12 @@ 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); + stm32_sai_sub_reg_wr(sai, STM_SAI_CLRFR_REGX, + SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); imr = SAI_XIMR_OVRUDRIE; if (STM_SAI_IS_CAPTURE(sai)) { - regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2); + stm32_sai_sub_reg_rd(sai, STM_SAI_CR2_REGX, &cr2); if (cr2 & SAI_XCR2_MUTECNT_MASK) imr |= SAI_XIMR_MUTEDETIE; } @@ -529,8 +798,8 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, else imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE; - regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, - SAI_XIMR_MASK, imr); + stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, + SAI_XIMR_MASK, imr); return 0; } @@ -547,10 +816,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)); + stm32_sai_sub_reg_wr(sai, 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)) { @@ -579,7 +848,7 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, if ((sai->slots == 2) && (params_channels(params) == 1)) cr1 |= SAI_XCR1_MONO; - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); + ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); if (ret < 0) { dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); return ret; @@ -593,7 +862,7 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int slotr, slot_sz; - regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr); + stm32_sai_sub_reg_rd(sai, STM_SAI_SLOTR_REGX, &slotr); /* * If SLOTSZ is set to auto in SLOTR, align slot width on data size @@ -615,16 +884,16 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) sai->slots = 2; /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/ - regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, - SAI_XSLOTR_NBSLOT_MASK, - SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); + stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, + SAI_XSLOTR_NBSLOT_MASK, + SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); /* Set default slots mask if not already set from DT */ if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) { sai->slot_mask = (1 << sai->slots) - 1; - regmap_update_bits(sai->regmap, - STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, - SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); + stm32_sai_sub_reg_up(sai, + STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, + SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); } dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n", @@ -654,14 +923,14 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n", sai->fs_length, fs_active); - regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); + stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr); if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) { offset = sai->slot_width - sai->data_size; - regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, - SAI_XSLOTR_FBOFF_MASK, - SAI_XSLOTR_FBOFF_SET(offset)); + stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, + SAI_XSLOTR_FBOFF_MASK, + SAI_XSLOTR_FBOFF_SET(offset)); } } @@ -722,31 +991,35 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { 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 div = 0; + int sai_clk_rate, mclk_ratio, den; unsigned int rate = params_rate(params); + int ret, cr1 = 0; - if (!sai->mclk_rate) { - dev_err(cpu_dai->dev, "Mclk rate is null\n"); - return -EINVAL; + if (!sai->sai_mclk) { + ret = stm32_sai_set_parent_clock(sai, rate); + if (ret) + return ret; } - - if (!(rate % 11025)) - clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); - else - clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); 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 : @@ -758,13 +1031,14 @@ 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; if (mclk_ratio == 512) { - mask = SAI_XCR1_OSR; cr1 = SAI_XCR1_OSR; } else if (mclk_ratio != 256) { dev_err(cpu_dai->dev, @@ -772,31 +1046,27 @@ 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); + + stm32_sai_sub_reg_up(sai, + STM_SAI_CR1_REGX, + SAI_XCR1_OSR, cr1); + + 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, @@ -841,12 +1111,12 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n"); - regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); + stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, + SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); /* Enable SAI */ - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); + ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, + SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); if (ret < 0) dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); break; @@ -855,16 +1125,16 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_STOP: dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); - regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, - SAI_XIMR_MASK, 0); + stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, + SAI_XIMR_MASK, 0); - regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_SAIEN, - (unsigned int)~SAI_XCR1_SAIEN); + stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, + SAI_XCR1_SAIEN, + (unsigned int)~SAI_XCR1_SAIEN); - ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_DMAEN, - (unsigned int)~SAI_XCR1_DMAEN); + ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, + SAI_XCR1_DMAEN, + (unsigned int)~SAI_XCR1_DMAEN); if (ret < 0) dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); @@ -882,14 +1152,24 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + + stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); - regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); + stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, + SAI_XCR1_NODIV); - regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, - SAI_XCR1_NODIV); + /* Release mclk rate only if rate was actually set */ + if (sai->mclk_rate) { + clk_rate_exclusive_put(sai->sai_mclk); + sai->mclk_rate = 0; + } clk_disable_unprepare(sai->sai_ck); + + spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = NULL; + spin_unlock_irqrestore(&sai->irq_lock, flags); } static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, @@ -910,7 +1190,9 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, 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; + int cr1 = 0, cr1_mask, ret; + + sai->cpu_dai = cpu_dai; sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); /* @@ -919,6 +1201,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; @@ -938,14 +1222,16 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) /* Configure synchronization */ if (sai->sync == SAI_SYNC_EXTERNAL) { /* Configure synchro client and provider */ - sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, - sai->synco, sai->synci); + ret = sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, + sai->synco, sai->synci); + if (ret) + return ret; } cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); - return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); + return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1); } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { @@ -1099,11 +1385,16 @@ 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", - base, sai->regmap_config); + /* + * Do not manage peripheral clock through regmap framework as this + * can lead to circular locking issue with sai master clock provider. + * Manage peripheral clock directly in driver instead. + */ + sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, + sai->regmap_config); if (IS_ERR(sai->regmap)) { dev_err(&pdev->dev, "Failed to initialize MMIO\n"); return PTR_ERR(sai->regmap); @@ -1201,6 +1492,27 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return PTR_ERR(sai->sai_ck); } + ret = clk_prepare(sai->pdata->pclk); + if (ret < 0) + return ret; + + 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; } @@ -1245,6 +1557,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) sai->pdev = pdev; mutex_init(&sai->ctrl_lock); + spin_lock_init(&sai->irq_lock); platform_set_drvdata(pdev, sai); sai->pdata = dev_get_drvdata(pdev->dev.parent); @@ -1285,12 +1598,63 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) return 0; } +static int stm32_sai_sub_remove(struct platform_device *pdev) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(&pdev->dev); + + clk_unprepare(sai->pdata->pclk); + + 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); + int ret; + + ret = clk_enable(sai->pdata->pclk); + if (ret < 0) + return ret; + + regcache_cache_only(sai->regmap, true); + regcache_mark_dirty(sai->regmap); + + clk_disable(sai->pdata->pclk); + + return 0; +} + +static int stm32_sai_sub_resume(struct device *dev) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(sai->pdata->pclk); + if (ret < 0) + return ret; + + regcache_cache_only(sai->regmap, false); + ret = regcache_sync(sai->regmap); + + clk_disable(sai->pdata->pclk); + + 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) +}; + 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, + .remove = stm32_sai_sub_remove, }; module_platform_driver(stm32_sai_sub_driver); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 373df4f..5a5094e 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -16,6 +16,7 @@ * details. */ +#include #include #include #include @@ -35,6 +36,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 +172,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 @@ -213,6 +229,7 @@ * @slave_config: dma slave channel runtime config pointer * @phys_addr: SPDIFRX registers physical base address * @lock: synchronization enabling lock + * @irq_lock: prevent race condition with IRQ on stream state * @cs: channel status buffer * @ub: user data buffer * @irq: SPDIFRX interrupt line @@ -233,6 +250,7 @@ struct stm32_spdifrx_data { struct dma_slave_config slave_config; dma_addr_t phys_addr; spinlock_t lock; /* Sync enabling lock */ + spinlock_t irq_lock; /* Prevent race condition on stream state */ unsigned char cs[SPDIFRX_CS_BYTES_NB]; unsigned char ub[SPDIFRX_UB_BYTES_NB]; int irq; @@ -313,6 +331,7 @@ static void stm32_spdifrx_dma_ctrl_stop(struct stm32_spdifrx_data *spdifrx) static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) { int cr, cr_mask, imr, ret; + unsigned long flags; /* Enable IRQs */ imr = SPDIFRX_IMR_IFEIE | SPDIFRX_IMR_SYNCDIE | SPDIFRX_IMR_PERRIE; @@ -320,7 +339,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) if (ret) return ret; - spin_lock(&spdifrx->lock); + spin_lock_irqsave(&spdifrx->lock, flags); spdifrx->refcount++; @@ -344,6 +363,8 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO; cr_mask = cr; + cr |= SPDIFRX_CR_NBTRSET(SPDIFRX_NBTR_63); + cr_mask |= SPDIFRX_CR_NBTR_MASK; cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); cr_mask |= SPDIFRX_CR_SPDIFEN_MASK; ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, @@ -353,7 +374,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) "Failed to start synchronization\n"); } - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); return ret; } @@ -361,11 +382,12 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx) { int cr, cr_mask, reg; + unsigned long flags; - spin_lock(&spdifrx->lock); + spin_lock_irqsave(&spdifrx->lock, flags); if (--spdifrx->refcount) { - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); return; } @@ -384,7 +406,7 @@ static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx) regmap_read(spdifrx->regmap, STM32_SPDIFRX_DR, ®); regmap_read(spdifrx->regmap, STM32_SPDIFRX_CSR, ®); - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); } static int stm32_spdifrx_dma_ctrl_register(struct device *dev, @@ -493,7 +515,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; } @@ -603,6 +625,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 +636,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,20 +663,21 @@ 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, + .num_reg_defaults_raw = STM32_SPDIFRX_SIDR / sizeof(u32) + 1, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) { struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid; - struct snd_pcm_substream *substream = spdifrx->substream; struct platform_device *pdev = spdifrx->pdev; unsigned int cr, mask, sr, imr; - unsigned int flags; + unsigned int flags, sync_state; int err = 0, err_xrun = 0; regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr); @@ -706,19 +737,36 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) } if (err) { - /* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */ + regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr); + sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) && + SPDIFRX_SPDIFEN_SYNC; + + /* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */ cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE); regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, SPDIFRX_CR_SPDIFEN_MASK, cr); - if (substream) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + /* If SPDIFRX was in STATE_SYNC, retry synchro */ + if (sync_state) { + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_SPDIFEN_MASK, cr); + return IRQ_HANDLED; + } + + spin_lock(&spdifrx->irq_lock); + if (spdifrx->substream) + snd_pcm_stop(spdifrx->substream, + SNDRV_PCM_STATE_DISCONNECTED); + spin_unlock(&spdifrx->irq_lock); return IRQ_HANDLED; } - if (err_xrun && substream) - snd_pcm_stop_xrun(substream); + spin_lock(&spdifrx->irq_lock); + if (err_xrun && spdifrx->substream) + snd_pcm_stop_xrun(spdifrx->substream); + spin_unlock(&spdifrx->irq_lock); return IRQ_HANDLED; } @@ -727,9 +775,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; int ret; + spin_lock_irqsave(&spdifrx->irq_lock, flags); spdifrx->substream = substream; + spin_unlock_irqrestore(&spdifrx->irq_lock, flags); ret = clk_prepare_enable(spdifrx->kclk); if (ret) @@ -805,8 +856,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + spin_lock_irqsave(&spdifrx->irq_lock, flags); spdifrx->substream = NULL; + spin_unlock_irqrestore(&spdifrx->irq_lock, flags); + clk_disable_unprepare(spdifrx->kclk); } @@ -835,7 +890,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 +957,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); @@ -910,6 +967,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->pdev = pdev; init_completion(&spdifrx->cs_completion); spin_lock_init(&spdifrx->lock); + spin_lock_init(&spdifrx->irq_lock); platform_set_drvdata(pdev, spdifrx); @@ -957,7 +1015,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 +1053,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