From dcf821319474edde7e85b95608a4539703a2b67d Mon Sep 17 00:00:00 2001 From: David Rhodes Date: Wed, 5 Jan 2022 11:30:19 +0000 Subject: [PATCH 01/23] ASoC: cs35l41: Add cs35l51/53 IDs Add IDs for the CS35L51/53 variants, the functionality is shared with CS35L41. Signed-off-by: David Rhodes Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-i2c.c | 2 ++ sound/soc/codecs/cs35l41-spi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index de5c8612f030..eb8dfb6d9c95 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -22,6 +22,8 @@ static const struct i2c_device_id cs35l41_id_i2c[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index c157153f28d8..86bbe2fba956 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -20,6 +20,8 @@ static const struct spi_device_id cs35l41_id_spi[] = { { "cs35l40", 0 }, { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, {} }; From 4e7c3cd87db8d9350062a25a8476f90fd1cbc4c9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:20 +0000 Subject: [PATCH 02/23] ASoC: cs35l41: Remove incorrect comment The IRQ is not used for the PDN_DONE bit, this is polled during the DAPM sequence, remove the misleading comment. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index d9e6e84e64d0..980294c1bcdb 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1338,8 +1338,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq, IRQF_ONESHOT | IRQF_SHARED | irq_pol, "cs35l41", cs35l41); - - /* CS35L41 needs INT for PDN_DONE */ if (ret != 0) { dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret); goto err; From 56852cf4b2179fb90068a49538501f31c2de18ea Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:21 +0000 Subject: [PATCH 03/23] ASoC: cs35l41: Correct DSP power down The wm_adsp_event should be called before the early_event on power down, event stops the core running and early_event then powers down the core. Additionally, the core should only be stopped if it was actually running in the first place. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 980294c1bcdb..05839fabf97b 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -181,17 +181,21 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); int ret; switch (event) { case SND_SOC_DAPM_PRE_PMU: return wm_adsp_early_event(w, kcontrol, event); case SND_SOC_DAPM_PRE_PMD: - ret = wm_adsp_early_event(w, kcontrol, event); - if (ret) - return ret; + if (cs35l41->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } - return wm_adsp_event(w, kcontrol, event); + return wm_adsp_early_event(w, kcontrol, event); default: return 0; } From 5f2f539901b0d9bda722637521a11b7f7cf753f1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:22 +0000 Subject: [PATCH 04/23] ASoC: cs35l41: Correct handling of some registers in the cache It makes no sense to cache the test/user key registers, since they require values written at specific times, mark them volatile. It is probably best if they can't be accessed from user-space either, so mark them precious as well. The interrupt force, edge, polarity and debounce are all settings applied to the IRQ rather than status bits and as such should not be volatile. The OTP trim values will require re-application in the event of a cache sync and as such should not be volatile. The OTPID however should be volatile. The DSP scratch registers are used to read back an error/debug code from the DSP on shutdown, as such these should be marked volatile. Finally, add some missing defaults, add TST_FS_MON0, and allow the DSP core control register to be cached. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-lib.c | 81 +++++++++------------------------- 1 file changed, 22 insertions(+), 59 deletions(-) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index d026c5e3a378..639dcd25b17e 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -20,6 +20,11 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_PWR_CTRL3, 0x01000010 }, { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, + { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_COEFF, 0x00002424 }, + { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_SP_ENABLES, 0x00000000 }, { CS35L41_SP_RATE_CTRL, 0x00000028 }, { CS35L41_SP_FORMAT, 0x18180200 }, @@ -48,11 +53,16 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_WKFET_CFG, 0x00000111 }, { CS35L41_NG_CFG, 0x00000033 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_IRQ1_MASK1, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, + { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, { CS35L41_GPIO1_CTRL1, 0xE1000001 }, { CS35L41_GPIO2_CTRL1, 0xE1000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, + { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 }, }; static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) @@ -84,6 +94,7 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_DSP_CLK_CTRL: case CS35L41_GLOBAL_CLK_CTRL: case CS35L41_DATA_FS_SEL: + case CS35L41_TST_FS_MON0: case CS35L41_MDSYNC_EN: case CS35L41_MDSYNC_TX_ID: case CS35L41_MDSYNC_PWR_CTRL: @@ -342,7 +353,10 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_TST_FS_MON0: case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: @@ -359,6 +373,9 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_SFT_RESET: case CS35L41_FABID: case CS35L41_REVID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: case CS35L41_DTEMP_EN: case CS35L41_IRQ1_STATUS: case CS35L41_IRQ1_STATUS1: @@ -369,17 +386,6 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ1_RAW_STATUS2: case CS35L41_IRQ1_RAW_STATUS3: case CS35L41_IRQ1_RAW_STATUS4: - case CS35L41_IRQ1_FRC1: - case CS35L41_IRQ1_FRC2: - case CS35L41_IRQ1_FRC3: - case CS35L41_IRQ1_FRC4: - case CS35L41_IRQ1_EDGE1: - case CS35L41_IRQ1_EDGE4: - case CS35L41_IRQ1_POL1: - case CS35L41_IRQ1_POL2: - case CS35L41_IRQ1_POL3: - case CS35L41_IRQ1_POL4: - case CS35L41_IRQ1_DB3: case CS35L41_IRQ2_STATUS: case CS35L41_IRQ2_STATUS1: case CS35L41_IRQ2_STATUS2: @@ -389,54 +395,7 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_IRQ2_RAW_STATUS2: case CS35L41_IRQ2_RAW_STATUS3: case CS35L41_IRQ2_RAW_STATUS4: - case CS35L41_IRQ2_FRC1: - case CS35L41_IRQ2_FRC2: - case CS35L41_IRQ2_FRC3: - case CS35L41_IRQ2_FRC4: - case CS35L41_IRQ2_EDGE1: - case CS35L41_IRQ2_EDGE4: - case CS35L41_IRQ2_POL1: - case CS35L41_IRQ2_POL2: - case CS35L41_IRQ2_POL3: - case CS35L41_IRQ2_POL4: - case CS35L41_IRQ2_DB3: case CS35L41_GPIO_STATUS1: - case CS35L41_OTP_TRIM_1: - case CS35L41_OTP_TRIM_2: - case CS35L41_OTP_TRIM_3: - case CS35L41_OTP_TRIM_4: - case CS35L41_OTP_TRIM_5: - case CS35L41_OTP_TRIM_6: - case CS35L41_OTP_TRIM_7: - case CS35L41_OTP_TRIM_8: - case CS35L41_OTP_TRIM_9: - case CS35L41_OTP_TRIM_10: - case CS35L41_OTP_TRIM_11: - case CS35L41_OTP_TRIM_12: - case CS35L41_OTP_TRIM_13: - case CS35L41_OTP_TRIM_14: - case CS35L41_OTP_TRIM_15: - case CS35L41_OTP_TRIM_16: - case CS35L41_OTP_TRIM_17: - case CS35L41_OTP_TRIM_18: - case CS35L41_OTP_TRIM_19: - case CS35L41_OTP_TRIM_20: - case CS35L41_OTP_TRIM_21: - case CS35L41_OTP_TRIM_22: - case CS35L41_OTP_TRIM_23: - case CS35L41_OTP_TRIM_24: - case CS35L41_OTP_TRIM_25: - case CS35L41_OTP_TRIM_26: - case CS35L41_OTP_TRIM_27: - case CS35L41_OTP_TRIM_28: - case CS35L41_OTP_TRIM_29: - case CS35L41_OTP_TRIM_30: - case CS35L41_OTP_TRIM_31: - case CS35L41_OTP_TRIM_32: - case CS35L41_OTP_TRIM_33: - case CS35L41_OTP_TRIM_34: - case CS35L41_OTP_TRIM_35: - case CS35L41_OTP_TRIM_36: case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: @@ -445,7 +404,11 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: - case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: From 7aa1cc1091e0a424e9e7711ca381ebe98b6865bc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:23 +0000 Subject: [PATCH 05/23] firmware: cs_dsp: Clear core reset for cache If the Halo registers are kept in the register cache the HALO_CORE_RESET bit will be retained as 1 after reset is triggered in cs_dsp_halo_start_core. This will cause subsequent writes to reset the core which is not desired. Apart from this bit the rest of the register bits are cacheable, so for safety sake clear the bit to ensure the cache is consistent. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 5af8171d6ced..e48108e694f8 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -2744,10 +2744,16 @@ EXPORT_SYMBOL_GPL(cs_dsp_stop); static int cs_dsp_halo_start_core(struct cs_dsp *dsp) { - return regmap_update_bits(dsp->regmap, - dsp->base + HALO_CCM_CORE_CONTROL, - HALO_CORE_RESET | HALO_CORE_EN, - HALO_CORE_RESET | HALO_CORE_EN); + int ret; + + ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET | HALO_CORE_EN, + HALO_CORE_RESET | HALO_CORE_EN); + if (ret) + return ret; + + return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, + HALO_CORE_RESET, 0); } static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) From ba235634b138cd9d012dbe983e7920481211e132 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jan 2022 11:30:24 +0000 Subject: [PATCH 06/23] ASoC: wm_adsp: Add support for "toggle" preloaders In the case a device can support retaining the firmware memory across low power states it is useful for the preloader widget to only power up whilst actually loading/unloading the core, as opposed to the normal operation where the widget is powered for the entire time a firmware is preloaded onto the core. Add support for this mode and a flag to enable it. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220105113026.18955-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 14 +++++++++++--- sound/soc/codecs/wm_adsp.h | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index c3112bf23866..f3672e3d1703 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -896,11 +896,12 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; + if (dsp->preloaded == ucontrol->value.integer.value[0]) + return 0; + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); - dsp->preloaded = ucontrol->value.integer.value[0]; - - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.integer.value[0] || dsp->toggle_preload) snd_soc_component_force_enable_pin(component, preload); else snd_soc_component_disable_pin(component, preload); @@ -909,6 +910,13 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, flush_work(&dsp->boot_work); + dsp->preloaded = ucontrol->value.integer.value[0]; + + if (dsp->toggle_preload) { + snd_soc_component_disable_pin(component, preload); + snd_soc_dapm_sync(dapm); + } + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 0e2f113bd342..7f4fabbc6ad3 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -41,6 +41,14 @@ struct wm_adsp { struct list_head compr_list; struct list_head buffer_list; + + /* + * Flag indicating the preloader widget only needs power toggled + * on state change rather than held on for the duration of the + * preload, useful for devices that can retain firmware memory + * across power down. + */ + bool toggle_preload; }; #define WM_ADSP1(wname, num) \ From a319cb32e7cfd2703db3a883ce260a7b06729895 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Jan 2022 15:06:13 -0300 Subject: [PATCH 07/23] ASoC: cs4265: Add a remove() function When the reset_gpio GPIO is used, it is better to put the codec back into reset state when the driver unbinds. Add a remove() function to accomplish that. Suggested-by: Charles Keepax Signed-off-by: Fabio Estevam Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220104180613.639317-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs4265.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index b89002189a2b..4aaee1873a11 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -626,6 +626,16 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, ARRAY_SIZE(cs4265_dai)); } +static int cs4265_i2c_remove(struct i2c_client *i2c) +{ + struct cs4265_private *cs4265 = i2c_get_clientdata(i2c); + + if (cs4265->reset_gpio) + gpiod_set_value_cansleep(cs4265->reset_gpio, 0); + + return 0; +} + static const struct of_device_id cs4265_of_match[] = { { .compatible = "cirrus,cs4265", }, { } @@ -645,6 +655,7 @@ static struct i2c_driver cs4265_i2c_driver = { }, .id_table = cs4265_id, .probe = cs4265_i2c_probe, + .remove = cs4265_i2c_remove, }; module_i2c_driver(cs4265_i2c_driver); From e4c35e75209bda13e57c9bc8d280366c2b9275a5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Jan 2022 17:24:09 +0100 Subject: [PATCH 08/23] ASoC: ak4375: Fix unused function error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A randconfig caught a compile warning that is now treated as a fatal error: sound/soc/codecs/ak4375.c:415:13: error: ‘ak4375_power_off’ defined but not used [-Werror=unused-function] where ak4375_power_off() is used only from the PM handler. As both suspend and resumes are already marked with __maybe_unused, let's rip off the superfluous ifdef CONFIG_PM, so that the error above can be avoided. Fixes: 53778b8292b5 ("ASoC: Add AK4375 support") Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20220105162409.20635-1-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/codecs/ak4375.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 22cda0699341..9a7b662016b9 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -438,7 +438,6 @@ static int ak4375_power_on(struct ak4375_priv *ak4375) return 0; } -#ifdef CONFIG_PM static int __maybe_unused ak4375_runtime_suspend(struct device *dev) { struct ak4375_priv *ak4375 = dev_get_drvdata(dev); @@ -463,7 +462,6 @@ static int __maybe_unused ak4375_runtime_resume(struct device *dev) return regcache_sync(ak4375->regmap); } -#endif /* CONFIG_PM */ static const struct snd_soc_component_driver soc_codec_dev_ak4375 = { .controls = ak4375_snd_controls, From 3349b3d0c63b8b6fcca58156d72407f0b2e101ac Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:33 +0800 Subject: [PATCH 09/23] ASoC: imx-card: Need special setting for ak4497 on i.MX8MQ The SAI on i.MX8MQ don't support one2one ratio for mclk:bclk, so the mclk frequency exceeds the supported range of codec for the case that sample rate is larger than 705kHZ and format is S32_LE. Update the supported width for such case. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index e758c4f1b0bc..e0f3aa6d9501 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -553,8 +553,23 @@ static int imx_card_parse_of(struct imx_card_data *data) link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1; /* sai may support mclk/bclk = 1 */ - if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) + if (of_find_property(np, "fsl,mclk-equal-bclk", NULL)) { link_data->one2one_ratio = true; + } else { + int i; + + /* + * i.MX8MQ don't support one2one ratio, then + * with ak4497 only 16bit case is supported. + */ + for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) { + if (ak4497_fs_mul[i].rmin == 705600 && + ak4497_fs_mul[i].rmax == 768000) { + ak4497_fs_mul[i].wmin = 32; + ak4497_fs_mul[i].wmax = 32; + } + } + } } link->cpus->of_node = args.np; From f331ae5fa59fbfb748317b290648fc3f1a50d932 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:34 +0800 Subject: [PATCH 10/23] ASoC: imx-card: Fix mclk calculation issue for akcodec Transfer the refined slots and slot_width to akcodec_get_mclk_rate() for mclk calculation, otherwise the mclk frequency does not match with the slots and slot_width for S16_LE format, because the default slot_width is 32. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index e0f3aa6d9501..f9196fb7e833 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -247,13 +247,14 @@ static bool codec_is_akcodec(unsigned int type) } static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + int slots, int slot_width) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card); const struct imx_card_plat_data *plat_data = data->plat_data; struct dai_link_data *link_data = &data->link_data[rtd->num]; - unsigned int width = link_data->slots * link_data->slot_width; + unsigned int width = slots * slot_width; unsigned int rate = params_rate(params); int i; @@ -349,7 +350,7 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream, /* Set MCLK freq */ if (codec_is_akcodec(plat_data->type)) - mclk_freq = akcodec_get_mclk_rate(substream, params); + mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width); else mclk_freq = params_rate(params) * slots * slot_width; /* Use the maximum freq from DSD512 (512*44100 = 22579200) */ From 3969341813eb56d2dfc39bb64229359a6ae3c195 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 4 Jan 2022 18:40:35 +0800 Subject: [PATCH 11/23] ASoC: imx-card: improve the sound quality for low rate According to RM, on auto mode: For codec AK4458 and AK4497, the lowest ratio of MLCK/FS is 256 if sample rate is 8kHz-48kHz, For codec AK5558, the lowest ratio of MLCK/FS is 512 if sample rate is 8kHz-48kHz. With these setting the sound quality for 8kHz-48kHz can be improved. Fixes: aa736700f42f ("ASoC: imx-card: Add imx-card machine driver") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641292835-19085-4-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index f9196fb7e833..6f8efd838fcc 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -120,7 +120,7 @@ struct imx_card_data { static struct imx_akcodec_fs_mul ak4458_fs_mul[] = { /* Normal, < 32kHz */ - { .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, }, /* Normal, 32kHz */ { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal */ @@ -151,8 +151,8 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * Table 7 - mapping multiplier and speed mode * Tables 8 & 9 - mapping speed mode and LRCK fs */ - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */ - { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, /* Normal */ + { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */ + { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */ { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */ @@ -164,7 +164,7 @@ static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { * (Table 4 from datasheet) */ static struct imx_akcodec_fs_mul ak5558_fs_mul[] = { - { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, + { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, }, { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, From a2d6d84db2e7bcc831aed90f33334c70a1b060a3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:23 +0100 Subject: [PATCH 12/23] ASoC: rt5640: Fix possible NULL pointer deref on resume Commit 2b9c8d2b3c89 ("ASoC: rt5640: Add the HDA header support") adds re-queuing of the jack_work on resume when rt5640->jd_src != 0. But the jack_work will unconditionally deref rt5640->jack and that might be NULL. E.g. the sound/soc/intel/boards/bytcr_rt5640.c machine driver call snd_soc_component_set_jack(codec, NULL, NULL) from pre_suspend to disable the IRQ to avoid spurious wakeups, so when rt5640_resume() runs rt5640->jack will be NULL in this case. Make the queueing of the work conditional on rt5640->jack instead of on rt5640->jd_src to fix this. Fixes: 2b9c8d2b3c89 ("ASoC: rt5640: Add the HDA header support") Cc: Oder Chiou Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-2-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f3659b14c74e..ceb2d50b17aa 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2737,7 +2737,7 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); - if (rt5640->jd_src) { + if (rt5640->jack) { if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); From a3b1aaf7aef9fa945810de3fd7c15b2e93ecdbfd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:24 +0100 Subject: [PATCH 13/23] ASoC: rt5640: Change jack_work to a delayed_work Change jack_work from a struct work_struct to a struct delayed_work, this is a preparation patch for adding support for boards where an external GPIO is used for jack-detect, rather then one of the JD pins of the codec. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-3-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 16 ++++++++-------- sound/soc/codecs/rt5640.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index ceb2d50b17aa..a1e4e3ac99f1 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2298,7 +2298,7 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset); static void rt5640_jack_work(struct work_struct *work) { struct rt5640_priv *rt5640 = - container_of(work, struct rt5640_priv, jack_work); + container_of(work, struct rt5640_priv, jack_work.work); struct snd_soc_component *component = rt5640->component; int status; @@ -2381,7 +2381,7 @@ static void rt5640_jack_work(struct work_struct *work) * disabled the OVCD IRQ, the IRQ pin will stay high and as * we react to edges, we miss the unplug event -> recheck. */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } } @@ -2390,7 +2390,7 @@ static irqreturn_t rt5640_irq(int irq, void *data) struct rt5640_priv *rt5640 = data; if (rt5640->jack) - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); return IRQ_HANDLED; } @@ -2399,7 +2399,7 @@ static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; - cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->jack_work); cancel_delayed_work_sync(&rt5640->bp_work); } @@ -2508,7 +2508,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static void rt5640_enable_hda_jack_detect( @@ -2546,7 +2546,7 @@ static void rt5640_enable_hda_jack_detect( } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static int rt5640_set_jack(struct snd_soc_component *component, @@ -2745,7 +2745,7 @@ static int rt5640_resume(struct snd_soc_component *component) snd_soc_component_write(component, RT5640_DUMMY2, 0x4001); - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } return 0; @@ -2950,7 +2950,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, rt5640->hp_mute = true; rt5640->irq = i2c->irq; INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); - INIT_WORK(&rt5640->jack_work, rt5640_jack_work); + INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); /* Make sure work is stopped on probe-error / remove */ ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2c28f83e338a..7ab930def8dd 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2145,7 +2145,7 @@ struct rt5640_priv { int release_count; int poll_count; struct delayed_work bp_work; - struct work_struct jack_work; + struct delayed_work jack_work; struct snd_soc_jack *jack; unsigned int jd_src; bool jd_inverted; From b35a9ab4904973a68b4473c2985b8ac0b6d57089 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:25 +0100 Subject: [PATCH 14/23] ASoC: rt5640: Allow snd_soc_component_set_jack() to override the codec IRQ On some boards where the firmware/fwnode information is in essence read-only (x86 + ACPI boards) the i2c_client for the codec may contain the wrong IRQ or no IRQ at all. Since we only request the IRQ once snd_soc_component_set_jack() gets called, allow machine drivers to override the IRQ with the proper one through the data parameter to snd_soc_component_set_jack(). Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-4-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 8 ++++++-- sound/soc/codecs/rt5640.h | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index a1e4e3ac99f1..fabc6e44b4a6 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2452,7 +2452,8 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) } static void rt5640_enable_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) + struct snd_soc_jack *jack, + struct rt5640_set_jack_data *jack_data) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int ret; @@ -2496,6 +2497,9 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640_enable_micbias1_ovcd_irq(component); } + if (jack_data && jack_data->codec_irq_override) + rt5640->irq = jack_data->codec_irq_override; + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); @@ -2558,7 +2562,7 @@ static int rt5640_set_jack(struct snd_soc_component *component, if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) rt5640_enable_hda_jack_detect(component, jack); else - rt5640_enable_jack_detect(component, jack); + rt5640_enable_jack_detect(component, jack, data); } else { rt5640_disable_jack_detect(component); } diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 7ab930def8dd..2f4da5a8ecb2 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2153,6 +2153,10 @@ struct rt5640_priv { unsigned int ovcd_sf; }; +struct rt5640_set_jack_data { + int codec_irq_override; +}; + int rt5640_dmic_enable(struct snd_soc_component *component, bool dmic1_data_pin, bool dmic2_data_pin); int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, From 701d636a224a77a4371f57ca2d4322ab0401a866 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:26 +0100 Subject: [PATCH 15/23] ASoC: rt5640: Add support for boards with an external jack-detect GPIO Some boards have the codec IRQ hooked-up as normally, so the driver can still do things like headset vs headphones and button-press detection, but instead of using one of the JD pins of the codec, an external GPIO is used to report the jack-presence switch status of the jack. Add support for this. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-5-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 45 +++++++++++++++++++++++++++++++++++---- sound/soc/codecs/rt5640.h | 5 +++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index fabc6e44b4a6..e7a82565b905 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2160,7 +2160,11 @@ static bool rt5640_jack_inserted(struct snd_soc_component *component) struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int val; - val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + if (rt5640->jd_gpio) + val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0; + else + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); if (rt5640->jd_inverted) @@ -2395,6 +2399,16 @@ static irqreturn_t rt5640_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + queue_delayed_work(system_long_wq, &rt5640->jack_work, + msecs_to_jiffies(JACK_SETTLE_TIME)); + + return IRQ_HANDLED; +} + static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; @@ -2439,7 +2453,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) if (!rt5640->jack) return; - free_irq(rt5640->irq, rt5640); + if (rt5640->jd_gpio_irq_requested) + free_irq(rt5640->jd_gpio_irq, rt5640); + + if (rt5640->irq_requested) + free_irq(rt5640->irq, rt5640); + rt5640_cancel_work(rt5640); if (rt5640->jack->status & SND_JACK_MICROPHONE) { @@ -2448,6 +2467,9 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } + rt5640->jd_gpio_irq_requested = false; + rt5640->irq_requested = false; + rt5640->jd_gpio = NULL; rt5640->jack = NULL; } @@ -2500,16 +2522,31 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, if (jack_data && jack_data->codec_irq_override) rt5640->irq = jack_data->codec_irq_override; + if (jack_data && jack_data->jd_gpio) { + rt5640->jd_gpio = jack_data->jd_gpio; + rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); + + ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", + rt5640->jd_gpio_irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->jd_gpio_irq_requested = true; + } + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); - rt5640->irq = -ENXIO; - /* Undo above settings */ rt5640_disable_jack_detect(component); return; } + rt5640->irq_requested = true; /* sync initial jack state */ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2f4da5a8ecb2..9e49b9a0ccaa 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2124,6 +2124,7 @@ struct rt5640_priv { int ldo1_en; /* GPIO for LDO1_EN */ int irq; + int jd_gpio_irq; int sysclk; int sysclk_src; int lrck[RT5640_AIFS]; @@ -2136,6 +2137,8 @@ struct rt5640_priv { bool hp_mute; bool asrc_en; + bool irq_requested; + bool jd_gpio_irq_requested; /* Jack and button detect data */ bool ovcd_irq_enabled; @@ -2147,6 +2150,7 @@ struct rt5640_priv { struct delayed_work bp_work; struct delayed_work jack_work; struct snd_soc_jack *jack; + struct gpio_desc *jd_gpio; unsigned int jd_src; bool jd_inverted; unsigned int ovcd_th; @@ -2155,6 +2159,7 @@ struct rt5640_priv { struct rt5640_set_jack_data { int codec_irq_override; + struct gpio_desc *jd_gpio; }; int rt5640_dmic_enable(struct snd_soc_component *component, From 45ed0166c39f878162872babc88830d91426beb5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:27 +0100 Subject: [PATCH 16/23] ASoC: Intel: bytcr_rt5640: Support retrieving the codec IRQ from the AMCR0F28 ACPI dev Some X86 tablets, which ship with Android as factory installed OS, specify codec IRQs/GPIOS in a special Android AMCR0F28 ACPI device. Add support for retrieving the codec IRQ from this ACPI device instead of from the 10EC5640 device describing the codec itself and enable this on Asus MemoPad 7 ME176C tablets. This fixes jack-detect not working on these tablets. Cc: Stephan Gerhold Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-6-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 43 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a0c5f0e9c22a..f37ab44ae957 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -79,6 +79,7 @@ enum { #define BYT_RT5640_LINEOUT_AS_HP2 BIT(26) #define BYT_RT5640_HSMIC2_ON_IN1 BIT(27) #define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28) +#define BYT_RT5640_USE_AMCR0F28 BIT(29) #define BYTCR_INPUT_DEFAULTS \ (BYT_RT5640_IN3_MAP | \ @@ -93,6 +94,7 @@ enum { struct byt_rt5640_private { struct snd_soc_jack jack; struct snd_soc_jack jack2; + struct rt5640_set_jack_data jack_data; struct gpio_desc *hsmic_detect; struct clk *mclk; struct device *codec_dev; @@ -597,7 +599,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | - BYT_RT5640_MCLK_EN), + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), }, { .matches = { @@ -1109,6 +1112,32 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, return ret; } +/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) +{ + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct rt5640_set_jack_data *data = &priv->jack_data; + struct acpi_device *adev; + int ret = 0; + + adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1); + if (!adev) { + dev_err(card->dev, "error cannot find AMCR0F28 adev\n"); + return -ENOENT; + } + + data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0); + if (data->codec_irq_override < 0) { + ret = data->codec_irq_override; + dev_err(card->dev, "error %d getting codec IRQ\n", ret); + goto put_adev; + } + +put_adev: + acpi_dev_put(adev); + return ret; +} + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; @@ -1244,7 +1273,14 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_soc_component_set_jack(component, &priv->jack, NULL); + + if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) { + ret = byt_rt5640_get_amcr0f28_settings(card); + if (ret) + return ret; + } + + snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data); } if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) { @@ -1448,7 +1484,8 @@ static int byt_rt5640_resume(struct snd_soc_card *card) for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); - snd_soc_component_set_jack(component, &priv->jack, NULL); + snd_soc_component_set_jack(component, &priv->jack, + &priv->jack_data); break; } } From 44125fd5315154c6b8326b5c27646af3b33ba25c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2022 12:01:28 +0100 Subject: [PATCH 17/23] ASoC: Intel: bytcr_rt5640: Add support for external GPIO jack-detect Some boards have the codec IRQ hooked-up as normally, so the driver can still do things like headset vs headphones and button-press detection, but instead of using one of the JD pins of the codec, an external GPIO is used to report the jack-presence switch status of the jack. Add support for boards which have this setup and which specify which external GPIO to use in the special Android AMCR0F28 ACPI device. And add a quirk for the Asus TF103C tablet which uses this setup. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20220106110128.66049-7-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 43 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index f37ab44ae957..2ace32c03ec9 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -40,6 +40,8 @@ enum { BYT_RT5640_NO_INTERNAL_MIC_MAP, }; +#define RT5640_JD_SRC_EXT_GPIO 0x0f + enum { BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4), BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4), @@ -47,6 +49,7 @@ enum { BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4), BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4), BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4), + BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4) }; enum { @@ -627,6 +630,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_EXT_GPIO | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), + }, { /* Chuwi Vi8 (CWI506) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), @@ -1083,9 +1099,11 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { - props[cnt++] = PROPERTY_ENTRY_U32( - "realtek,jack-detect-source", - BYT_RT5640_JDSRC(byt_rt5640_quirk)); + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jack-detect-source", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + } props[cnt++] = PROPERTY_ENTRY_U32( "realtek,over-current-threshold-microamp", @@ -1113,6 +1131,13 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } /* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping amcr0f28_gpios[] = { + { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 }, + { } +}; + static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) { struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); @@ -1133,6 +1158,18 @@ static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) goto put_adev; } + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) { + acpi_dev_add_driver_gpios(adev, amcr0f28_gpios); + data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev), + "rt5640-jd", GPIOD_IN, "rt5640-jd"); + acpi_dev_remove_driver_gpios(adev); + + if (IS_ERR(data->jd_gpio)) { + ret = PTR_ERR(data->jd_gpio); + dev_err(card->dev, "error %d getting jd GPIO\n", ret); + } + } + put_adev: acpi_dev_put(adev); return ret; From 320386343451ab6a3577e0ee200dac56a6182944 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 5 Jan 2022 19:08:03 +0800 Subject: [PATCH 18/23] ASoC: fsl_asrc: refine the check of available clock divider According to RM, the clock divider range is from 1 to 8, clock prescaling ratio may be any power of 2 from 1 to 128. So the supported divider is not all the value between 1 and 1024, just limited value in that range. Create table for the supported divder and add function to check the clock divider is available by comparing with the table. Fixes: d0250cf4f2ab ("ASoC: fsl_asrc: Add an option to select internal ratio mode") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1641380883-20709-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 69 +++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 24b41881a68f..d7d1536a4f37 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -19,6 +19,7 @@ #include "fsl_asrc.h" #define IDEAL_RATIO_DECIMAL_DEPTH 26 +#define DIVIDER_NUM 64 #define pair_err(fmt, ...) \ dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) @@ -101,6 +102,55 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { }, }; +/* + * According to RM, the divider range is 1 ~ 8, + * prescaler is power of 2 from 1 ~ 128. + */ +static int asrc_clk_divider[DIVIDER_NUM] = { + 1, 2, 4, 8, 16, 32, 64, 128, /* divider = 1 */ + 2, 4, 8, 16, 32, 64, 128, 256, /* divider = 2 */ + 3, 6, 12, 24, 48, 96, 192, 384, /* divider = 3 */ + 4, 8, 16, 32, 64, 128, 256, 512, /* divider = 4 */ + 5, 10, 20, 40, 80, 160, 320, 640, /* divider = 5 */ + 6, 12, 24, 48, 96, 192, 384, 768, /* divider = 6 */ + 7, 14, 28, 56, 112, 224, 448, 896, /* divider = 7 */ + 8, 16, 32, 64, 128, 256, 512, 1024, /* divider = 8 */ +}; + +/* + * Check if the divider is available for internal ratio mode + */ +static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div) +{ + u32 rem, i; + u64 n; + + if (div) + *div = 0; + + if (clk_rate == 0 || rate == 0) + return false; + + n = clk_rate; + rem = do_div(n, rate); + + if (div) + *div = n; + + if (rem != 0) + return false; + + for (i = 0; i < DIVIDER_NUM; i++) { + if (n == asrc_clk_divider[i]) + break; + } + + if (i == DIVIDER_NUM) + return false; + + return true; +} + /** * fsl_asrc_sel_proc - Select the pre-processing and post-processing options * @inrate: input sample rate @@ -330,12 +380,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) enum asrc_word_width input_word_width; enum asrc_word_width output_word_width; u32 inrate, outrate, indiv, outdiv; - u32 clk_index[2], div[2], rem[2]; + u32 clk_index[2], div[2]; u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; - bool ideal; + bool ideal, div_avail; if (!config) { pair_err("invalid pair config\n"); @@ -415,8 +465,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; clk_rate = clk_get_rate(clk); - rem[IN] = do_div(clk_rate, inrate); - div[IN] = (u32)clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]); /* * The divider range is [1, 1024], defined by the hardware. For non- @@ -425,7 +474,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) * only result in different converting speeds. So remainder does not * matter, as long as we keep the divider within its valid range. */ - if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) { + if (div[IN] == 0 || (!ideal && !div_avail)) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; @@ -436,13 +485,12 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) clk = asrc_priv->asrck_clk[clk_index[OUT]]; clk_rate = clk_get_rate(clk); if (ideal && use_ideal_rate) - rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE); + div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]); else - rem[OUT] = do_div(clk_rate, outrate); - div[OUT] = clk_rate; + div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]); /* Output divider has the same limitation as the input one */ - if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) { + if (div[OUT] == 0 || (!ideal && !div_avail)) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; @@ -621,8 +669,7 @@ static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv, clk_index = asrc_priv->clk_map[j][i]; clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]); /* Only match a perfect clock source with no remainder */ - if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 && - (clk_rate % rate[j]) == 0) + if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL)) break; } From 00ac838924f73b51e82994c7fc870f0a994e4d34 Mon Sep 17 00:00:00 2001 From: Qinghua Jin Date: Thu, 6 Jan 2022 17:28:47 +0800 Subject: [PATCH 19/23] ASoC: topology: Fix typo change 'postion' to 'position' Signed-off-by: Qinghua Jin Link: https://lore.kernel.org/r/20220106092847.357035-1-qhjin.dev@gmail.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f5b9e66ac3b8..2630df024dff 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -56,7 +56,7 @@ struct soc_tplg { const struct firmware *fw; /* runtime FW parsing */ - const u8 *pos; /* read postion */ + const u8 *pos; /* read position */ const u8 *hdr_pos; /* header position */ unsigned int pass; /* pass number */ From 7112550890d7e415188a3351ec0a140be60f6deb Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Thu, 6 Jan 2022 20:35:21 +0530 Subject: [PATCH 20/23] ASoC: amd: acp: acp-mach: Change default RT1019 amp dev id RT1019 components was initially registered with i2c1 and i2c2 but now changed to i2c0 and i2c1 in most of our AMD platforms. Change default rt1019 components to 10EC1019:00 and 10EC1019:01 which is aligned with most of AMD machines. Any exception to rt1019 device ids in near future board design can be handled using dmi based quirk for that machine. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220106150525.396170-1-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 03d8d1af14b3..c9caade5cb74 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -293,8 +293,8 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = { /* Declare RT1019 codec components */ SND_SOC_DAILINK_DEF(rt1019, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"), - COMP_CODEC("i2c-10EC1019:02", "rt1019-aif"))); + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), + COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"))); static const struct snd_soc_dapm_route rt1019_map_lr[] = { { "Left Spk", NULL, "Left SPO" }, @@ -303,11 +303,11 @@ static const struct snd_soc_dapm_route rt1019_map_lr[] = { static struct snd_soc_codec_conf rt1019_conf[] = { { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"), .name_prefix = "Left", }, { - .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"), + .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"), .name_prefix = "Right", }, }; From 3b247eeaecfefe35ecca1578b0ed48be65bc6ca3 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Tue, 4 Jan 2022 03:35:36 +0000 Subject: [PATCH 21/23] ASoC: wcd9335: Keep a RX port value for each SLIM RX mux Currently, rx_port_value is a single unsigned int that gets overwritten when slim_rx_mux_put() is called for any RX mux, then the same value is read when slim_rx_mux_get() is called for any of them. This results in slim_rx_mux_get() reporting the last value set by slim_rx_mux_put() regardless of which SLIM RX mux is in question. Turn rx_port_value into an array and store a separate value for each SLIM RX mux. Signed-off-by: Yassine Oudjana Link: https://lore.kernel.org/r/20220104033356.343685-1-y.oudjana@protonmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index bc5d68c53e5a..1e60db4056ad 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -341,7 +341,7 @@ struct wcd9335_codec { int reset_gpio; struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; - unsigned int rx_port_value; + unsigned int rx_port_value[WCD9335_RX_MAX]; unsigned int tx_port_value; int hph_l_gain; int hph_r_gain; @@ -1269,10 +1269,11 @@ static const struct snd_kcontrol_new sb_tx8_mux = static int slim_rx_mux_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); - struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + u32 port_id = w->shift; - ucontrol->value.enumerated.item[0] = wcd->rx_port_value; + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id]; return 0; } @@ -1286,9 +1287,9 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, struct snd_soc_dapm_update *update = NULL; u32 port_id = w->shift; - wcd->rx_port_value = ucontrol->value.enumerated.item[0]; + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; - switch (wcd->rx_port_value) { + switch (wcd->rx_port_value[port_id]) { case 0: list_del_init(&wcd->rx_chs[port_id].list); break; @@ -1309,11 +1310,11 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, &wcd->dai[AIF4_PB].slim_ch_list); break; default: - dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value); + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]); goto err; } - snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value, + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], e, update); return 0; From d92321bbe46b0ecae0941461379d39599610d869 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Jan 2022 16:06:35 +0000 Subject: [PATCH 22/23] ASoC: cs35l41: Update handling of test key registers In preparation for the addition of PM runtime support move the test key out of the register patches themselves. This is necessary to allow the test key to be held during cache synchronisation, which is required by the OTP settings which were unpacked from the device and written by the driver. Also whilst at it, the driver uses a mixture of accessing the test key register by name and by address, consistently use the name. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220107160636.6555-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 2 + sound/soc/codecs/cs35l41-lib.c | 67 +++++++++++++++++----------------- sound/soc/codecs/cs35l41.c | 32 +++++++++------- 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 29a527457b48..56289b67b9a0 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -762,6 +762,8 @@ struct cs35l41_otp_map_element_t { extern struct regmap_config cs35l41_regmap_i2c; extern struct regmap_config cs35l41_regmap_spi; +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap); +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap); int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); int cs35l41_set_channels(struct device *dev, struct regmap *reg, diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 639dcd25b17e..ecaf67fd7653 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -623,8 +623,6 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] }; static const struct reg_sequence cs35l41_reva0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { 0x00003854, 0x05180240 }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, @@ -637,38 +635,28 @@ static const struct reg_sequence cs35l41_reva0_errata_patch[] = { { CS35L41_IRQ2_DB3, 0x00000000 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb0_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, { CS35L41_VPVBST_FS_SEL, 0x00000000 }, { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; static const struct reg_sequence cs35l41_revb2_errata_patch[] = { - { 0x00000040, 0x00005555 }, - { 0x00000040, 0x0000AAAA }, { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, { 0x00004310, 0x00000000 }, { CS35L41_VPVBST_FS_SEL, 0x00000000 }, { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, - { 0x00000040, 0x0000CCCC }, - { 0x00000040, 0x00003333 }, { CS35L41_PWR_CTRL2, 0x00000000 }, { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, }; @@ -756,6 +744,39 @@ static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) return NULL; } +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to unlock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock); + +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to lock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_lock); + +/* Must be called with the TEST_KEY unlocked */ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) { const struct cs35l41_otp_map_element_t *otp_map_match; @@ -794,17 +815,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) bit_offset = otp_map_match->bit_offset; word_offset = otp_map_match->word_offset; - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000055); - if (ret) { - dev_err(dev, "Write Unlock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); - if (ret) { - dev_err(dev, "Write Unlock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } - for (i = 0; i < otp_map_match->num_elements; i++) { dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", bit_offset, word_offset, bit_sum % 32); @@ -840,16 +850,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) } } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); - if (ret) { - dev_err(dev, "Write Lock key failed 1/2: %d\n", ret); - goto err_otp_unpack; - } - ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000033); - if (ret) { - dev_err(dev, "Write Lock key failed 2/2: %d\n", ret); - goto err_otp_unpack; - } ret = 0; err_otp_unpack: @@ -859,6 +859,7 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) } EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); +/* Must be called with the TEST_KEY unlocked */ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid) { char *rev; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 05839fabf97b..e1b9fd8ee996 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -534,19 +534,19 @@ static irqreturn_t cs35l41_irq(int irq, void *data) } static const struct reg_sequence cs35l41_pup_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA0 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static const struct reg_sequence cs35l41_pdn_patch[] = { - { 0x00000040, 0x00000055 }, - { 0x00000040, 0x000000AA }, + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, { 0x00002084, 0x002F1AA3 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, }; static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, @@ -1329,10 +1329,20 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); if (ret) goto err; + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret < 0) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; + } + + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + irq_pol = cs35l41_irq_gpio_config(cs35l41); /* Set interrupt masks for critical errors */ @@ -1347,12 +1357,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } - ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); - if (ret < 0) { - dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); - goto err; - } - ret = cs35l41_set_pdata(cs35l41); if (ret < 0) { dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); From f517ba4924ad026f2583553db02f3c8bc69de88b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Jan 2022 16:06:36 +0000 Subject: [PATCH 23/23] ASoC: cs35l41: Add support for hibernate memory retention mode The cs35l41 supports a low power DSP memory retention mode. Add support for entering this mode when then device is not in use. Co-authored-by: David Rhodes Signed-off-by: David Rhodes Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220107160636.6555-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 5 + sound/soc/codecs/cs35l41-i2c.c | 1 + sound/soc/codecs/cs35l41-lib.c | 6 + sound/soc/codecs/cs35l41-spi.c | 1 + sound/soc/codecs/cs35l41.c | 201 ++++++++++++++++++++++++++++++++- sound/soc/codecs/cs35l41.h | 4 + 6 files changed, 214 insertions(+), 4 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 56289b67b9a0..bf7f9a9aeba0 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -40,6 +40,9 @@ #define CS35L41_PROTECT_REL_ERR_IGN 0x00002034 #define CS35L41_GPIO_PAD_CONTROL 0x0000242C #define CS35L41_JTAG_CONTROL 0x00002438 +#define CS35L41_PWRMGT_CTL 0x00002900 +#define CS35L41_WAKESRC_CTL 0x00002904 +#define CS35L41_PWRMGT_STS 0x00002908 #define CS35L41_PLL_CLK_CTRL 0x00002C04 #define CS35L41_DSP_CLK_CTRL 0x00002C08 #define CS35L41_GLOBAL_CLK_CTRL 0x00002C0C @@ -635,6 +638,8 @@ #define CS35L41_INPUT_DSP_TX1 0x32 #define CS35L41_INPUT_DSP_TX2 0x33 +#define CS35L41_WR_PEND_STS_MASK 0x2 + #define CS35L41_PLL_CLK_SEL_MASK 0x07 #define CS35L41_PLL_CLK_SEL_SHIFT 0 #define CS35L41_PLL_CLK_EN_MASK 0x10 diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index eb8dfb6d9c95..faad5c638cb8 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -86,6 +86,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct i2c_driver cs35l41_i2c_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index ecaf67fd7653..e5a56bcbb223 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -90,6 +90,9 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_PROTECT_REL_ERR_IGN: case CS35L41_GPIO_PAD_CONTROL: case CS35L41_JTAG_CONTROL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_PLL_CLK_CTRL: case CS35L41_DSP_CLK_CTRL: case CS35L41_GLOBAL_CLK_CTRL: @@ -376,6 +379,9 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_OTPID: case CS35L41_TEST_KEY_CTL: case CS35L41_USER_KEY_CTL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: case CS35L41_DTEMP_EN: case CS35L41_IRQ1_STATUS: case CS35L41_IRQ1_STATUS1: diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index 86bbe2fba956..6dfd5459aa20 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -84,6 +84,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); static struct spi_driver cs35l41_spi_driver = { .driver = { .name = "cs35l41", + .pm = &cs35l41_pm_ops, .of_match_table = of_match_ptr(cs35l41_of_match), .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index e1b9fd8ee996..77a017694645 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -187,8 +188,14 @@ static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + if (cs35l41->dsp.cs_dsp.booted) + return 0; + return wm_adsp_early_event(w, kcontrol, event); case SND_SOC_DAPM_PRE_PMD: + if (cs35l41->dsp.preloaded) + return 0; + if (cs35l41->dsp.cs_dsp.running) { ret = wm_adsp_event(w, kcontrol, event); if (ret) @@ -209,6 +216,7 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, case CSPL_MBOX_CMD_UNKNOWN_CMD: return true; case CSPL_MBOX_CMD_PAUSE: + case CSPL_MBOX_CMD_OUT_OF_HIBERNATE: return (sts == CSPL_MBOX_STS_PAUSED); case CSPL_MBOX_CMD_RESUME: return (sts == CSPL_MBOX_STS_RUNNING); @@ -230,7 +238,8 @@ static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41, // Set mailbox cmd ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd); if (ret < 0) { - dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret); return ret; } @@ -413,6 +422,8 @@ static irqreturn_t cs35l41_irq(int irq, void *data) int ret = IRQ_NONE; unsigned int i; + pm_runtime_get_sync(cs35l41->dev); + for (i = 0; i < ARRAY_SIZE(status); i++) { regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE), @@ -425,7 +436,7 @@ static irqreturn_t cs35l41_irq(int irq, void *data) /* Check to see if unmasked bits are active */ if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) - return IRQ_NONE; + goto done; if (status[3] & CS35L41_OTP_BOOT_DONE) { regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4, @@ -530,6 +541,10 @@ static irqreturn_t cs35l41_irq(int irq, void *data) ret = IRQ_HANDLED; } +done: + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_put_autosuspend(cs35l41->dev); + return ret; } @@ -1180,6 +1195,7 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) dsp->cs_dsp.type = WMFW_HALO; dsp->cs_dsp.rev = 0; dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; dsp->cs_dsp.dev = cs35l41->dev; dsp->cs_dsp.regmap = cs35l41->regmap; dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE; @@ -1367,20 +1383,32 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, if (ret < 0) goto err; + pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); + pm_runtime_use_autosuspend(cs35l41->dev); + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_set_active(cs35l41->dev); + pm_runtime_get_noresume(cs35l41->dev); + pm_runtime_enable(cs35l41->dev); + ret = devm_snd_soc_register_component(cs35l41->dev, &soc_component_dev_cs35l41, cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); if (ret < 0) { dev_err(cs35l41->dev, "Register codec failed: %d\n", ret); - goto err_dsp; + goto err_pm; } + pm_runtime_put_autosuspend(cs35l41->dev); + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); return 0; -err_dsp: +err_pm: + pm_runtime_disable(cs35l41->dev); + pm_runtime_put_noidle(cs35l41->dev); + wm_adsp2_remove(&cs35l41->dsp); err: regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); @@ -1392,13 +1420,178 @@ EXPORT_SYMBOL_GPL(cs35l41_probe); void cs35l41_remove(struct cs35l41_private *cs35l41) { + pm_runtime_get_sync(cs35l41->dev); + pm_runtime_disable(cs35l41->dev); + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); wm_adsp2_remove(&cs35l41->dsp); + + pm_runtime_put_noidle(cs35l41->dev); + regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } EXPORT_SYMBOL_GPL(cs35l41_remove); +static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Runtime suspend\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + dev_dbg(cs35l41->dev, "Enter hibernate\n"); + + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + // Don't wait for ACK since bus activity would wake the device + regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, + CSPL_MBOX_CMD_HIBERNATE); + + regcache_cache_only(cs35l41->regmap, true); + regcache_mark_dirty(cs35l41->regmap); + + return 0; +} + +static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41) +{ + const int pwrmgt_retries = 10; + unsigned int sts; + int i, ret; + + for (i = 0; i < pwrmgt_retries; i++) { + ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts); + if (ret) + dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret); + else if (!(sts & CS35L41_WR_PEND_STS_MASK)) + return; + + udelay(20); + } + + dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n"); +} + +static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41) +{ + const int wake_retries = 20; + const int sleep_retries = 5; + int ret, i, j; + + for (i = 0; i < sleep_retries; i++) { + dev_dbg(cs35l41->dev, "Exit hibernate\n"); + + for (j = 0; j < wake_retries; j++) { + ret = cs35l41_set_cspl_mbox_cmd(cs35l41, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE); + if (!ret) + break; + + usleep_range(100, 200); + } + + if (j < wake_retries) { + dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j); + return 0; + } + + dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188); + + cs35l41_wait_for_pwrmgt_sts(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3); + } + + dev_err(cs35l41->dev, "Timed out waking device\n"); + + return -ETIMEDOUT; +} + +static int __maybe_unused cs35l41_runtime_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + int ret; + + dev_dbg(cs35l41->dev, "Runtime resume\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + regcache_cache_only(cs35l41->regmap, false); + + ret = cs35l41_exit_hibernate(cs35l41); + if (ret) + return ret; + + /* Test key needs to be unlocked to allow the OTP settings to re-apply */ + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = regcache_sync(cs35l41->regmap); + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); + return ret; + } + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +const struct dev_pm_ops cs35l41_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL) + + SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq) +}; +EXPORT_SYMBOL_GPL(cs35l41_pm_ops); + MODULE_DESCRIPTION("ASoC CS35L41 driver"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 26a08d58a8c3..88a3d6e3434f 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -21,6 +21,8 @@ #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +extern const struct dev_pm_ops cs35l41_pm_ops; + enum cs35l41_cspl_mbox_status { CSPL_MBOX_STS_RUNNING = 0, CSPL_MBOX_STS_PAUSED = 1, @@ -33,6 +35,8 @@ enum cs35l41_cspl_mbox_cmd { CSPL_MBOX_CMD_RESUME = 2, CSPL_MBOX_CMD_REINIT = 3, CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_HIBERNATE = 5, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6, CSPL_MBOX_CMD_UNKNOWN_CMD = -1, CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, };