From 74b14e2850a34740c121cf2758d4181063d4c77c Mon Sep 17 00:00:00 2001 From: "Vlad.Karpovich" Date: Wed, 15 Mar 2023 10:47:21 -0500 Subject: ASoC: cs35l45: DSP Support The CS35L45 digital core incorporates one programmable DSP block, capable of running a wide range of audio enhancement and speaker and battery protection functions. Signed-off-by: Vlad Karpovich Link: https://lore.kernel.org/r/167933510679.26.5992985447093367768@mailman-core.alsa-project.org Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l45.c | 391 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 379 insertions(+), 12 deletions(-) (limited to 'sound/soc/codecs/cs35l45.c') diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index a7095da34f5a..97f6819fa2ad 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,69 @@ #include "cs35l45.h" +static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd, + enum cs35l45_cspl_mboxstate sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + 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); + case CSPL_MBOX_CMD_REINIT: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_STOP_PRE_REINIT: + return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + default: + return false; + } +} + +static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45, + struct regmap *regmap, + const enum cs35l45_cspl_mboxcmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + if (!cs35l45->dsp.cs_dsp.running) { + dev_err(cs35l45->dev, "DSP not running\n"); + return -EPERM; + } + + // Set mailbox cmd + ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd); + if (ret < 0) { + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret); + return ret; + } + + // Read mailbox status and verify it is appropriate for the given cmd + for (i = 0; i < 5; i++) { + usleep_range(1000, 1100); + + ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l45_check_cspl_mbox_sts(cmd, sts)) + dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts); + else + return 0; + } + + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts); + + return -ENOMSG; +} + static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -46,10 +110,68 @@ static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w, return 0; } +static int cs35l45_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 cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs35l45->dsp.cs_dsp.booted) + return 0; + + return wm_adsp_early_event(w, kcontrol, event); + case SND_SOC_DAPM_POST_PMU: + if (cs35l45->dsp.cs_dsp.running) + return 0; + + regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL, + CS35L45_MEM_RDY_MASK); + + return wm_adsp_event(w, kcontrol, event); + case SND_SOC_DAPM_PRE_PMD: + if (cs35l45->dsp.preloaded) + return 0; + + if (cs35l45->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } + + return wm_adsp_early_event(w, kcontrol, event); + default: + return 0; + } +} + +static int cs35l45_dsp_audio_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 cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap, + CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap, + CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } + + return 0; +} + static const char * const cs35l45_asp_tx_txt[] = { "Zero", "ASP_RX1", "ASP_RX2", "VMON", "IMON", "ERR_VOL", "VDD_BATTMON", "VDD_BSTMON", + "DSP_TX1", "DSP_TX2", "Interpolator", "IL_TARGET", }; @@ -57,6 +179,7 @@ static const unsigned int cs35l45_asp_tx_val[] = { CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON, + CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2, CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET, }; @@ -78,12 +201,54 @@ static const struct soc_enum cs35l45_asp_tx_enums[] = { cs35l45_asp_tx_val), }; +static const char * const cs35l45_dsp_rx_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2", + "VMON", "IMON", "ERR_VOL", + "CLASSH_TGT", "VDD_BATTMON", + "VDD_BSTMON", "TEMPMON", +}; + +static const unsigned int cs35l45_dsp_rx_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, + CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON, + CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON, +}; + +static const struct soc_enum cs35l45_dsp_rx_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), +}; + static const char * const cs35l45_dac_txt[] = { - "Zero", "ASP_RX1", "ASP_RX2" + "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2" }; static const unsigned int cs35l45_dac_val[] = { - CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2 + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2 }; static const struct soc_enum cs35l45_dacpcm_enums[] = { @@ -100,11 +265,29 @@ static const struct snd_kcontrol_new cs35l45_asp_muxes[] = { SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]), }; +static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = { + SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]), + SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]), + SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]), + SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]), + SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]), + SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]), + SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]), + SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]), +}; + static const struct snd_kcontrol_new cs35l45_dac_muxes[] = { SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]), }; static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l45_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l45_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0, cs35l45_global_en_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -139,6 +322,15 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]), SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]), + SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]), + SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]), + SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]), + SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]), + SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]), + SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]), + SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]), + SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]), + SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -149,6 +341,8 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { #define CS35L45_ASP_MUX_ROUTE(name) \ { name" Source", "ASP_RX1", "ASP_RX1" }, \ { name" Source", "ASP_RX2", "ASP_RX2" }, \ + { name" Source", "DSP_TX1", "DSP1" }, \ + { name" Source", "DSP_TX2", "DSP1" }, \ { name" Source", "VMON", "VMON" }, \ { name" Source", "IMON", "IMON" }, \ { name" Source", "ERR_VOL", "ERR_VOL" }, \ @@ -157,10 +351,16 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { { name" Source", "Interpolator", "AMP_INTP" }, \ { name" Source", "IL_TARGET", "IL_TARGET" } -#define CS35L45_DAC_MUX_ROUTE(name) \ +#define CS35L45_DSP_MUX_ROUTE(name) \ { name" Source", "ASP_RX1", "ASP_RX1" }, \ { name" Source", "ASP_RX2", "ASP_RX2" } +#define CS35L45_DAC_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" }, \ + { name" Source", "DSP_TX1", "DSP1" }, \ + { name" Source", "DSP_TX2", "DSP1" } + static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { /* Feedback */ { "VMON", NULL, "VMON_SRC" }, @@ -204,6 +404,27 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { { "AMP", NULL, "DACPCM1 Source"}, { "AMP", NULL, "GLOBAL_EN"}, + CS35L45_DSP_MUX_ROUTE("DSP_RX1"), + CS35L45_DSP_MUX_ROUTE("DSP_RX2"), + CS35L45_DSP_MUX_ROUTE("DSP_RX3"), + CS35L45_DSP_MUX_ROUTE("DSP_RX4"), + CS35L45_DSP_MUX_ROUTE("DSP_RX5"), + CS35L45_DSP_MUX_ROUTE("DSP_RX6"), + CS35L45_DSP_MUX_ROUTE("DSP_RX7"), + CS35L45_DSP_MUX_ROUTE("DSP_RX8"), + + {"DSP1", NULL, "DSP_RX1 Source"}, + {"DSP1", NULL, "DSP_RX2 Source"}, + {"DSP1", NULL, "DSP_RX3 Source"}, + {"DSP1", NULL, "DSP_RX4 Source"}, + {"DSP1", NULL, "DSP_RX5 Source"}, + {"DSP1", NULL, "DSP_RX6 Source"}, + {"DSP1", NULL, "DSP_RX7 Source"}, + {"DSP1", NULL, "DSP_RX8 Source"}, + + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + CS35L45_DAC_MUX_ROUTE("DACPCM1"), { "SPK", NULL, "AMP"}, @@ -219,6 +440,8 @@ static const struct snd_kcontrol_new cs35l45_controls[] = { -409, 48, (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1, 0, cs35l45_dig_pcm_vol_tlv), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), }; static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq) @@ -489,7 +712,24 @@ static struct snd_soc_dai_driver cs35l45_dai[] = { }, }; +static int cs35l45_component_probe(struct snd_soc_component *component) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + return wm_adsp2_component_probe(&cs35l45->dsp, component); +} + +static void cs35l45_component_remove(struct snd_soc_component *component) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l45->dsp, component); +} + static const struct snd_soc_component_driver cs35l45_component = { + .probe = cs35l45_component_probe, + .remove = cs35l45_component_remove, + .dapm_widgets = cs35l45_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets), @@ -607,6 +847,60 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45) return 0; } +static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45, + const unsigned int cmd, + unsigned int data) +{ + static char *speak_status = "Unknown"; + + switch (cmd) { + case EVENT_SPEAKER_STATUS: + switch (data) { + case 1: + speak_status = "All Clear"; + break; + case 2: + speak_status = "Open Circuit"; + break; + case 4: + speak_status = "Short Circuit"; + break; + } + + dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n", + speak_status); + break; + case EVENT_BOOT_DONE: + dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n"); + break; + default: + dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd); + return -EINVAL; + } + + return 0; +} + +static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data) +{ + struct cs35l45_private *cs35l45 = data; + unsigned int mbox_val; + int ret = 0; + + ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val); + if (!ret && mbox_val) + ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK, + (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT); + + /* Handle DSP trace log IRQ */ + ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val); + if (!ret && mbox_val != 0) { + dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n"); + } + + return IRQ_RETVAL(ret); +} + static irqreturn_t cs35l45_pll_unlock(int irq, void *data) { struct cs35l45_private *cs35l45 = data; @@ -639,6 +933,7 @@ static const struct cs35l45_irq cs35l45_irqs[] = { CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err), CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock), CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock), + CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb), }; static irqreturn_t cs35l45_spk_safe_err(int irq, void *data) @@ -665,6 +960,7 @@ static const struct regmap_irq cs35l45_reg_irqs[] = { CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE), CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE), CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG), + CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX), }; static const struct regmap_irq_chip cs35l45_regmap_irq_chip = { @@ -724,14 +1020,63 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45) if (ret < 0) return ret; - pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); - pm_runtime_use_autosuspend(cs35l45->dev); - pm_runtime_set_active(cs35l45->dev); - pm_runtime_enable(cs35l45->dev); - return 0; } +static const struct reg_sequence cs35l45_fs_errata_patch[] = { + {0x02B80080, 0x00000001}, + {0x02B80088, 0x00000001}, + {0x02B80090, 0x00000001}, + {0x02B80098, 0x00000001}, + {0x02B800A0, 0x00000001}, + {0x02B800A8, 0x00000001}, + {0x02B800B0, 0x00000001}, + {0x02B800B8, 0x00000001}, + {0x02B80280, 0x00000001}, + {0x02B80288, 0x00000001}, + {0x02B80290, 0x00000001}, + {0x02B80298, 0x00000001}, + {0x02B802A0, 0x00000001}, + {0x02B802A8, 0x00000001}, + {0x02B802B0, 0x00000001}, + {0x02B802B8, 0x00000001}, +}; + +static const struct cs_dsp_region cs35l45_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L45_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L45_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L45_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L45_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L45_DSP1_YMEM_UNPACK24_0}, +}; + +static int cs35l45_dsp_init(struct cs35l45_private *cs35l45) +{ + struct wm_adsp *dsp = &cs35l45->dsp; + int ret; + + dsp->part = "cs35l45"; + dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->cs_dsp.dev = cs35l45->dev; + dsp->cs_dsp.regmap = cs35l45->regmap; + dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ; + dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID; + dsp->cs_dsp.mem = cs35l45_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions); + dsp->cs_dsp.lock_regions = 0xFFFFFFFF; + + ret = wm_halo_init(dsp); + + regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch, + ARRAY_SIZE(cs35l45_fs_errata_patch)); + + return ret; +} + int cs35l45_probe(struct cs35l45_private *cs35l45) { struct device *dev = cs35l45->dev; @@ -781,6 +1126,17 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) if (ret < 0) goto err_reset; + ret = cs35l45_dsp_init(cs35l45); + if (ret < 0) + goto err_reset; + + pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); + pm_runtime_use_autosuspend(cs35l45->dev); + pm_runtime_mark_last_busy(cs35l45->dev); + pm_runtime_set_active(cs35l45->dev); + pm_runtime_get_noresume(cs35l45->dev); + pm_runtime_enable(cs35l45->dev); + if (cs35l45->irq) { if (cs35l45->irq_invert) irq_pol |= IRQF_TRIGGER_HIGH; @@ -791,7 +1147,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) &cs35l45_regmap_irq_chip, &cs35l45->irq_data); if (ret) { dev_err(dev, "Failed to register IRQ chip: %d\n", ret); - goto err_reset; + goto err_dsp; } for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) { @@ -799,7 +1155,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) if (irq < 0) { dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name); ret = irq; - goto err_reset; + goto err_dsp; } ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler, @@ -807,7 +1163,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) if (ret) { dev_err(dev, "Failed to request IRQ %s: %d\n", cs35l45_irqs[i].name, ret); - goto err_reset; + goto err_dsp; } } } @@ -816,10 +1172,17 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) cs35l45_dai, ARRAY_SIZE(cs35l45_dai)); if (ret < 0) - goto err_reset; + goto err_dsp; + + pm_runtime_put_autosuspend(cs35l45->dev); return 0; +err_dsp: + pm_runtime_disable(cs35l45->dev); + pm_runtime_put_noidle(cs35l45->dev); + wm_adsp2_remove(&cs35l45->dsp); + err_reset: gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); err: @@ -832,9 +1195,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45); void cs35l45_remove(struct cs35l45_private *cs35l45) { + pm_runtime_get_sync(cs35l45->dev); pm_runtime_disable(cs35l45->dev); + wm_adsp2_remove(&cs35l45->dsp); gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); + + pm_runtime_put_noidle(cs35l45->dev); regulator_disable(cs35l45->vdd_a); /* VDD_BATT must be the last to power-off */ regulator_disable(cs35l45->vdd_batt); -- cgit v1.2.3