All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add low power hibernation support to cs35l41
@ 2022-01-07 16:06 Charles Keepax
  2022-01-07 16:06 ` [PATCH v2 1/2] ASoC: cs35l41: Update handling of test key registers Charles Keepax
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Charles Keepax @ 2022-01-07 16:06 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, david.rhodes, lgirdwood, tiwai

This patch series adds support for the low power hibernation feature
on cs35l41. This allows the DSP memory to be retained whilst the
device enters a very low power state.

These patches will cause some very minor conflicts with Lucas's
currently outstanding work on the HDA version of cs35l41.  Whilst
things will still build (well now I fixed my silly mistake), this
patch adds a test key function his code will now have to call.

It looks like Lucas's patch might get a respin based on the comments
from Andy, in which case I guess we can pull the small change into the
next version of it. But either way these patches are not urgent so I
am happy if they sit around until Lucas's stuff is merged too.

Thanks,
Charles

Charles Keepax (2):
  ASoC: cs35l41: Update handling of test key registers
  ASoC: cs35l41: Add support for hibernate memory retention mode

 include/sound/cs35l41.h        |   7 ++
 sound/soc/codecs/cs35l41-i2c.c |   1 +
 sound/soc/codecs/cs35l41-lib.c |  73 +++++++------
 sound/soc/codecs/cs35l41-spi.c |   1 +
 sound/soc/codecs/cs35l41.c     | 233 +++++++++++++++++++++++++++++++++++++----
 sound/soc/codecs/cs35l41.h     |   4 +
 6 files changed, 268 insertions(+), 51 deletions(-)

-- 
2.11.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/2] ASoC: cs35l41: Update handling of test key registers
  2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
@ 2022-01-07 16:06 ` Charles Keepax
  2022-01-07 16:06 ` [PATCH v2 2/2] ASoC: cs35l41: Add support for hibernate memory retention mode Charles Keepax
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Charles Keepax @ 2022-01-07 16:06 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, david.rhodes, lgirdwood, tiwai

In preparation for the addition of PM runtime support move the test
key out of the register patches themselves. This is necessary to
allow the test key to be held during cache synchronisation, which is
required by the OTP settings which were unpacked from the device and
written by the driver.

Also whilst at it, the driver uses a mixture of accessing the test key
register by name and by address, consistently use the name.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---

Changes since v1:
 - Add missing export symbol for new test_key functions

 include/sound/cs35l41.h        |  2 ++
 sound/soc/codecs/cs35l41-lib.c | 67 +++++++++++++++++++++---------------------
 sound/soc/codecs/cs35l41.c     | 32 +++++++++++---------
 3 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 29a527457b486..56289b67b9a0e 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -762,6 +762,8 @@ struct cs35l41_otp_map_element_t {
 extern struct regmap_config cs35l41_regmap_i2c;
 extern struct regmap_config cs35l41_regmap_spi;
 
+int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap);
+int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap);
 int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap);
 int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid);
 int cs35l41_set_channels(struct device *dev, struct regmap *reg,
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index 639dcd25b17e9..ecaf67fd76531 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -623,8 +623,6 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM]
 };
 
 static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
-	{ 0x00000040,			 0x00005555 },
-	{ 0x00000040,			 0x0000AAAA },
 	{ 0x00003854,			 0x05180240 },
 	{ CS35L41_VIMON_SPKMON_RESYNC,	 0x00000000 },
 	{ 0x00004310,			 0x00000000 },
@@ -637,38 +635,28 @@ static const struct reg_sequence cs35l41_reva0_errata_patch[] = {
 	{ CS35L41_IRQ2_DB3,		 0x00000000 },
 	{ CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
 	{ CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
-	{ 0x00000040,			 0x0000CCCC },
-	{ 0x00000040,			 0x00003333 },
 	{ CS35L41_PWR_CTRL2,		 0x00000000 },
 	{ CS35L41_AMP_GAIN_CTRL,	 0x00000000 },
 };
 
 static const struct reg_sequence cs35l41_revb0_errata_patch[] = {
-	{ 0x00000040,			 0x00005555 },
-	{ 0x00000040,			 0x0000AAAA },
 	{ CS35L41_VIMON_SPKMON_RESYNC,	 0x00000000 },
 	{ 0x00004310,			 0x00000000 },
 	{ CS35L41_VPVBST_FS_SEL,	 0x00000000 },
 	{ CS35L41_BSTCVRT_DCM_CTRL,	 0x00000051 },
 	{ CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
 	{ CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
-	{ 0x00000040,			 0x0000CCCC },
-	{ 0x00000040,			 0x00003333 },
 	{ CS35L41_PWR_CTRL2,		 0x00000000 },
 	{ CS35L41_AMP_GAIN_CTRL,	 0x00000000 },
 };
 
 static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
-	{ 0x00000040,			 0x00005555 },
-	{ 0x00000040,			 0x0000AAAA },
 	{ CS35L41_VIMON_SPKMON_RESYNC,	 0x00000000 },
 	{ 0x00004310,			 0x00000000 },
 	{ CS35L41_VPVBST_FS_SEL,	 0x00000000 },
 	{ CS35L41_BSTCVRT_DCM_CTRL,	 0x00000051 },
 	{ CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 },
 	{ CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 },
-	{ 0x00000040,			 0x0000CCCC },
-	{ 0x00000040,			 0x00003333 },
 	{ CS35L41_PWR_CTRL2,		 0x00000000 },
 	{ CS35L41_AMP_GAIN_CTRL,	 0x00000000 },
 };
@@ -756,6 +744,39 @@ static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
 	return NULL;
 }
 
+int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap)
+{
+	static const struct reg_sequence unlock[] = {
+		{ CS35L41_TEST_KEY_CTL, 0x00000055 },
+		{ CS35L41_TEST_KEY_CTL, 0x000000AA },
+	};
+	int ret;
+
+	ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+	if (ret)
+		dev_err(dev, "Failed to unlock test key: %d\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock);
+
+int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap)
+{
+	static const struct reg_sequence unlock[] = {
+		{ CS35L41_TEST_KEY_CTL, 0x000000CC },
+		{ CS35L41_TEST_KEY_CTL, 0x00000033 },
+	};
+	int ret;
+
+	ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock));
+	if (ret)
+		dev_err(dev, "Failed to lock test key: %d\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_test_key_lock);
+
+/* Must be called with the TEST_KEY unlocked */
 int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
 {
 	const struct cs35l41_otp_map_element_t *otp_map_match;
@@ -794,17 +815,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
 	bit_offset = otp_map_match->bit_offset;
 	word_offset = otp_map_match->word_offset;
 
-	ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000055);
-	if (ret) {
-		dev_err(dev, "Write Unlock key failed 1/2: %d\n", ret);
-		goto err_otp_unpack;
-	}
-	ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000AA);
-	if (ret) {
-		dev_err(dev, "Write Unlock key failed 2/2: %d\n", ret);
-		goto err_otp_unpack;
-	}
-
 	for (i = 0; i < otp_map_match->num_elements; i++) {
 		dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n",
 			bit_offset, word_offset, bit_sum % 32);
@@ -840,16 +850,6 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
 		}
 	}
 
-	ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000CC);
-	if (ret) {
-		dev_err(dev, "Write Lock key failed 1/2: %d\n", ret);
-		goto err_otp_unpack;
-	}
-	ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000033);
-	if (ret) {
-		dev_err(dev, "Write Lock key failed 2/2: %d\n", ret);
-		goto err_otp_unpack;
-	}
 	ret = 0;
 
 err_otp_unpack:
@@ -859,6 +859,7 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
 }
 EXPORT_SYMBOL_GPL(cs35l41_otp_unpack);
 
+/* Must be called with the TEST_KEY unlocked */
 int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid)
 {
 	char *rev;
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 05839fabf97bc..e1b9fd8ee9966 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -534,19 +534,19 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
 }
 
 static const struct reg_sequence cs35l41_pup_patch[] = {
-	{ 0x00000040, 0x00000055 },
-	{ 0x00000040, 0x000000AA },
+	{ CS35L41_TEST_KEY_CTL, 0x00000055 },
+	{ CS35L41_TEST_KEY_CTL, 0x000000AA },
 	{ 0x00002084, 0x002F1AA0 },
-	{ 0x00000040, 0x000000CC },
-	{ 0x00000040, 0x00000033 },
+	{ CS35L41_TEST_KEY_CTL, 0x000000CC },
+	{ CS35L41_TEST_KEY_CTL, 0x00000033 },
 };
 
 static const struct reg_sequence cs35l41_pdn_patch[] = {
-	{ 0x00000040, 0x00000055 },
-	{ 0x00000040, 0x000000AA },
+	{ CS35L41_TEST_KEY_CTL, 0x00000055 },
+	{ CS35L41_TEST_KEY_CTL, 0x000000AA },
 	{ 0x00002084, 0x002F1AA3 },
-	{ 0x00000040, 0x000000CC },
-	{ 0x00000040, 0x00000033 },
+	{ CS35L41_TEST_KEY_CTL, 0x000000CC },
+	{ CS35L41_TEST_KEY_CTL, 0x00000033 },
 };
 
 static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
@@ -1329,10 +1329,20 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
 		goto err;
 	}
 
+	cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+
 	ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
 	if (ret)
 		goto err;
 
+	ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+	if (ret < 0) {
+		dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
+		goto err;
+	}
+
+	cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+
 	irq_pol = cs35l41_irq_gpio_config(cs35l41);
 
 	/* Set interrupt masks for critical errors */
@@ -1347,12 +1357,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
 		goto err;
 	}
 
-	ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
-	if (ret < 0) {
-		dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
-		goto err;
-	}
-
 	ret = cs35l41_set_pdata(cs35l41);
 	if (ret < 0) {
 		dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret);
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/2] ASoC: cs35l41: Add support for hibernate memory retention mode
  2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
  2022-01-07 16:06 ` [PATCH v2 1/2] ASoC: cs35l41: Update handling of test key registers Charles Keepax
@ 2022-01-07 16:06 ` Charles Keepax
  2022-01-07 16:10 ` [PATCH 0/2] Add low power hibernation support to cs35l41 Mark Brown
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Charles Keepax @ 2022-01-07 16:06 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, david.rhodes, lgirdwood, tiwai

The cs35l41 supports a low power DSP memory retention mode. Add support
for entering this mode when then device is not in use.

Co-authored-by: David Rhodes <david.rhodes@cirrus.com>
Signed-off-by: David Rhodes <david.rhodes@cirrus.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---

Changes since v1:
 - Add missing export symbol for cs35l41_pm_ops

 include/sound/cs35l41.h        |   5 +
 sound/soc/codecs/cs35l41-i2c.c |   1 +
 sound/soc/codecs/cs35l41-lib.c |   6 ++
 sound/soc/codecs/cs35l41-spi.c |   1 +
 sound/soc/codecs/cs35l41.c     | 201 ++++++++++++++++++++++++++++++++++++++++-
 sound/soc/codecs/cs35l41.h     |   4 +
 6 files changed, 214 insertions(+), 4 deletions(-)

diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 56289b67b9a0e..bf7f9a9aeba04 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -40,6 +40,9 @@
 #define CS35L41_PROTECT_REL_ERR_IGN	0x00002034
 #define CS35L41_GPIO_PAD_CONTROL	0x0000242C
 #define CS35L41_JTAG_CONTROL		0x00002438
+#define CS35L41_PWRMGT_CTL		0x00002900
+#define CS35L41_WAKESRC_CTL		0x00002904
+#define CS35L41_PWRMGT_STS		0x00002908
 #define CS35L41_PLL_CLK_CTRL		0x00002C04
 #define CS35L41_DSP_CLK_CTRL		0x00002C08
 #define CS35L41_GLOBAL_CLK_CTRL		0x00002C0C
@@ -635,6 +638,8 @@
 #define CS35L41_INPUT_DSP_TX1		0x32
 #define CS35L41_INPUT_DSP_TX2		0x33
 
+#define CS35L41_WR_PEND_STS_MASK	0x2
+
 #define CS35L41_PLL_CLK_SEL_MASK	0x07
 #define CS35L41_PLL_CLK_SEL_SHIFT	0
 #define CS35L41_PLL_CLK_EN_MASK		0x10
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
index eb8dfb6d9c950..faad5c638cb82 100644
--- a/sound/soc/codecs/cs35l41-i2c.c
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -86,6 +86,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
 static struct i2c_driver cs35l41_i2c_driver = {
 	.driver = {
 		.name		= "cs35l41",
+		.pm		= &cs35l41_pm_ops,
 		.of_match_table = of_match_ptr(cs35l41_of_match),
 		.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
 	},
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index ecaf67fd76531..e5a56bcbb223d 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -90,6 +90,9 @@ static bool cs35l41_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L41_PROTECT_REL_ERR_IGN:
 	case CS35L41_GPIO_PAD_CONTROL:
 	case CS35L41_JTAG_CONTROL:
+	case CS35L41_PWRMGT_CTL:
+	case CS35L41_WAKESRC_CTL:
+	case CS35L41_PWRMGT_STS:
 	case CS35L41_PLL_CLK_CTRL:
 	case CS35L41_DSP_CLK_CTRL:
 	case CS35L41_GLOBAL_CLK_CTRL:
@@ -376,6 +379,9 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg)
 	case CS35L41_OTPID:
 	case CS35L41_TEST_KEY_CTL:
 	case CS35L41_USER_KEY_CTL:
+	case CS35L41_PWRMGT_CTL:
+	case CS35L41_WAKESRC_CTL:
+	case CS35L41_PWRMGT_STS:
 	case CS35L41_DTEMP_EN:
 	case CS35L41_IRQ1_STATUS:
 	case CS35L41_IRQ1_STATUS1:
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
index 86bbe2fba956e..6dfd5459aa207 100644
--- a/sound/soc/codecs/cs35l41-spi.c
+++ b/sound/soc/codecs/cs35l41-spi.c
@@ -84,6 +84,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
 static struct spi_driver cs35l41_spi_driver = {
 	.driver = {
 		.name		= "cs35l41",
+		.pm		= &cs35l41_pm_ops,
 		.of_match_table = of_match_ptr(cs35l41_of_match),
 		.acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
 	},
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index e1b9fd8ee9966..77a0176946459 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/property.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
@@ -187,8 +188,14 @@ static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w,
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
+		if (cs35l41->dsp.cs_dsp.booted)
+			return 0;
+
 		return wm_adsp_early_event(w, kcontrol, event);
 	case SND_SOC_DAPM_PRE_PMD:
+		if (cs35l41->dsp.preloaded)
+			return 0;
+
 		if (cs35l41->dsp.cs_dsp.running) {
 			ret = wm_adsp_event(w, kcontrol, event);
 			if (ret)
@@ -209,6 +216,7 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
 	case CSPL_MBOX_CMD_UNKNOWN_CMD:
 		return true;
 	case CSPL_MBOX_CMD_PAUSE:
+	case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
 		return (sts == CSPL_MBOX_STS_PAUSED);
 	case CSPL_MBOX_CMD_RESUME:
 		return (sts == CSPL_MBOX_STS_RUNNING);
@@ -230,7 +238,8 @@ static int cs35l41_set_cspl_mbox_cmd(struct cs35l41_private *cs35l41,
 	// Set mailbox cmd
 	ret = regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd);
 	if (ret < 0) {
-		dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret);
+		if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+			dev_err(cs35l41->dev, "Failed to write MBOX: %d\n", ret);
 		return ret;
 	}
 
@@ -413,6 +422,8 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
 	int ret = IRQ_NONE;
 	unsigned int i;
 
+	pm_runtime_get_sync(cs35l41->dev);
+
 	for (i = 0; i < ARRAY_SIZE(status); i++) {
 		regmap_read(cs35l41->regmap,
 			    CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE),
@@ -425,7 +436,7 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
 	/* Check to see if unmasked bits are active */
 	if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) &&
 	    !(status[2] & ~masks[2]) && !(status[3] & ~masks[3]))
-		return IRQ_NONE;
+		goto done;
 
 	if (status[3] & CS35L41_OTP_BOOT_DONE) {
 		regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4,
@@ -530,6 +541,10 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+done:
+	pm_runtime_mark_last_busy(cs35l41->dev);
+	pm_runtime_put_autosuspend(cs35l41->dev);
+
 	return ret;
 }
 
@@ -1180,6 +1195,7 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41)
 	dsp->cs_dsp.type = WMFW_HALO;
 	dsp->cs_dsp.rev = 0;
 	dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+	dsp->toggle_preload = true;
 	dsp->cs_dsp.dev = cs35l41->dev;
 	dsp->cs_dsp.regmap = cs35l41->regmap;
 	dsp->cs_dsp.base = CS35L41_DSP1_CTRL_BASE;
@@ -1367,20 +1383,32 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
 	if (ret < 0)
 		goto err;
 
