Skip to content

Commit

Permalink
ALSA: cs35l41: Add shared boost feature
Browse files Browse the repository at this point in the history
Shared boost allows two amplifiers to share a single boost circuit by
communicating on the MDSYNC bus.
The passive amplifier does not control the boost and receives data from
the active amplifier.

Shared Boost is not supported in HDA Systems.
Based on David Rhodes shared boost patches.

Signed-off-by: Lucas Tanure <lucas.tanure@collabora.com>
  • Loading branch information
Lucas Tanure authored and intel-lab-lkp committed Feb 8, 2023
1 parent 2674a0d commit 87a5c35
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 7 deletions.
13 changes: 12 additions & 1 deletion include/sound/cs35l41.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define __CS35L41_H

#include <linux/regmap.h>
#include <linux/completion.h>
#include <linux/firmware/cirrus/cs_dsp.h>

#define CS35L41_FIRSTREG 0x00000000
Expand Down Expand Up @@ -677,6 +678,7 @@

#define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F
#define CS35L36_PUP_DONE_IRQ_MASK 0xBF
#define CS35L41_SYNC_EN_MASK BIT(8)

#define CS35L41_AMP_SHORT_ERR 0x80000000
#define CS35L41_BST_SHORT_ERR 0x0100
Expand All @@ -686,6 +688,7 @@
#define CS35L41_BST_DCM_UVP_ERR 0x80
#define CS35L41_OTP_BOOT_DONE 0x02
#define CS35L41_PLL_UNLOCK 0x10
#define CS35L41_PLL_LOCK BIT(1)
#define CS35L41_OTP_BOOT_ERR 0x80000000

#define CS35L41_AMP_SHORT_ERR_RLS 0x02
Expand All @@ -705,6 +708,8 @@
#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F
#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF
#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF
#define CS35L41_INT3_PLL_LOCK_SHIFT 1
#define CS35L41_INT3_PLL_LOCK_MASK BIT(CS35L41_INT3_PLL_LOCK_SHIFT)

#define CS35L41_GPIO_DIR_MASK 0x80000000
#define CS35L41_GPIO_DIR_SHIFT 31
Expand Down Expand Up @@ -742,6 +747,11 @@
enum cs35l41_boost_type {
CS35L41_INT_BOOST,
CS35L41_EXT_BOOST,
CS35L41_SHD_BOOST_ACTV,
CS35L41_SHD_BOOST_PASS,

// Not present in Binding Documentation, so no system should use this value.
// This value is only used in CLSA0100 Laptop
CS35L41_EXT_BOOST_NO_VSPK_SWITCH,
};

Expand Down Expand Up @@ -891,6 +901,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap);
int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
struct cs35l41_hw_cfg *hw_cfg);
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 regmap *regmap, enum cs35l41_boost_type b_type, int enable,
struct completion *pll_lock);

#endif /* __CS35L41_H */
6 changes: 3 additions & 3 deletions sound/pci/hda/cs35l41_hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,13 +515,13 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
break;
case HDA_GEN_PCM_ACT_PREPARE:
mutex_lock(&cs35l41->fw_mutex);
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1);
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL);
mutex_unlock(&cs35l41->fw_mutex);
break;
case HDA_GEN_PCM_ACT_CLEANUP:
mutex_lock(&cs35l41->fw_mutex);
regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0);
ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL);
mutex_unlock(&cs35l41->fw_mutex);
break;
case HDA_GEN_PCM_ACT_CLOSE:
Expand Down Expand Up @@ -673,7 +673,7 @@ static int cs35l41_runtime_suspend(struct device *dev)
if (cs35l41->playback_started) {
regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
ARRAY_SIZE(cs35l41_hda_mute));
cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL);
regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
Expand Down
69 changes: 68 additions & 1 deletion sound/soc/codecs/cs35l41-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1114,12 +1114,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 = 0; SYNC_BST_CTL_TX_EN = 1 */
{CS35L41_MDSYNC_EN, 0x00001000},
/* BST_CTL_SEL = CLASSH */
{CS35L41_BSTCVRT_VCTRL2, 0x00000001},
};

static const struct reg_sequence cs35l41_pass_seq[] = {
/* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 0 */
{CS35L41_MDSYNC_EN, 0x00002000},
/* 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);
Expand All @@ -1138,6 +1157,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;
Expand Down Expand Up @@ -1165,11 +1188,55 @@ 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)
int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
struct completion *pll_lock)
{
int ret;
unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3;

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;

struct reg_sequence cs35l41_mdsync_down_seq[] = {
{CS35L41_PWR_CTRL3, pwr_ctrl3},
{CS35L41_GPIO_PAD_CONTROL, pad_control},
{CS35L41_PWR_CTRL1, pwr_ctrl1, 3000},
};
ret = regmap_multi_reg_write(cs35l45->regmap, cs35l41_mdsync_down_seq,
ARRAY_SIZE(cs35l41_mdsync_down_seq));
if (!enable)
break;

if (!pll_lock)
return -EINVAL;

ret = wait_for_completion_timeout(pll_lock, msecs_to_jiffies(1000));
if (ret == 0) {
ret = -ETIMEDOUT;
} else {
regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
pwr_ctrl3 |= CS35L41_SYNC_EN_MASK;
struct reg_sequence cs35l41_mdsync_up_seq[] = {
{CS35L41_PWR_CTRL3, pwr_ctrl3},
{CS35L41_PWR_CTRL1, 0x00000000, 3000},
{CS35L41_PWR_CTRL1, 0x00000001, 3000},
};
ret = regmap_multi_reg_write(cs35l45->regmap, cs35l41_mdsync_up_seq,
ARRAY_SIZE(cs35l41_mdsync_up_seq));
}
break;
case CS35L41_INT_BOOST:
ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
enable << CS35L41_GLOBAL_EN_SHIFT);
Expand Down
27 changes: 25 additions & 2 deletions sound/soc/codecs/cs35l41.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int e
{
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);
Expand Down Expand Up @@ -455,6 +456,12 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
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;
}

done:
pm_runtime_mark_last_busy(cs35l41->dev);
pm_runtime_put_autosuspend(cs35l41->dev);
Expand Down Expand Up @@ -492,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,
Expand Down Expand Up @@ -802,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,
Expand Down Expand Up @@ -1252,6 +1265,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,
Expand All @@ -1275,6 +1292,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);
Expand Down Expand Up @@ -1317,6 +1336,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);
Expand Down
1 change: 1 addition & 0 deletions sound/soc/codecs/cs35l41.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct cs35l41_private {
int irq;
/* GPIO for /RST */
struct gpio_desc *reset_gpio;
struct completion pll_lock;
};

int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
Expand Down

0 comments on commit 87a5c35

Please sign in to comment.