[PATCH 2/2] ASoC: cs35l45: Adds support for LDPM feature
From: Ricardo Rivera-Matos
Date: Thu Oct 01 2026 - 11:47:39 EST
Adds support for level dependent power management
feature.
Signed-off-by: Ricardo Rivera-Matos <rriveram@xxxxxxxxxxxxxxxxxxxxx>
---
sound/soc/codecs/cs35l45-tables.c | 58 ++++++++++++++++++++++++++++
sound/soc/codecs/cs35l45.c | 80 +++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs35l45.h | 30 +++++++++++++++
3 files changed, 168 insertions(+)
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 57982b8ae645..554c8121a74f 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -53,6 +53,7 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
{ CS35L45_SYNC_TX_RX_ENABLES, 0x00000200 },
{ CS35L45_SYNC_SW_TX_ID, 0x00000000 },
+ { CS35L45_BOOST_LPMODE_CFG, 0x00000002 },
{ CS35L45_ASP_ENABLES1, 0x00000000 },
{ CS35L45_ASP_CONTROL1, 0x00000028 },
{ CS35L45_ASP_CONTROL2, 0x18180200 },
@@ -76,6 +77,7 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_DSP1RX6_INPUT, 0x00000028 },
{ CS35L45_DSP1RX7_INPUT, 0x0000003A },
{ CS35L45_DSP1RX8_INPUT, 0x00000028 },
+ { CS35L45_LDPM_CONFIG, 0x00003636 },
{ CS35L45_AMP_PCM_CONTROL, 0x00100000 },
{ CS35L45_AMP_GAIN, 0x00002300 },
{ CS35L45_IRQ1_CFG, 0x00000000 },
@@ -138,6 +140,7 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_GLOBAL_SAMPLE_RATE:
case CS35L45_SYNC_TX_RX_ENABLES:
case CS35L45_SYNC_SW_TX_ID:
+ case CS35L45_BOOST_LPMODE_CFG:
case CS35L45_ASP_ENABLES1:
case CS35L45_ASP_CONTROL1:
case CS35L45_ASP_CONTROL2:
@@ -162,6 +165,7 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_DSP1RX7_INPUT:
case CS35L45_DSP1RX8_INPUT:
case CS35L45_HVLV_CONFIG:
+ case CS35L45_LDPM_CONFIG:
case CS35L45_AMP_PCM_CONTROL:
case CS35L45_AMP_GAIN:
case CS35L45_AMP_PCM_HPF_TST:
@@ -334,3 +338,57 @@ int cs35l45_get_clk_freq_id(unsigned int freq)
return -EINVAL;
}
EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, "SND_SOC_CS35L45");
+
+static const struct {
+ u8 cfg_id;
+ s32 pcm_thld_db;
+} cs35l45_ldpm_pcm_thld[] = {
+ { 0x0, -66 },
+ { 0x1, -72 },
+ { 0x2, -78 },
+ { 0x3, -84 },
+ { 0x4, -90 },
+ { 0x5, -96 },
+ { 0x6, -102 },
+ { 0x7, -2147483648 },
+};
+
+int cs35l45_get_ldpm_pcm_thld(s32 pcm_thld_db)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_ldpm_pcm_thld); i++) {
+ if (cs35l45_ldpm_pcm_thld[i].pcm_thld_db == pcm_thld_db)
+ return cs35l45_ldpm_pcm_thld[i].cfg_id;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_ldpm_pcm_thld, "SND_SOC_CS35L45");
+
+static const struct {
+ u8 cfg_id;
+ u32 delay_ms;
+} cs35l45_ldpm_delay[] = {
+ { 0x0, 5 },
+ { 0x1, 10 },
+ { 0x2, 25 },
+ { 0x3, 50 },
+ { 0x4, 100 },
+ { 0x5, 250 },
+ { 0x6, 500 },
+ { 0x7, 1000 },
+};
+
+int cs35l45_get_ldpm_delay(u32 delay_ms)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_ldpm_delay); i++) {
+ if (cs35l45_ldpm_delay[i].delay_ms == delay_ms)
+ return cs35l45_ldpm_delay[i].cfg_id;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_ldpm_delay, "SND_SOC_CS35L45");
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 9eef7c8b1837..064ee612f05e 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -1148,6 +1148,82 @@ static void cs35l45_apply_sync_property_config(struct cs35l45_private *cs35l45)
CS35L45_SYNC_SW_TXID_MASK, val);
}
+static int cs35l45_apply_ldpm_config(struct cs35l45_private *cs35l45)
+{
+ char prop_name[CS35L45_LDPM_PROP_NAME_MAX];
+ int i, ret;
+ u32 val;
+
+ for (i = LDPM_GROUP1; i < NUM_LDPM_GROUP; i++) {
+ sprintf(prop_name, "cirrus,ldpm-group%d-pcm-threshold-db", i);
+ if (device_property_read_u32(cs35l45->dev, prop_name, &val) == 0) {
+ ret = cs35l45_get_ldpm_pcm_thld((s32)val);
+ if (ret == -EINVAL)
+ return -EINVAL;
+
+ switch (i) {
+ case LDPM_GROUP1:
+ regmap_update_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG,
+ CS35L45_LDPM_GP1_PCM_THLD_MASK,
+ FIELD_PREP(CS35L45_LDPM_GP1_PCM_THLD_MASK,
+ ret));
+ break;
+ case LDPM_GROUP2:
+ regmap_update_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG,
+ CS35L45_LDPM_GP2_PCM_THLD_MASK,
+ FIELD_PREP(CS35L45_LDPM_GP2_PCM_THLD_MASK,
+ ret));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ sprintf(prop_name, "cirrus,ldpm-group%d-delay-ms", i);
+ if (device_property_read_u32(cs35l45->dev, prop_name, &val) == 0) {
+ ret = cs35l45_get_ldpm_delay(val);
+ if (ret == -EINVAL)
+ return -EINVAL;
+
+ switch (i) {
+ case LDPM_GROUP1:
+ regmap_update_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG,
+ CS35L45_LDPM_GP1_DELAY_MASK,
+ FIELD_PREP(CS35L45_LDPM_GP1_DELAY_MASK, ret));
+ break;
+ case LDPM_GROUP2:
+ regmap_update_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG,
+ CS35L45_LDPM_GP2_DELAY_MASK,
+ FIELD_PREP(CS35L45_LDPM_GP2_DELAY_MASK, ret));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (device_property_read_bool(cs35l45->dev, "cirrus,ldpm-group1-boost-select"))
+ regmap_set_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG, CS35L45_LDPM_GP1_BOOST_SEL);
+
+ if (device_property_read_u32(cs35l45->dev, "cirrus,boost-low-power-mode", &val) == 0)
+ regmap_write(cs35l45->regmap, CS35L45_BOOST_LPMODE_CFG, val);
+
+ if (device_property_read_bool(cs35l45->dev, "cirrus,ldpm-group1-amp-select"))
+ regmap_set_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG, CS35L45_LDPM_GP1_AMP_SEL);
+
+ if ((device_property_read_u32(cs35l45->dev, "cirrus,amplifier-low-power-mode", &val) == 0)
+ && (val == 0))
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES, CS35L45_NFR_EN_MASK);
+
+ if (device_property_read_bool(cs35l45->dev, "cirrus,ldpm-group2-vmon-select"))
+ regmap_set_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG, CS35L45_LDPM_GP2_VMON_SEL);
+
+ if (device_property_read_bool(cs35l45->dev, "cirrus,ldpm-group2-imon-select"))
+ regmap_set_bits(cs35l45->regmap, CS35L45_LDPM_CONFIG, CS35L45_LDPM_GP2_IMON_SEL);
+
+ return 0;
+}
+
static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
{
struct device_node *node = cs35l45->dev->of_node;
@@ -1223,6 +1299,10 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT);
}
+ ret = cs35l45_apply_ldpm_config(cs35l45);
+ if (ret < 0)
+ return ret;
+
return 0;
}
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index f8a4713ae92f..5014c765adcb 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -38,6 +38,7 @@
#define CS35L45_SYNC_TX_RX_ENABLES 0x00003400
#define CS35L45_SYNC_SW_TX_ID 0x00003408
#define CS35L45_BOOST_CCM_CFG 0x00003808
+#define CS35L45_BOOST_LPMODE_CFG 0x00003810
#define CS35L45_BOOST_DCM_CFG 0x0000380C
#define CS35L45_BOOST_OV_CFG 0x0000382C
#define CS35L45_ASP_ENABLES1 0x00004800
@@ -174,6 +175,8 @@
#define CS35L45_BST_EN_MASK GENMASK(5, 4)
#define CS35L45_RCV_EN_SHIFT 2
#define CS35L45_RCV_EN_MASK BIT(2)
+#define CS35L45_NFR_EN_SHIFT 1
+#define CS35L45_NFR_EN_MASK BIT(1)
#define CS35L45_AMP_EN_SHIFT 0
#define CS35L45_AMP_EN_MASK BIT(0)
@@ -240,6 +243,9 @@
#define CS35L45_SYNC_SW_TXID_SHIFT 0
#define CS35L45_SYNC_SW_TXID_MASK GENMASK(2, 0)
+/* BOOST_LPMODE_CFG */
+#define CS35L45_BST_LPMODE_SEL GENMASK(1, 0)
+
/* ASP_ENABLES_1 */
#define CS35L45_ASP_RX2_EN_SHIFT 17
#define CS35L45_ASP_RX1_EN_SHIFT 16
@@ -303,6 +309,20 @@
#define CS35L45_HVLV_MODE_SHIFT 0
#define CS35L45_HVLV_MODE_MASK GENMASK(1, 0)
+/* LDPM_CONFIG */
+#define CS35L45_LDPM_GP1_BOOST_SEL BIT(15)
+#define CS35L45_LDPM_GP1_AMP_SEL BIT(14)
+#define CS35L45_LDPM_GP1_DELAY_SHIFT 11
+#define CS35L45_LDPM_GP1_DELAY_MASK GENMASK(13, 11)
+#define CS35L45_LDPM_GP1_PCM_THLD_SHIFT 8
+#define CS35L45_LDPM_GP1_PCM_THLD_MASK GENMASK(10, 8)
+#define CS35L45_LDPM_GP2_IMON_SEL BIT(7)
+#define CS35L45_LDPM_GP2_VMON_SEL BIT(6)
+#define CS35L45_LDPM_GP2_DELAY_SHIFT 3
+#define CS35L45_LDPM_GP2_DELAY_MASK GENMASK(5, 3)
+#define CS35L45_LDPM_GP2_PCM_THLD_SHIFT 0
+#define CS35L45_LDPM_GP2_PCM_THLD_MASK GENMASK(2, 0)
+
/* AMP_PCM_CONTROL */
#define CS35L45_AMP_VOL_PCM_SHIFT 0
#define CS35L45_AMP_VOL_PCM_WIDTH 11
@@ -448,6 +468,12 @@ enum amp_mode {
AMP_MODE_RCV = 1,
};
+enum ldpm_groups {
+ LDPM_GROUP1 = 1,
+ LDPM_GROUP2 = 2,
+ NUM_LDPM_GROUP,
+};
+
#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE| \
SNDRV_PCM_FMTBIT_S24_LE)
@@ -457,6 +483,8 @@ enum amp_mode {
SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000)
+#define CS35L45_LDPM_PROP_NAME_MAX 37
+
/*
* IRQs
*/
@@ -531,6 +559,8 @@ extern const struct regmap_config cs35l45_i2c_regmap;
extern const struct regmap_config cs35l45_spi_regmap;
int cs35l45_apply_patch(struct cs35l45_private *cs35l45);
int cs35l45_get_clk_freq_id(unsigned int freq);
+int cs35l45_get_ldpm_pcm_thld(int pcm_thld_db);
+int cs35l45_get_ldpm_delay(u32 delay_ms);
int cs35l45_probe(struct cs35l45_private *cs35l45);
void cs35l45_remove(struct cs35l45_private *cs35l45);
--
2.43.0