+	pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+	pm_runtime_use_autosuspend(cs35l41->dev);
+	pm_runtime_mark_last_busy(cs35l41->dev);
+	pm_runtime_set_active(cs35l41->dev);
+	pm_runtime_get_noresume(cs35l41->dev);
+	pm_runtime_enable(cs35l41->dev);
+
 	ret = devm_snd_soc_register_component(cs35l41->dev,
 					      &soc_component_dev_cs35l41,
 					      cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
 	if (ret < 0) {
 		dev_err(cs35l41->dev, "Register codec failed: %d\n", ret);
-		goto err_dsp;
+		goto err_pm;
 	}
 
+	pm_runtime_put_autosuspend(cs35l41->dev);
+
 	dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n",
 		 regid, reg_revid);
 
 	return 0;
 
-err_dsp:
+err_pm:
+	pm_runtime_disable(cs35l41->dev);
+	pm_runtime_put_noidle(cs35l41->dev);
+
 	wm_adsp2_remove(&cs35l41->dsp);
 err:
 	regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
@@ -1392,13 +1420,178 @@ EXPORT_SYMBOL_GPL(cs35l41_probe);
 
 void cs35l41_remove(struct cs35l41_private *cs35l41)
 {
+	pm_runtime_get_sync(cs35l41->dev);
+	pm_runtime_disable(cs35l41->dev);
+
 	regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
 	wm_adsp2_remove(&cs35l41->dsp);
+
+	pm_runtime_put_noidle(cs35l41->dev);
+
 	regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
 	gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
 }
 EXPORT_SYMBOL_GPL(cs35l41_remove);
 
+static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+	dev_dbg(cs35l41->dev, "Runtime suspend\n");
+
+	if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+		return 0;
+
+	dev_dbg(cs35l41->dev, "Enter hibernate\n");
+
+	regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
+	regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+	// Don't wait for ACK since bus activity would wake the device
+	regmap_write(cs35l41->regmap, CS35L41_DSP_VIRT1_MBOX_1,
+		     CSPL_MBOX_CMD_HIBERNATE);
+
+	regcache_cache_only(cs35l41->regmap, true);
+	regcache_mark_dirty(cs35l41->regmap);
+
+	return 0;
+}
+
+static void cs35l41_wait_for_pwrmgt_sts(struct cs35l41_private *cs35l41)
+{
+	const int pwrmgt_retries = 10;
+	unsigned int sts;
+	int i, ret;
+
+	for (i = 0; i < pwrmgt_retries; i++) {
+		ret = regmap_read(cs35l41->regmap, CS35L41_PWRMGT_STS, &sts);
+		if (ret)
+			dev_err(cs35l41->dev, "Failed to read PWRMGT_STS: %d\n", ret);
+		else if (!(sts & CS35L41_WR_PEND_STS_MASK))
+			return;
+
+		udelay(20);
+	}
+
+	dev_err(cs35l41->dev, "Timed out reading PWRMGT_STS\n");
+}
+
+static int cs35l41_exit_hibernate(struct cs35l41_private *cs35l41)
+{
+	const int wake_retries = 20;
+	const int sleep_retries = 5;
+	int ret, i, j;
+
+	for (i = 0; i < sleep_retries; i++) {
+		dev_dbg(cs35l41->dev, "Exit hibernate\n");
+
+		for (j = 0; j < wake_retries; j++) {
+			ret = cs35l41_set_cspl_mbox_cmd(cs35l41,
+							CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+			if (!ret)
+				break;
+
+			usleep_range(100, 200);
+		}
+
+		if (j < wake_retries) {
+			dev_dbg(cs35l41->dev, "Wake success at cycle: %d\n", j);
+			return 0;
+		}
+
+		dev_err(cs35l41->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+		cs35l41_wait_for_pwrmgt_sts(cs35l41);
+		regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0088);
+
+		cs35l41_wait_for_pwrmgt_sts(cs35l41);
+		regmap_write(cs35l41->regmap, CS35L41_WAKESRC_CTL, 0x0188);
+
+		cs35l41_wait_for_pwrmgt_sts(cs35l41);
+		regmap_write(cs35l41->regmap, CS35L41_PWRMGT_CTL, 0x3);
+	}
+
+	dev_err(cs35l41->dev, "Timed out waking device\n");
+
+	return -ETIMEDOUT;
+}
+
+static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(cs35l41->dev, "Runtime resume\n");
+
+	if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running)
+		return 0;
+
+	regcache_cache_only(cs35l41->regmap, false);
+
+	ret = cs35l41_exit_hibernate(cs35l41);
+	if (ret)
+		return ret;
+
+	/* Test key needs to be unlocked to allow the OTP settings to re-apply */
+	cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+	ret = regcache_sync(cs35l41->regmap);
+	cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+	if (ret) {
+		dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+	dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n");
+	disable_irq(cs35l41->irq);
+
+	return 0;
+}
+
+static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+	dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n");
+	enable_irq(cs35l41->irq);
+
+	return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+	dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n");
+	disable_irq(cs35l41->irq);
+
+	return 0;
+}
+
+static int __maybe_unused cs35l41_sys_resume(struct device *dev)
+{
+	struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
+
+	dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n");
+	enable_irq(cs35l41->irq);
+
+	return 0;
+}
+
+const struct dev_pm_ops cs35l41_pm_ops = {
+	SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+	SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+EXPORT_SYMBOL_GPL(cs35l41_pm_ops);
+
 MODULE_DESCRIPTION("ASoC CS35L41 driver");
 MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
index 26a08d58a8c34..88a3d6e3434fb 100644
--- a/sound/soc/codecs/cs35l41.h
+++ b/sound/soc/codecs/cs35l41.h
@@ -21,6 +21,8 @@
 #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
 #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
 
+extern const struct dev_pm_ops cs35l41_pm_ops;
+
 enum cs35l41_cspl_mbox_status {
 	CSPL_MBOX_STS_RUNNING = 0,
 	CSPL_MBOX_STS_PAUSED = 1,
@@ -33,6 +35,8 @@ enum cs35l41_cspl_mbox_cmd {
 	CSPL_MBOX_CMD_RESUME = 2,
 	CSPL_MBOX_CMD_REINIT = 3,
 	CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+	CSPL_MBOX_CMD_HIBERNATE = 5,
+	CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
 	CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
 	CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
 };
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add low power hibernation support to cs35l41
  2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
  2022-01-07 16:06 ` [PATCH v2 1/2] ASoC: cs35l41: Update handling of test key registers Charles Keepax
  2022-01-07 16:06 ` [PATCH v2 2/2] ASoC: cs35l41: Add support for hibernate memory retention mode Charles Keepax
@ 2022-01-07 16:10 ` Mark Brown
  2022-01-07 16:15 ` Takashi Iwai
  2022-01-07 18:25 ` Mark Brown
  4 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2022-01-07 16:10 UTC (permalink / raw)
  To: Charles Keepax; +Cc: patches, alsa-devel, david.rhodes, lgirdwood, tiwai

[-- Attachment #1: Type: text/plain, Size: 410 bytes --]

On Fri, Jan 07, 2022 at 04:06:34PM +0000, Charles Keepax wrote:

> These patches will cause some very minor conflicts with Lucas's
> currently outstanding work on the HDA version of cs35l41.  Whilst
> things will still build (well now I fixed my silly mistake), this
> patch adds a test key function his code will now have to call.

Which patches are these?  It looks like everything I was aware of is
merged.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add low power hibernation support to cs35l41
  2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
                   ` (2 preceding siblings ...)
  2022-01-07 16:10 ` [PATCH 0/2] Add low power hibernation support to cs35l41 Mark Brown
@ 2022-01-07 16:15 ` Takashi Iwai
  2022-01-07 16:47   ` Charles Keepax
  2022-01-07 18:25 ` Mark Brown
  4 siblings, 1 reply; 7+ messages in thread
From: Takashi Iwai @ 2022-01-07 16:15 UTC (permalink / raw)
  To: Charles Keepax
  Cc: alsa-devel, patches, tiwai, lgirdwood, david.rhodes, broonie

On Fri, 07 Jan 2022 17:06:34 +0100,
Charles Keepax wrote:
> 
> This patch series adds support for the low power hibernation feature
> on cs35l41. This allows the DSP memory to be retained whilst the
> device enters a very low power state.
> 
> These patches will cause some very minor conflicts with Lucas's
> currently outstanding work on the HDA version of cs35l41.  Whilst
> things will still build (well now I fixed my silly mistake), this
> patch adds a test key function his code will now have to call.
> 
> It looks like Lucas's patch might get a respin based on the comments
> from Andy, in which case I guess we can pull the small change into the
> next version of it. But either way these patches are not urgent so I
> am happy if they sit around until Lucas's stuff is merged too.

Well, I've already merged HD-audio part, so any changes will be
incremental over it.

CS35L41 HD-audio stuff still won't work as of now with my tree alone
because of the lack of the ACPI patch.  So it's fine to merge those
changes to Mark's tree and break the stuff now, supposing it'll be
synced again before 5.17-rc1 pull request.  After it's merged to my
tree, you can submit the HD-audio fix, to be merged before rc1. 


thanks,

Takashi

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add low power hibernation support to cs35l41
  2022-01-07 16:15 ` Takashi Iwai
@ 2022-01-07 16:47   ` Charles Keepax
  0 siblings, 0 replies; 7+ messages in thread
From: Charles Keepax @ 2022-01-07 16:47 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, patches, tiwai, lgirdwood, david.rhodes, broonie

On Fri, Jan 07, 2022 at 05:15:52PM +0100, Takashi Iwai wrote:
> On Fri, 07 Jan 2022 17:06:34 +0100,
> Charles Keepax wrote:
> > 
> > This patch series adds support for the low power hibernation feature
> > on cs35l41. This allows the DSP memory to be retained whilst the
> > device enters a very low power state.
> > 
> > These patches will cause some very minor conflicts with Lucas's
> > currently outstanding work on the HDA version of cs35l41.  Whilst
> > things will still build (well now I fixed my silly mistake), this
> > patch adds a test key function his code will now have to call.
> > 
> > It looks like Lucas's patch might get a respin based on the comments
> > from Andy, in which case I guess we can pull the small change into the
> > next version of it. But either way these patches are not urgent so I
> > am happy if they sit around until Lucas's stuff is merged too.
> 
> Well, I've already merged HD-audio part, so any changes will be
> incremental over it.
> 
> CS35L41 HD-audio stuff still won't work as of now with my tree alone
> because of the lack of the ACPI patch.  So it's fine to merge those
> changes to Mark's tree and break the stuff now, supposing it'll be
> synced again before 5.17-rc1 pull request.  After it's merged to my
> tree, you can submit the HD-audio fix, to be merged before rc1. 
> 

Apologies I didn't realise those were already merged. I will do a
patch to update them to use the new functions.

Thanks,
Charles

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add low power hibernation support to cs35l41
  2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
                   ` (3 preceding siblings ...)
  2022-01-07 16:15 ` Takashi Iwai
@ 2022-01-07 18:25 ` Mark Brown
  4 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2022-01-07 18:25 UTC (permalink / raw)
  To: Charles Keepax; +Cc: patches, alsa-devel, david.rhodes, lgirdwood, tiwai

On Fri, 7 Jan 2022 16:06:34 +0000, Charles Keepax wrote:
> This patch series adds support for the low power hibernation feature
> on cs35l41. This allows the DSP memory to be retained whilst the
> device enters a very low power state.
> 
> These patches will cause some very minor conflicts with Lucas's
> currently outstanding work on the HDA version of cs35l41.  Whilst
> things will still build (well now I fixed my silly mistake), this
> patch adds a test key function his code will now have to call.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/2] ASoC: cs35l41: Update handling of test key registers
      commit: d92321bbe46b0ecae0941461379d39599610d869
[2/2] ASoC: cs35l41: Add support for hibernate memory retention mode
      commit: f517ba4924ad026f2583553db02f3c8bc69de88b

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-01-07 18:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-07 16:06 [PATCH 0/2] Add low power hibernation support to cs35l41 Charles Keepax
2022-01-07 16:06 ` [PATCH v2 1/2] ASoC: cs35l41: Update handling of test key registers Charles Keepax
2022-01-07 16:06 ` [PATCH v2 2/2] ASoC: cs35l41: Add support for hibernate memory retention mode Charles Keepax
2022-01-07 16:10 ` [PATCH 0/2] Add low power hibernation support to cs35l41 Mark Brown
2022-01-07 16:15 ` Takashi Iwai
2022-01-07 16:47   ` Charles Keepax
2022-01-07 18:25 ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.