diff options
Diffstat (limited to 'sound/soc/codecs/cs35l41-lib.c')
| -rw-r--r-- | sound/soc/codecs/cs35l41-lib.c | 221 |
1 files changed, 200 insertions, 21 deletions
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 04be71435491..1702f26049d3 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -16,6 +16,8 @@ #include <sound/cs35l41.h> +#define CS35L41_FIRMWARE_OLD_VERSION 0x001C00 /* v0.28.0 */ + static const struct reg_default cs35l41_reg[] = { { CS35L41_PWR_CTRL1, 0x00000000 }, { CS35L41_PWR_CTRL2, 0x00000000 }, @@ -46,7 +48,7 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_DSP1_RX5_SRC, 0x00000020 }, { CS35L41_DSP1_RX6_SRC, 0x00000021 }, { CS35L41_DSP1_RX7_SRC, 0x0000003A }, - { CS35L41_DSP1_RX8_SRC, 0x00000001 }, + { CS35L41_DSP1_RX8_SRC, 0x0000003B }, { CS35L41_NGATE1_SRC, 0x00000008 }, { CS35L41_NGATE2_SRC, 0x00000009 }, { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, @@ -58,8 +60,8 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, - { CS35L41_GPIO1_CTRL1, 0xE1000001 }, - { CS35L41_GPIO2_CTRL1, 0xE1000001 }, + { CS35L41_GPIO1_CTRL1, 0x81000001 }, + { CS35L41_GPIO2_CTRL1, 0x81000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, @@ -74,6 +76,7 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) case CS35L41_FABID: case CS35L41_RELID: case CS35L41_OTPID: + case CS35L41_SFT_RESET: case CS35L41_TEST_KEY_CTL: case CS35L41_USER_KEY_CTL: case CS35L41_OTP_CTRL0: @@ -743,7 +746,7 @@ struct regmap_config cs35l41_regmap_i2c = { .volatile_reg = cs35l41_volatile_reg, .readable_reg = cs35l41_readable_reg, .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); @@ -760,7 +763,7 @@ struct regmap_config cs35l41_regmap_spi = { .volatile_reg = cs35l41_volatile_reg, .readable_reg = cs35l41_readable_reg, .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); @@ -933,8 +936,8 @@ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsign EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); int cs35l41_set_channels(struct device *dev, struct regmap *reg, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { unsigned int val, mask; int i; @@ -1080,28 +1083,32 @@ static const struct reg_sequence cs35l41_safe_to_reset[] = { { 0x00000040, 0x00000033 }, }; -static const struct reg_sequence cs35l41_active_to_safe[] = { +static const struct reg_sequence cs35l41_active_to_safe_start[] = { { 0x00000040, 0x00000055 }, { 0x00000040, 0x000000AA }, { 0x00007438, 0x00585941 }, { CS35L41_PWR_CTRL1, 0x00000000 }, - { 0x0000742C, 0x00000009, 3000 }, + { 0x0000742C, 0x00000009 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe_end[] = { { 0x00007438, 0x00580941 }, { 0x00000040, 0x000000CC }, { 0x00000040, 0x00000033 }, }; -static const struct reg_sequence cs35l41_safe_to_active[] = { +static const struct reg_sequence cs35l41_safe_to_active_start[] = { { 0x00000040, 0x00000055 }, { 0x00000040, 0x000000AA }, { 0x0000742C, 0x0000000F }, { 0x0000742C, 0x00000079 }, { 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 }, { 0x00007438, 0x00580941 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, }; static const struct reg_sequence cs35l41_reset_to_safe[] = { @@ -1114,12 +1121,31 @@ static const struct reg_sequence cs35l41_reset_to_safe[] = { { 0x00000040, 0x00000033 }, }; +static const struct reg_sequence cs35l41_actv_seq[] = { + /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */ + {CS35L41_MDSYNC_EN, 0x00003000}, + /* BST_CTL_SEL = MDSYNC */ + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, +}; + +static const struct reg_sequence cs35l41_pass_seq[] = { + /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */ + {CS35L41_MDSYNC_EN, 0x00001000}, + /* BST_EN = 0 */ + {CS35L41_PWR_CTRL2, 0x00003300}, + /* BST_CTL_SEL = MDSYNC */ + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, +}; + int cs35l41_init_boost(struct device *dev, struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg) { int ret; switch (hw_cfg->bst_type) { + case CS35L41_SHD_BOOST_ACTV: + regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq)); + fallthrough; case CS35L41_INT_BOOST: ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk); @@ -1138,6 +1164,10 @@ int cs35l41_init_boost(struct device *dev, struct regmap *regmap, ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT); break; + case CS35L41_SHD_BOOST_PASS: + ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq, + ARRAY_SIZE(cs35l41_pass_seq)); + break; default: dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type); ret = -EINVAL; @@ -1165,24 +1195,155 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type) } EXPORT_SYMBOL_GPL(cs35l41_safe_reset); -int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable) +/* + * Enabling the CS35L41_SHD_BOOST_ACTV and CS35L41_SHD_BOOST_PASS shared boosts + * does also require a call to cs35l41_mdsync_up(), but not before getting the + * PLL Lock signal. + * + * PLL Lock seems to be triggered soon after snd_pcm_start() is executed and + * SNDRV_PCM_TRIGGER_START command is processed, which happens (long) after the + * SND_SOC_DAPM_PRE_PMU event handler is invoked as part of snd_pcm_prepare(). + * + * This event handler is where cs35l41_global_enable() is normally called from, + * but waiting for PLL Lock here will time out. Increasing the wait duration + * will not help, as the only consequence of it would be to add an unnecessary + * delay in the invocation of snd_pcm_start(). + * + * Trying to move the wait in the SNDRV_PCM_TRIGGER_START callback is not a + * solution either, as the trigger is executed in an IRQ-off atomic context. + * + * The current approach is to invoke cs35l41_mdsync_up() right after receiving + * the PLL Lock interrupt, in the IRQ handler. + */ +int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type, + int enable, struct cs_dsp *dsp) { int ret; + unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask; + unsigned int pwr_ctl1_val; + struct reg_sequence cs35l41_mdsync_down_seq[] = { + {CS35L41_PWR_CTRL3, 0}, + {CS35L41_GPIO_PAD_CONTROL, 0}, + {CS35L41_PWR_CTRL1, 0, 3000}, + }; + + pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK; + + ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val); + if (ret) + return ret; + + 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) { + case CS35L41_SHD_BOOST_ACTV: + case CS35L41_SHD_BOOST_PASS: + regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3); + regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control); + + pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK; + pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT; + + gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ; + gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT; + + pad_control &= ~CS35L41_GPIO1_CTRL_MASK; + pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK; + + cs35l41_mdsync_down_seq[0].def = pwr_ctrl3; + cs35l41_mdsync_down_seq[1].def = pad_control; + cs35l41_mdsync_down_seq[2].def = pwr_ctrl1; + + ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq, + ARRAY_SIZE(cs35l41_mdsync_down_seq)); + /* Activation to be completed later via cs35l41_mdsync_up() */ + if (ret || enable) + return ret; + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + /* Clear PUP/PDN status */ + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); + break; case CS35L41_INT_BOOST: ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, enable << CS35L41_GLOBAL_EN_SHIFT); - usleep_range(3000, 3100); + if (ret) { + dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret); + return ret; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + /* Clear PUP/PDN status */ + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); break; case CS35L41_EXT_BOOST: case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: - if (enable) - ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active, - ARRAY_SIZE(cs35l41_safe_to_active)); - else - ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe, - ARRAY_SIZE(cs35l41_active_to_safe)); + if (enable) { + /* Test Key is unlocked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start, + ARRAY_SIZE(cs35l41_safe_to_active_start)); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PUP_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK); + + if (dsp->running && dsp->fw_id_version > CS35L41_FIRMWARE_OLD_VERSION) + ret = cs35l41_set_cspl_mbox_cmd(dev, regmap, + CSPL_MBOX_CMD_SPK_OUT_ENABLE); + else + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk, + 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; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PDN_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK); + + /* Test Key is locked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end, + ARRAY_SIZE(cs35l41_active_to_safe_end)); + } break; default: ret = -EINVAL; @@ -1193,6 +1354,17 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, } EXPORT_SYMBOL_GPL(cs35l41_global_enable); +/* + * To be called after receiving the IRQ Lock interrupt, in order to complete + * any shared boost activation initiated by cs35l41_global_enable(). + */ +int cs35l41_mdsync_up(struct regmap *regmap) +{ + return regmap_update_bits(regmap, CS35L41_PWR_CTRL3, + CS35L41_SYNC_EN_MASK, CS35L41_SYNC_EN_MASK); +} +EXPORT_SYMBOL_GPL(cs35l41_mdsync_up); + int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg) { struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1; @@ -1273,6 +1445,8 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, return (sts == CSPL_MBOX_STS_RUNNING); case CSPL_MBOX_CMD_STOP_PRE_REINIT: return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + case CSPL_MBOX_CMD_SPK_OUT_ENABLE: + return (sts == CSPL_MBOX_STS_RUNNING); default: return false; } @@ -1302,6 +1476,11 @@ int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, continue; } + if (sts == CSPL_MBOX_STS_ERROR || sts == CSPL_MBOX_STS_ERROR2) { + dev_err(dev, "CSPL Error Detected\n"); + return -EINVAL; + } + if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) dev_dbg(dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts); else |
