Commit fa3efcc3 authored by Stefan Binding's avatar Stefan Binding Committed by Takashi Iwai

ALSA: cs35l41: Use mbox command to enable speaker output for external boost

To enable the speaker output in external boost mode, 2 registers must
be set, one after another. The longer the time between the writes of
the two registers, the more likely, and more loudly a pop may occur.
To minimize this, an mbox command can be used to allow the firmware
to perform this action, minimizing any delay between write, thus
minimizing any pop or click as a result. The old method will remain
when running without firmware.
Acked-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarStefan Binding <sbinding@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20230721151816.2080453-2-sbinding@opensource.cirrus.comSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 6d37a07f
...@@ -829,6 +829,7 @@ enum cs35l41_cspl_mbox_cmd { ...@@ -829,6 +829,7 @@ enum cs35l41_cspl_mbox_cmd {
CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
CSPL_MBOX_CMD_HIBERNATE = 5, CSPL_MBOX_CMD_HIBERNATE = 5,
CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6, CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
CSPL_MBOX_CMD_SPK_OUT_ENABLE = 7,
CSPL_MBOX_CMD_UNKNOWN_CMD = -1, CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
}; };
...@@ -901,7 +902,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap); ...@@ -901,7 +902,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap);
int cs35l41_init_boost(struct device *dev, struct regmap *regmap, int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
struct cs35l41_hw_cfg *hw_cfg); struct cs35l41_hw_cfg *hw_cfg);
bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type); bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type);
int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
struct completion *pll_lock); int enable, struct completion *pll_lock, bool firmware_running);
#endif /* __CS35L41_H */ #endif /* __CS35L41_H */
...@@ -514,13 +514,15 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action) ...@@ -514,13 +514,15 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
break; break;
case HDA_GEN_PCM_ACT_PREPARE: case HDA_GEN_PCM_ACT_PREPARE:
mutex_lock(&cs35l41->fw_mutex); mutex_lock(&cs35l41->fw_mutex);
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL); ret = cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1, NULL,
cs35l41->firmware_running);
mutex_unlock(&cs35l41->fw_mutex); mutex_unlock(&cs35l41->fw_mutex);
break; break;
case HDA_GEN_PCM_ACT_CLEANUP: case HDA_GEN_PCM_ACT_CLEANUP:
mutex_lock(&cs35l41->fw_mutex); mutex_lock(&cs35l41->fw_mutex);
regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute)); regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL); ret = cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0, NULL,
cs35l41->firmware_running);
mutex_unlock(&cs35l41->fw_mutex); mutex_unlock(&cs35l41->fw_mutex);
break; break;
case HDA_GEN_PCM_ACT_CLOSE: case HDA_GEN_PCM_ACT_CLOSE:
...@@ -672,7 +674,8 @@ static int cs35l41_runtime_suspend(struct device *dev) ...@@ -672,7 +674,8 @@ static int cs35l41_runtime_suspend(struct device *dev)
if (cs35l41->playback_started) { if (cs35l41->playback_started) {
regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute, regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
ARRAY_SIZE(cs35l41_hda_mute)); ARRAY_SIZE(cs35l41_hda_mute));
cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL); cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
NULL, cs35l41->firmware_running);
regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
......
...@@ -1080,28 +1080,32 @@ static const struct reg_sequence cs35l41_safe_to_reset[] = { ...@@ -1080,28 +1080,32 @@ static const struct reg_sequence cs35l41_safe_to_reset[] = {
{ 0x00000040, 0x00000033 }, { 0x00000040, 0x00000033 },
}; };
static const struct reg_sequence cs35l41_active_to_safe[] = { static const struct reg_sequence cs35l41_active_to_safe_start[] = {
{ 0x00000040, 0x00000055 }, { 0x00000040, 0x00000055 },
{ 0x00000040, 0x000000AA }, { 0x00000040, 0x000000AA },
{ 0x00007438, 0x00585941 }, { 0x00007438, 0x00585941 },
{ CS35L41_PWR_CTRL1, 0x00000000 }, { CS35L41_PWR_CTRL1, 0x00000000 },
{ 0x0000742C, 0x00000009, 3000 }, { 0x0000742C, 0x00000009 },
};
static const struct reg_sequence cs35l41_active_to_safe_end[] = {
{ 0x00007438, 0x00580941 }, { 0x00007438, 0x00580941 },
{ 0x00000040, 0x000000CC }, { 0x00000040, 0x000000CC },
{ 0x00000040, 0x00000033 }, { 0x00000040, 0x00000033 },
}; };
static const struct reg_sequence cs35l41_safe_to_active[] = { static const struct reg_sequence cs35l41_safe_to_active_start[] = {
{ 0x00000040, 0x00000055 }, { 0x00000040, 0x00000055 },
{ 0x00000040, 0x000000AA }, { 0x00000040, 0x000000AA },
{ 0x0000742C, 0x0000000F }, { 0x0000742C, 0x0000000F },
{ 0x0000742C, 0x00000079 }, { 0x0000742C, 0x00000079 },
{ 0x00007438, 0x00585941 }, { 0x00007438, 0x00585941 },
{ CS35L41_PWR_CTRL1, 0x00000001, 3000 }, // GLOBAL_EN = 1 { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1
};
static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = {
{ 0x0000742C, 0x000000F9 }, { 0x0000742C, 0x000000F9 },
{ 0x00007438, 0x00580941 }, { 0x00007438, 0x00580941 },
{ 0x00000040, 0x000000CC },
{ 0x00000040, 0x00000033 },
}; };
static const struct reg_sequence cs35l41_reset_to_safe[] = { static const struct reg_sequence cs35l41_reset_to_safe[] = {
...@@ -1188,11 +1192,11 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type) ...@@ -1188,11 +1192,11 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
} }
EXPORT_SYMBOL_GPL(cs35l41_safe_reset); EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
struct completion *pll_lock) int enable, struct completion *pll_lock, bool firmware_running)
{ {
int ret; int ret;
unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3; unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status;
struct reg_sequence cs35l41_mdsync_down_seq[] = { struct reg_sequence cs35l41_mdsync_down_seq[] = {
{CS35L41_PWR_CTRL3, 0}, {CS35L41_PWR_CTRL3, 0},
{CS35L41_GPIO_PAD_CONTROL, 0}, {CS35L41_GPIO_PAD_CONTROL, 0},
...@@ -1204,6 +1208,14 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, ...@@ -1204,6 +1208,14 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type,
{CS35L41_PWR_CTRL1, 0x00000001, 3000}, {CS35L41_PWR_CTRL1, 0x00000001, 3000},
}; };
if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) {
dev_dbg(dev, "Cannot set Global Enable - already set.\n");
return 0;
} else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) {
dev_dbg(dev, "Cannot unset Global Enable - not set.\n");
return 0;
}
switch (b_type) { switch (b_type) {
case CS35L41_SHD_BOOST_ACTV: case CS35L41_SHD_BOOST_ACTV:
case CS35L41_SHD_BOOST_PASS: case CS35L41_SHD_BOOST_PASS:
...@@ -1244,16 +1256,48 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, ...@@ -1244,16 +1256,48 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type,
case CS35L41_INT_BOOST: case CS35L41_INT_BOOST:
ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
enable << CS35L41_GLOBAL_EN_SHIFT); enable << CS35L41_GLOBAL_EN_SHIFT);
if (ret) {
dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret);
return ret;
}
usleep_range(3000, 3100); usleep_range(3000, 3100);
break; break;
case CS35L41_EXT_BOOST: case CS35L41_EXT_BOOST:
case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
if (enable) if (enable) {
ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active, /* Test Key is unlocked here */
ARRAY_SIZE(cs35l41_safe_to_active)); ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start,
ARRAY_SIZE(cs35l41_safe_to_active_start));
if (ret)
return ret;
usleep_range(3000, 3100);
if (firmware_running)
ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
CSPL_MBOX_CMD_SPK_OUT_ENABLE);
else else
ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe, ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk,
ARRAY_SIZE(cs35l41_active_to_safe)); ARRAY_SIZE(cs35l41_safe_to_active_en_spk));
/* Lock the test key, it was unlocked during the multi_reg_write */
cs35l41_test_key_lock(dev, regmap);
} else {
/* Test Key is unlocked here */
ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start,
ARRAY_SIZE(cs35l41_active_to_safe_start));
if (ret) {
/* Lock the test key, it was unlocked during the multi_reg_write */
cs35l41_test_key_lock(dev, regmap);
return ret;
}
usleep_range(3000, 3100);
/* Test Key is locked here */
ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end,
ARRAY_SIZE(cs35l41_active_to_safe_end));
}
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
...@@ -1344,6 +1388,8 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, ...@@ -1344,6 +1388,8 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
return (sts == CSPL_MBOX_STS_RUNNING); return (sts == CSPL_MBOX_STS_RUNNING);
case CSPL_MBOX_CMD_STOP_PRE_REINIT: case CSPL_MBOX_CMD_STOP_PRE_REINIT:
return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
case CSPL_MBOX_CMD_SPK_OUT_ENABLE:
return (sts == CSPL_MBOX_STS_RUNNING);
default: default:
return false; return false;
} }
......
...@@ -500,12 +500,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, ...@@ -500,12 +500,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
cs35l41_pup_patch, cs35l41_pup_patch,
ARRAY_SIZE(cs35l41_pup_patch)); ARRAY_SIZE(cs35l41_pup_patch));
cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1, ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
&cs35l41->pll_lock); 1, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running);
break; break;
case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_POST_PMD:
cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
&cs35l41->pll_lock); 0, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running);
ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1, ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
val, val & CS35L41_PDN_DONE_MASK, val, val & CS35L41_PDN_DONE_MASK,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment