diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index e6871dc873ea..b138d4ce778e 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -61,6 +61,7 @@ struct cs35l41_private { int extclk_freq; int extclk_cfg; int sclk; + unsigned int cspl_cmd; bool tdm_mode; bool i2s_mode; bool swire_mode; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 8d9b0cc2b567..9bcc629d4dda 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -216,6 +216,143 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L41_AMP_DIG_VOL_CTRL, 0, cs35l41_pcm_sftramp_text); +static const char * const cs35l41_cspl_cmd_text[] = { + "CSPL_MBOX_CMD_RESUME", + "CSPL_MBOX_CMD_REINIT", + "CSPL_MBOX_CMD_STOP_PRE_REINIT", +}; + +static const unsigned int cs35l41_cspl_cmd_val[] = { + (unsigned int)CSPL_MBOX_CMD_RESUME, + (unsigned int)CSPL_MBOX_CMD_REINIT, + (unsigned int)CSPL_MBOX_CMD_STOP_PRE_REINIT, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_cspl_cmd, SND_SOC_NOPM, 0, 0, + cs35l41_cspl_cmd_text, cs35l41_cspl_cmd_val); + +static bool cs35l41_is_csplmboxsts_correct(enum cs35l41_cspl_mboxcmd cmd, + enum cs35l41_cspl_mboxstate 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_csplmboxcmd(struct cs35l41_private *cs35l41, + enum cs35l41_cspl_mboxcmd cmd) +{ + int ret; + unsigned int sts; + + /* Reset DSP sticky bit */ + regmap_write(cs35l41->regmap, CS35L41_IRQ2_STATUS2, + 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT); + + /* Reset AP sticky bit */ + regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS2, + 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT); + + /* + * Set mailbox cmd + */ + reinit_completion(&cs35l41->mbox_cmd); + /* Unmask DSP INT */ + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ2_MASK2, + 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT, 0); + /* Unmask AP INT */ + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK2, + 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT, 0); + regmap_write(cs35l41->regmap, CS35L41_CSPL_MBOX_CMD_DRV, cmd); + ret = wait_for_completion_timeout(&cs35l41->mbox_cmd, + usecs_to_jiffies(CS35L41_MBOXWAIT)); + if (ret == 0) { + dev_err(cs35l41->dev, + "Timout waiting for DSP to set mbox cmd\n"); + ret = -ETIMEDOUT; + } + + /* Mask AP INT */ + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK2, + 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT, + 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT); + /* Mask DSP INT */ + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ2_MASK2, + 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT, + 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT); + + if (regmap_read(cs35l41->regmap, + CS35L41_CSPL_MBOX_STS, &sts) < 0) { + dev_err(cs35l41->dev, "Failed to read %u\n", + CS35L41_CSPL_MBOX_STS); + ret = -EACCES; + } + + if (!cs35l41_is_csplmboxsts_correct(cmd, + (enum cs35l41_cspl_mboxstate)sts)) { + dev_err(cs35l41->dev, + "Failed to set mailbox(cmd: %u, sts: %u)\n", cmd, sts); + ret = -ENOMSG; + } + + return ret; +} + +static int cs35l41_cspl_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *soc_enum; + unsigned int i = ucontrol->value.enumerated.item[0]; + int ret = 0; + + soc_enum = (struct soc_enum *)kcontrol->private_value; + cs35l41->cspl_cmd = soc_enum->values[i]; + + return ret; +} + +static int cs35l41_cspl_cmd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *soc_enum; + unsigned int i; + int ret = 0; + + soc_enum = (struct soc_enum *)kcontrol->private_value; + + for (i = 0; i < soc_enum->items; i++) { + if (cs35l41->cspl_cmd == soc_enum->values[i]) + break; + } + + if (i >= soc_enum->items) { + /* Cannot find value */ + dev_err(cs35l41->dev, "Cannot find cspl cmd\n"); + i = 0; + ret = -EINVAL; + } + + ucontrol->value.enumerated.item[0] = i; + + return ret; +} + 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, @@ -326,6 +463,8 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { SOC_SINGLE_RANGE("ASPTX4 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 24, 0, 7, 0), SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_VALUE_ENUM_EXT("CSPL Command", cs35l41_cspl_cmd, + cs35l41_cspl_cmd_get, cs35l41_cspl_cmd_put), SOC_SINGLE_EXT("DSP Booted", SND_SOC_NOPM, 0, 1, 0, cs35l41_halo_booted_get, cs35l41_halo_booted_put), WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), @@ -618,80 +757,6 @@ static const struct reg_sequence cs35l41_pdn_patch[] = { {0x00000040, 0x00000033}, }; -static bool cs35l41_is_csplmboxsts_correct(enum cspl_mboxcmd cmd, - enum cspl_mboxstate 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); - default: - return false; - } -} - -static int cs35l41_set_csplmboxcmd(struct cs35l41_private *cs35l41, - enum cspl_mboxcmd cmd) -{ - int ret; - unsigned int sts; - - /* Reset DSP sticky bit */ - regmap_write(cs35l41->regmap, CS35L41_IRQ2_STATUS2, - 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT); - - /* Reset AP sticky bit */ - regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS2, - 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT); - - /* - * Set mailbox cmd - */ - reinit_completion(&cs35l41->mbox_cmd); - /* Unmask DSP INT */ - regmap_update_bits(cs35l41->regmap, CS35L41_IRQ2_MASK2, - 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT, 0); - /* Unmask AP INT */ - regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK2, - 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT, 0); - regmap_write(cs35l41->regmap, CS35L41_CSPL_MBOX_CMD_DRV, cmd); - ret = wait_for_completion_timeout(&cs35l41->mbox_cmd, - usecs_to_jiffies(CS35L41_MBOXWAIT)); - if (ret == 0) { - dev_err(cs35l41->dev, - "Timout waiting for DSP to set mbox cmd\n"); - ret = -ETIMEDOUT; - } - - /* Mask AP INT */ - regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK2, - 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT, - 1 << CS35L41_CSPL_MBOX_CMD_FW_SHIFT); - /* Mask DSP INT */ - regmap_update_bits(cs35l41->regmap, CS35L41_IRQ2_MASK2, - 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT, - 1 << CS35L41_CSPL_MBOX_CMD_DRV_SHIFT); - - if (regmap_read(cs35l41->regmap, - CS35L41_CSPL_MBOX_STS, &sts) < 0) { - dev_err(cs35l41->dev, "Failed to read %u\n", - CS35L41_CSPL_MBOX_STS); - ret = -EACCES; - } - - if (!cs35l41_is_csplmboxsts_correct(cmd, (enum cspl_mboxstate)sts)) { - dev_err(cs35l41->dev, - "Failed to set mailbox(cmd: %u, sts: %u)\n", cmd, sts); - ret = -ENOMSG; - } - - return ret; -} - static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -711,14 +776,25 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, usleep_range(1000, 1100); - if (cs35l41->halo_booted) - ret = cs35l41_set_csplmboxcmd(cs35l41, - CSPL_MBOX_CMD_RESUME); + if (cs35l41->halo_booted) { + if (cs35l41->cspl_cmd == CSPL_MBOX_CMD_STOP_PRE_REINIT) + /* Send this command on power down event */ + ret = cs35l41_set_csplmboxcmd(cs35l41, + CSPL_MBOX_CMD_RESUME); + else + ret = cs35l41_set_csplmboxcmd(cs35l41, + cs35l41->cspl_cmd); + } break; case SND_SOC_DAPM_POST_PMD: - if (cs35l41->halo_booted) - ret = cs35l41_set_csplmboxcmd(cs35l41, - CSPL_MBOX_CMD_PAUSE); + if (cs35l41->halo_booted) { + if (cs35l41->cspl_cmd == CSPL_MBOX_CMD_STOP_PRE_REINIT) + ret = cs35l41_set_csplmboxcmd(cs35l41, + cs35l41->cspl_cmd); + else + ret = cs35l41_set_csplmboxcmd(cs35l41, + CSPL_MBOX_CMD_PAUSE); + } regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, 0); @@ -1669,6 +1745,9 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, int timeout = 100; int irq_pol = 0; + /* Default to RESUME cmd */ + cs35l41->cspl_cmd = (unsigned int)CSPL_MBOX_CMD_RESUME; + for (i = 0; i < ARRAY_SIZE(cs35l41_supplies); i++) cs35l41->supplies[i].supply = cs35l41_supplies[i]; diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h index 72c173c5e19c..9cce2801ce2e 100644 --- a/sound/soc/codecs/cs35l41.h +++ b/sound/soc/codecs/cs35l41.h @@ -741,16 +741,20 @@ extern const struct cs35l41_otp_map_element_t #define CS35L41_CSPL_MBOX_CMD_DRV CS35L41_DSP_VIRT1_MBOX_1 #define CS35L41_CSPL_MBOX_CMD_DRV_SHIFT CS35L41_DSP_VIRT1_MBOX_SHIFT -enum cspl_mboxstate { +enum cs35l41_cspl_mboxstate { CSPL_MBOX_STS_RUNNING = 0, CSPL_MBOX_STS_PAUSED = 1, + CSPL_MBOX_STS_RDY_FOR_REINIT = 2, }; -enum cspl_mboxcmd { +enum cs35l41_cspl_mboxcmd { 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, }; #endif /*__CS35L41_H__*/