Support for HALO DSP and firmware Signed-off-by: David Rhodes <drhodes@xxxxxxxxxxxxxxxxxxxxx> --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/cs35l41-tables.c | 96 +++++++++ sound/soc/codecs/cs35l41.c | 312 +++++++++++++++++++++++++++++- sound/soc/codecs/cs35l41.h | 20 ++ 4 files changed, 426 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 326f2d611ad4..3fe62df32238 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -343,11 +343,15 @@ config SND_SOC_WM_ADSP default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y + default y if SND_SOC_CS35L41_SPI=y + default y if SND_SOC_CS35L41_I2C=y default m if SND_SOC_MADERA=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m + default m if SND_SOC_CS35L41_SPI=m + default m if SND_SOC_CS35L41_I2C=m config SND_SOC_AB8500_CODEC tristate diff --git a/sound/soc/codecs/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c index 964e530afa27..9d1a7d7dd24d 100644 --- a/sound/soc/codecs/cs35l41-tables.c +++ b/sound/soc/codecs/cs35l41-tables.c @@ -200,6 +200,83 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_DIE_STS2: case CS35L41_TEMP_CAL1: case CS35L41_TEMP_CAL2: + case CS35L41_DSP1_TIMESTAMP_COUNT: + case CS35L41_DSP1_SYS_ID: + case CS35L41_DSP1_SYS_VERSION: + case CS35L41_DSP1_SYS_CORE_ID: + case CS35L41_DSP1_SYS_AHB_ADDR: + case CS35L41_DSP1_SYS_XSRAM_SIZE: + case CS35L41_DSP1_SYS_YSRAM_SIZE: + case CS35L41_DSP1_SYS_PSRAM_SIZE: + case CS35L41_DSP1_SYS_PM_BOOT_SIZE: + case CS35L41_DSP1_SYS_FEATURES: + case CS35L41_DSP1_SYS_FIR_FILTERS: + case CS35L41_DSP1_SYS_LMS_FILTERS: + case CS35L41_DSP1_SYS_XM_BANK_SIZE: + case CS35L41_DSP1_SYS_YM_BANK_SIZE: + case CS35L41_DSP1_SYS_PM_BANK_SIZE: + case CS35L41_DSP1_RX1_RATE: + case CS35L41_DSP1_RX2_RATE: + case CS35L41_DSP1_RX3_RATE: + case CS35L41_DSP1_RX4_RATE: + case CS35L41_DSP1_RX5_RATE: + case CS35L41_DSP1_RX6_RATE: + case CS35L41_DSP1_RX7_RATE: + case CS35L41_DSP1_RX8_RATE: + case CS35L41_DSP1_TX1_RATE: + case CS35L41_DSP1_TX2_RATE: + case CS35L41_DSP1_TX3_RATE: + case CS35L41_DSP1_TX4_RATE: + case CS35L41_DSP1_TX5_RATE: + case CS35L41_DSP1_TX6_RATE: + case CS35L41_DSP1_TX7_RATE: + case CS35L41_DSP1_TX8_RATE: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CORE_CTRL: + case CS35L41_DSP1_CCM_CLK_OVERRIDE: + case CS35L41_DSP1_XM_MSTR_EN: + case CS35L41_DSP1_XM_CORE_PRI: + case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_XM_AHB_UP_PL_PRI: + case CS35L41_DSP1_XM_ACCEL_PL0_PRI: + case CS35L41_DSP1_XM_NPL0_PRI: + case CS35L41_DSP1_YM_MSTR_EN: + case CS35L41_DSP1_YM_CORE_PRI: + case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_YM_AHB_UP_PL_PRI: + case CS35L41_DSP1_YM_ACCEL_PL0_PRI: + case CS35L41_DSP1_YM_NPL0_PRI: + case CS35L41_DSP1_MPU_XM_ACCESS0: + case CS35L41_DSP1_MPU_YM_ACCESS0: + case CS35L41_DSP1_MPU_WNDW_ACCESS0: + case CS35L41_DSP1_MPU_XREG_ACCESS0: + case CS35L41_DSP1_MPU_YREG_ACCESS0: + case CS35L41_DSP1_MPU_XM_ACCESS1: + case CS35L41_DSP1_MPU_YM_ACCESS1: + case CS35L41_DSP1_MPU_WNDW_ACCESS1: + case CS35L41_DSP1_MPU_XREG_ACCESS1: + case CS35L41_DSP1_MPU_YREG_ACCESS1: + case CS35L41_DSP1_MPU_XM_ACCESS2: + case CS35L41_DSP1_MPU_YM_ACCESS2: + case CS35L41_DSP1_MPU_WNDW_ACCESS2: + case CS35L41_DSP1_MPU_XREG_ACCESS2: + case CS35L41_DSP1_MPU_YREG_ACCESS2: + case CS35L41_DSP1_MPU_XM_ACCESS3: + case CS35L41_DSP1_MPU_YM_ACCESS3: + case CS35L41_DSP1_MPU_WNDW_ACCESS3: + case CS35L41_DSP1_MPU_XREG_ACCESS3: + case CS35L41_DSP1_MPU_YREG_ACCESS3: + case CS35L41_DSP1_MPU_XM_VIO_ADDR: + case CS35L41_DSP1_MPU_XM_VIO_STATUS: + case CS35L41_DSP1_MPU_YM_VIO_ADDR: + case CS35L41_DSP1_MPU_YM_VIO_STATUS: + case CS35L41_DSP1_MPU_PM_VIO_ADDR: + case CS35L41_DSP1_MPU_PM_VIO_STATUS: + case CS35L41_DSP1_MPU_LOCK_CONFIG: + case CS35L41_DSP1_MPU_WDT_RST_CTRL: case CS35L41_OTP_TRIM_1: case CS35L41_OTP_TRIM_2: case CS35L41_OTP_TRIM_3: @@ -237,6 +314,13 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_OTP_TRIM_35: case CS35L41_OTP_TRIM_36: case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + 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: /*test regs*/ case CS35L41_PLL_OVR: case CS35L41_BST_TEST_DUTY: @@ -251,6 +335,9 @@ bool cs35l41_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + 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: return true; default: return false; @@ -342,6 +429,15 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) 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: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + 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_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 9d0530dde996..afb07d2991ba 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -197,6 +197,122 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L41_AMP_DIG_VOL_CTRL, 0, cs35l41_pcm_sftramp_text); +static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + 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; + + return wm_adsp_event(w, kcontrol, event); + default: + return 0; + } +} + +static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, + enum cs35l41_cspl_mbox_status sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + case CSPL_MBOX_CMD_UNKNOWN_CMD: + return true; + case CSPL_MBOX_CMD_PAUSE: + 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 cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41, + enum cs35l41_cspl_mbox_cmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + // 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); + 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(cs35l41->regmap, CS35L41_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) { + dev_dbg(cs35l41->dev, + "[%u] cmd %u returned invalid sts %u", + i, cmd, sts); + } else { + return 0; + } + } + + dev_err(cs35l41->dev, + "Failed to set mailbox cmd %u (status %u)\n", + cmd, sts); + + return -ENOMSG; +} + +static int cs35l41_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 cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + unsigned int fw_status; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l41->dsp.cs_dsp.running) + return wm_adsp_event(w, kcontrol, event); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status); + if (ret < 0) { + dev_err(cs35l41->dev, + "Failed to read firmware status: %d\n", ret); + return ret; + } + + switch (fw_status) { + case CSPL_MBOX_STS_RUNNING: + case CSPL_MBOX_STS_PAUSED: + break; + default: + dev_err(cs35l41->dev, "Firmware status is invalid: %u\n", + fw_status); + return -EINVAL; + } + + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l41_set_cspl_mbox_cmd(cs35l41, CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } +} + static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"}; static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, @@ -255,6 +371,24 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, static const struct snd_kcontrol_new asp_tx4_mux = SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum); +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum, + CS35L41_DSP1_RX1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx1_mux = + SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, + CS35L41_DSP1_RX2_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx2_mux = + SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum); + static const struct snd_kcontrol_new cs35l41_aud_controls[] = { SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL, 3, 0x4CF, 0x391, dig_vol_tlv), @@ -282,6 +416,8 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { CS35L41_AMP_INV_PCM_SHIFT, 1, 0), SOC_SINGLE("Amp Gain ZC", CS35L41_AMP_GAIN_CTRL, CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), }; static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) @@ -603,6 +739,14 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l41_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l41_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("SPK"), SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0), @@ -618,11 +762,18 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_SIGGEN("VBST"), SND_SOC_DAPM_SIGGEN("TEMP"), - SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L41_PWR_CTRL2, 12, 0), - SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L41_PWR_CTRL2, 13, 0), - SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L41_PWR_CTRL2, 8, 0), - SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L41_PWR_CTRL2, 9, 0), - SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, CS35L41_PWR_CTRL2, 10, 0), + SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0), SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0, @@ -633,33 +784,51 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux), + SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux), + SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux), SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl), }; static const struct snd_soc_dapm_route cs35l41_audio_map[] = { + {"DSP RX1 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX1 Source", "ASPRX2", "ASPRX2"}, + {"DSP RX2 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX2 Source", "ASPRX2", "ASPRX2"}, + + {"DSP1", NULL, "DSP RX1 Source"}, + {"DSP1", NULL, "DSP RX2 Source"}, + {"ASP TX1 Source", "VMON", "VMON ADC"}, {"ASP TX1 Source", "IMON", "IMON ADC"}, {"ASP TX1 Source", "VPMON", "VPMON ADC"}, {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX1 Source", "DSPTX1", "DSP1"}, + {"ASP TX1 Source", "DSPTX2", "DSP1"}, {"ASP TX1 Source", "ASPRX1", "ASPRX1" }, {"ASP TX1 Source", "ASPRX2", "ASPRX2" }, {"ASP TX2 Source", "VMON", "VMON ADC"}, {"ASP TX2 Source", "IMON", "IMON ADC"}, {"ASP TX2 Source", "VPMON", "VPMON ADC"}, {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX2 Source", "DSPTX1", "DSP1"}, + {"ASP TX2 Source", "DSPTX2", "DSP1"}, {"ASP TX2 Source", "ASPRX1", "ASPRX1" }, {"ASP TX2 Source", "ASPRX2", "ASPRX2" }, {"ASP TX3 Source", "VMON", "VMON ADC"}, {"ASP TX3 Source", "IMON", "IMON ADC"}, {"ASP TX3 Source", "VPMON", "VPMON ADC"}, {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX3 Source", "DSPTX1", "DSP1"}, + {"ASP TX3 Source", "DSPTX2", "DSP1"}, {"ASP TX3 Source", "ASPRX1", "ASPRX1" }, {"ASP TX3 Source", "ASPRX2", "ASPRX2" }, {"ASP TX4 Source", "VMON", "VMON ADC"}, {"ASP TX4 Source", "IMON", "IMON ADC"}, {"ASP TX4 Source", "VPMON", "VPMON ADC"}, {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX4 Source", "DSPTX1", "DSP1"}, + {"ASP TX4 Source", "DSPTX2", "DSP1"}, {"ASP TX4 Source", "ASPRX1", "ASPRX1" }, {"ASP TX4 Source", "ASPRX2", "ASPRX2" }, {"ASPTX1", NULL, "ASP TX1 Source"}, @@ -671,12 +840,27 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"AMP Capture", NULL, "ASPTX3"}, {"AMP Capture", NULL, "ASPTX4"}, + {"DSP1", NULL, "VMON"}, + {"DSP1", NULL, "IMON"}, + {"DSP1", NULL, "VPMON"}, + {"DSP1", NULL, "VBSTMON"}, + {"DSP1", NULL, "TEMPMON"}, + + {"VMON ADC", NULL, "VMON"}, + {"IMON ADC", NULL, "IMON"}, + {"VPMON ADC", NULL, "VPMON"}, + {"VBSTMON ADC", NULL, "VBSTMON"}, + {"TEMPMON ADC", NULL, "TEMPMON"}, + {"VMON ADC", NULL, "VSENSE"}, {"IMON ADC", NULL, "ISENSE"}, {"VPMON ADC", NULL, "VP"}, {"VBSTMON ADC", NULL, "VBST"}, {"TEMPMON ADC", NULL, "TEMP"}, + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + {"ASPRX1", NULL, "AMP Playback"}, {"ASPRX2", NULL, "AMP Playback"}, {"DRE", "Switch", "CLASS H"}, @@ -685,9 +869,18 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"SPK", NULL, "Main AMP"}, {"PCM Source", "ASP", "ASPRX1"}, + {"PCM Source", "DSP", "DSP1"}, {"CLASS H", NULL, "PCM Source"}, }; +static const struct cs_dsp_region cs35l41_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0}, +}; + static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) @@ -1098,6 +1291,20 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) return irq_pol; } +static int cs35l41_component_probe(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + return wm_adsp2_component_probe(&cs35l41->dsp, component); +} + +static void cs35l41_component_remove(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l41->dsp, component); +} + static const struct snd_soc_dai_ops cs35l41_ops = { .startup = cs35l41_pcm_startup, .set_fmt = cs35l41_set_dai_fmt, @@ -1131,6 +1338,8 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { .name = "cs35l41-codec", + .probe = cs35l41_component_probe, + .remove = cs35l41_component_remove, .dapm_widgets = cs35l41_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), @@ -1237,6 +1446,90 @@ static const struct reg_sequence cs35l41_revb2_errata_patch[] = { { 0x00000040, 0x00003333 }, }; +static const struct reg_sequence cs35l41_fs_errata_patch[] = { + { CS35L41_DSP1_RX1_RATE, 0x00000001 }, + { CS35L41_DSP1_RX2_RATE, 0x00000001 }, + { CS35L41_DSP1_RX3_RATE, 0x00000001 }, + { CS35L41_DSP1_RX4_RATE, 0x00000001 }, + { CS35L41_DSP1_RX5_RATE, 0x00000001 }, + { CS35L41_DSP1_RX6_RATE, 0x00000001 }, + { CS35L41_DSP1_RX7_RATE, 0x00000001 }, + { CS35L41_DSP1_RX8_RATE, 0x00000001 }, + { CS35L41_DSP1_TX1_RATE, 0x00000001 }, + { CS35L41_DSP1_TX2_RATE, 0x00000001 }, + { CS35L41_DSP1_TX3_RATE, 0x00000001 }, + { CS35L41_DSP1_TX4_RATE, 0x00000001 }, + { CS35L41_DSP1_TX5_RATE, 0x00000001 }, + { CS35L41_DSP1_TX6_RATE, 0x00000001 }, + { CS35L41_DSP1_TX7_RATE, 0x00000001 }, + { CS35L41_DSP1_TX8_RATE, 0x00000001 }, +}; + +static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) +{ + struct wm_adsp *dsp; + int ret; + + dsp = &cs35l41->dsp; + dsp->part = "cs35l41"; + dsp->cs_dsp.num = 1; + 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->cs_dsp.dev = cs35l41->dev; + dsp->cs_dsp.regmap = cs35l41->regmap; + dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE; + dsp->cs_dsp.base_sysinfo = CS35L41_DSP1_SYS_ID; + dsp->cs_dsp.mem = cs35l41_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l41_dsp1_regions); + dsp->cs_dsp.lock_regions = 0xFFFFFFFF; + + ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_fs_errata_patch, + ARRAY_SIZE(cs35l41_fs_errata_patch)); + if (ret < 0) { + dev_err(cs35l41->dev, "Failed to write fs errata: %d\n", ret); + return ret; + } + + ret = wm_halo_init(dsp); + if (ret) { + dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret); + return ret; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, + CS35L41_INPUT_SRC_VPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, + CS35L41_INPUT_SRC_CLASSH); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, + CS35L41_INPUT_SRC_TEMPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, + CS35L41_INPUT_SRC_RSVD); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret); + goto err_dsp; + } + + return 0; + +err_dsp: + wm_adsp2_remove(dsp); + + return ret; +} + int cs35l41_probe(struct cs35l41_private *cs35l41, struct cs35l41_platform_data *pdata) { @@ -1413,12 +1706,16 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, goto err; } + ret = cs35l41_dsp_init(cs35l41); + if (ret < 0) + goto err; + 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; + goto err_dsp; } dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", @@ -1426,6 +1723,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, return 0; +err_dsp: + wm_adsp2_remove(&cs35l41->dsp); err: regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); @@ -1436,6 +1735,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, void cs35l41_remove(struct cs35l41_private *cs35l41) { regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + wm_adsp2_remove(&cs35l41->dsp); regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); } diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 6cffe8a55beb..eea3b14acb0b 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -13,9 +13,12 @@ #include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/firmware.h> #include <sound/core.h> #include <sound/cs35l41.h> +#include "wm_adsp.h" + #define CS35L41_FIRSTREG 0x00000000 #define CS35L41_LASTREG 0x03804FE8 #define CS35L41_DEVID 0x00000000 @@ -755,7 +758,24 @@ extern const struct cs35l41_otp_map_element_t #define CS35L41_REGSTRIDE 4 +enum cs35l41_cspl_mbox_status { + CSPL_MBOX_STS_RUNNING = 0, + CSPL_MBOX_STS_PAUSED = 1, + CSPL_MBOX_STS_RDY_FOR_REINIT = 2, +}; + +enum cs35l41_cspl_mbox_cmd { + CSPL_MBOX_CMD_NONE = 0, + CSPL_MBOX_CMD_PAUSE = 1, + CSPL_MBOX_CMD_RESUME = 2, + CSPL_MBOX_CMD_REINIT = 3, + CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_UNKNOWN_CMD = -1, + CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, +}; + struct cs35l41_private { + struct wm_adsp dsp; /* needs to be first member */ struct snd_soc_codec *codec; struct cs35l41_platform_data pdata; struct device *dev; -- 2.25.1