summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/cs35l41.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/cs35l41.c')
-rw-r--r--sound/soc/codecs/cs35l41.c143
1 files changed, 75 insertions, 68 deletions
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index c223d83e02cf..9ec01a5f09ca 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -356,6 +356,30 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
WM_ADSP_FW_CONTROL("DSP1", 0),
};
+static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable)
+{
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF;
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ enable << CS35L41_BST_EN_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit,
+ unsigned int rel_err_bit)
+{
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit);
+ regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0);
+}
+
static irqreturn_t cs35l41_irq(int irq, void *data)
{
struct cs35l41_private *cs35l41 = data;
@@ -392,94 +416,49 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
*/
if (status[0] & CS35L41_AMP_SHORT_ERR) {
dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_AMP_SHORT_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_AMP_SHORT_ERR_RLS,
- CS35L41_AMP_SHORT_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_AMP_SHORT_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_TEMP_WARN) {
dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_TEMP_WARN);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_WARN_ERR_RLS,
- CS35L41_TEMP_WARN_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_WARN_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_TEMP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_TEMP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_ERR_RLS,
- CS35L41_TEMP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_OVP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_OVP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_OVP_ERR_RLS,
- CS35L41_BST_OVP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_OVP_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_DCM_UVP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_UVP_ERR_RLS,
- CS35L41_BST_UVP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_UVP_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_SHORT_ERR) {
dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_SHORT_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_SHORT_ERR_RLS,
- CS35L41_BST_SHORT_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_SHORT_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[2] & CS35L41_PLL_LOCK) {
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK);
+ complete(&cs35l41->pll_lock);
ret = IRQ_HANDLED;
}
@@ -520,10 +499,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
cs35l41_pup_patch,
ARRAY_SIZE(cs35l41_pup_patch));
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1,
+ &cs35l41->pll_lock);
break;
case SND_SOC_DAPM_POST_PMD:
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
+ &cs35l41->pll_lock);
ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
val, val & CS35L41_PDN_DONE_MASK,
@@ -830,6 +811,10 @@ static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+ reinit_completion(&cs35l41->pll_lock);
+
if (substream->runtime)
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -1037,9 +1022,21 @@ static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cf
unsigned int val;
int ret;
- ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
- if (ret >= 0)
- hw_cfg->bst_type = val;
+ /* Some ACPI systems received the Shared Boost feature before the upstream driver,
+ * leaving those systems with deprecated _DSD properties.
+ * To correctly configure those systems add shared-boost-active and shared-boost-passive
+ * properties mapped to the correct value in boost-type.
+ * These two are not DT properties and should not be used in new systems designs.
+ */
+ if (device_property_read_bool(dev, "cirrus,shared-boost-active")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV;
+ } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS;
+ } else {
+ ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+ if (ret >= 0)
+ hw_cfg->bst_type = val;
+ }
ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
if (ret >= 0)
@@ -1280,6 +1277,10 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
/* Set interrupt masks for critical errors */
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
CS35L41_INT1_MASK_DEFAULT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 0 << CS35L41_INT3_PLL_LOCK_SHIFT);
ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
IRQF_ONESHOT | IRQF_SHARED | irq_pol,
@@ -1303,6 +1304,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
if (ret < 0)
goto err;
+ init_completion(&cs35l41->pll_lock);
+
pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
pm_runtime_use_autosuspend(cs35l41->dev);
pm_runtime_mark_last_busy(cs35l41->dev);
@@ -1345,6 +1348,10 @@ void cs35l41_remove(struct cs35l41_private *cs35l41)
pm_runtime_disable(cs35l41->dev);
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 1 << CS35L41_INT3_PLL_LOCK_SHIFT);
kfree(cs35l41->dsp.system_name);
wm_adsp2_remove(&cs35l41->dsp);
cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);