All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration.
@ 2023-03-02 17:11 Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 2/5] ASoC: dt-bindings: cs35l45: GPIOs configuration Vlad Karpovich
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:11 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: alsa-devel, patches, linux-kernel, Vlad.Karpovich

From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>

Adds device tree configuration for cs35l45 GPIOs

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
---
 include/dt-bindings/sound/cs35l45.h | 57 +++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l45-tables.c   | 14 +++++++
 sound/soc/codecs/cs35l45.c          | 56 ++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l45.h          | 27 +++++++++++++-
 4 files changed, 152 insertions(+), 2 deletions(-)

diff --git a/include/dt-bindings/sound/cs35l45.h b/include/dt-bindings/sound/cs35l45.h
index 076da4b2c28d..25386af18445 100644
--- a/include/dt-bindings/sound/cs35l45.h
+++ b/include/dt-bindings/sound/cs35l45.h
@@ -17,4 +17,61 @@
 #define CS35L45_ASP_TX_HIZ_UNUSED	0x1
 #define CS35L45_ASP_TX_HIZ_DISABLED	0x2
 
+/*
+ * Optional GPIOX Sub-nodes:
+ *  The cs35l45 node can have up to three "cirrus,gpio-ctrlX" ('X' = [1,2,3])
+ *  sub-nodes for configuring the GPIO pins.
+ *
+ * - gpio-dir : GPIO pin direction. Valid only when 'gpio-ctrl'
+ *   is 1.
+ *    0 = Output
+ *    1 = Input (Default)
+ *
+ * - gpio-lvl : GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0.
+ *
+ *    0 = Low (Default)
+ *    1 = High
+ *
+ * - gpio-op-cfg : GPIO output configuration. Valid only when 'gpio-ctrl' is 1
+ *   and 'gpio-dir' is 0.
+ *
+ *    0 = CMOS (Default)
+ *    1 = Open Drain
+ *
+ * - gpio-pol : GPIO output polarity select. Valid only when 'gpio-ctrl' is 1
+ *   and 'gpio-dir' is 0.
+ *
+ *    0 = Non-inverted, Active High (Default)
+ *    1 = Inverted, Active Low
+ *
+ * - gpio-invert : Defines the polarity of the GPIO pin if configured
+ *   as input.
+ *
+ *    0 = Not inverted (Default)
+ *    1 = Inverted
+ *
+ * - gpio-ctrl : Defines the function of the GPIO pin.
+ *
+ * GPIO1:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2 = Pin acts as MDSYNC, direction controlled by MDSYNC
+ *   3-7 = Reserved
+ *
+ * GPIO2:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2 = Pin acts as open drain INT
+ *   3 = Reserved
+ *   4 = Pin acts as push-pull output INT. Active low.
+ *   5 = Pin acts as push-pull output INT. Active high.
+ *   6,7 = Reserved
+ *
+ * GPIO3:
+ *   0 = High impedance input (Default)
+ *   1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+ *   2-7 = Reserved
+ */
+#define CS35L45_NUM_GPIOS	0x3
+
 #endif /* DT_CS35L45_H */
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 5a2c2e684ef9..bca7e830821f 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -43,6 +43,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES);
 static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_BLOCK_ENABLES,		0x00003323 },
 	{ CS35L45_BLOCK_ENABLES2,		0x00000010 },
+	{ CS35L45_SYNC_GPIO1,			0x00000007 },
+	{ CS35L45_INTB_GPIO2_MCLK_REF,		0x00000005 },
+	{ CS35L45_GPIO3,			0x00000005 },
 	{ CS35L45_REFCLK_INPUT,			0x00000510 },
 	{ CS35L45_GLOBAL_SAMPLE_RATE,		0x00000003 },
 	{ CS35L45_ASP_ENABLES1,			0x00000000 },
@@ -61,6 +64,9 @@ static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_ASPTX4_INPUT,			0x00000028 },
 	{ CS35L45_ASPTX5_INPUT,			0x00000048 },
 	{ CS35L45_AMP_PCM_CONTROL,		0x00100000 },
+	{ CS35L45_GPIO1_CTRL1,			0x81000001 },
+	{ CS35L45_GPIO2_CTRL1,			0x81000001 },
+	{ CS35L45_GPIO3_CTRL1,			0x81000001 },
 };
 
 static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
@@ -72,6 +78,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_BLOCK_ENABLES:
 	case CS35L45_BLOCK_ENABLES2:
 	case CS35L45_ERROR_RELEASE:
+	case CS35L45_SYNC_GPIO1:
+	case CS35L45_INTB_GPIO2_MCLK_REF:
+	case CS35L45_GPIO3:
 	case CS35L45_REFCLK_INPUT:
 	case CS35L45_GLOBAL_SAMPLE_RATE:
 	case CS35L45_ASP_ENABLES1:
@@ -92,6 +101,10 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_AMP_PCM_CONTROL:
 	case CS35L45_AMP_PCM_HPF_TST:
 	case CS35L45_IRQ1_EINT_4:
+	case CS35L45_GPIO_STATUS1:
+	case CS35L45_GPIO1_CTRL1:
+	case CS35L45_GPIO2_CTRL1:
+	case CS35L45_GPIO3_CTRL1:
 		return true;
 	default:
 		return false;
@@ -107,6 +120,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
 	case CS35L45_ERROR_RELEASE:
 	case CS35L45_AMP_PCM_HPF_TST:	/* not cachable */
 	case CS35L45_IRQ1_EINT_4:
+	case CS35L45_GPIO_STATUS1:
 		return true;
 	default:
 		return false;
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index d15b3b77c7eb..901f3647fbda 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -536,7 +536,63 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
 
 static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
 {
+	struct device_node *node = cs35l45->dev->of_node;
+	unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+				    CS35L45_GPIO3_CTRL1};
+	unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+				   CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+	struct device_node *child;
 	unsigned int val;
+	char of_name[32];
+	int ret, i;
+
+	if (!node)
+		return 0;
+
+	for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+		sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+		child = of_get_child_by_name(node, of_name);
+		if (!child)
+			continue;
+
+		ret = of_property_read_u32(child, "gpio-dir", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+					   CS35L45_GPIO_DIR_MASK,
+					   val << CS35L45_GPIO_DIR_SHIFT);
+
+		ret = of_property_read_u32(child, "gpio-lvl", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+					   CS35L45_GPIO_LVL_MASK,
+					   val << CS35L45_GPIO_LVL_SHIFT);
+
+		ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+					   CS35L45_GPIO_OP_CFG_MASK,
+					   val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+		ret = of_property_read_u32(child, "gpio-pol", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+					   CS35L45_GPIO_POL_MASK,
+					   val << CS35L45_GPIO_POL_SHIFT);
+
+		ret = of_property_read_u32(child, "gpio-ctrl", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, pad_regs[i],
+					   CS35L45_GPIO_CTRL_MASK,
+					   val << CS35L45_GPIO_CTRL_SHIFT);
+
+		ret = of_property_read_u32(child, "gpio-invert", &val);
+		if (!ret)
+			regmap_update_bits(cs35l45->regmap, pad_regs[i],
+					   CS35L45_GPIO_INVERT_MASK,
+					   val << CS35L45_GPIO_INVERT_SHIFT);
+
+		of_node_put(child);
+	}
 
 	if (device_property_read_u32(cs35l45->dev,
 				     "cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 53fe9d2b7b15..f3a54fc57d53 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -14,6 +14,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
 
 #define CS35L45_DEVID				0x00000000
 #define CS35L45_REVID				0x00000004
@@ -24,6 +25,9 @@
 #define CS35L45_BLOCK_ENABLES			0x00002018
 #define CS35L45_BLOCK_ENABLES2			0x0000201C
 #define CS35L45_ERROR_RELEASE			0x00002034
+#define CS35L45_SYNC_GPIO1			0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF		0x00002434
+#define CS35L45_GPIO3				0x00002438
 #define CS35L45_REFCLK_INPUT			0x00002C04
 #define CS35L45_GLOBAL_SAMPLE_RATE		0x00002C0C
 #define CS35L45_BOOST_CCM_CFG			0x00003808
@@ -48,8 +52,11 @@
 #define CS35L45_AMP_PCM_CONTROL			0x00007000
 #define CS35L45_AMP_PCM_HPF_TST			0x00007004
 #define CS35L45_IRQ1_EINT_4			0x0000E01C
-#define CS35L45_LASTREG				0x0000E01C
-
+#define CS35L45_GPIO_STATUS1			0x0000F000
+#define CS35L45_GPIO1_CTRL1			0x0000F008
+#define CS35L45_GPIO2_CTRL1			0x0000F00C
+#define CS35L45_GPIO3_CTRL1			0x0000F010
+#define CS35L45_LASTREG			0x0000F010
 /* SFT_RESET */
 #define CS35L45_SOFT_RESET_TRIGGER		0x5A000000
 
@@ -165,6 +172,22 @@
 #define CS35L45_OTP_BOOT_DONE_STS_MASK		BIT(1)
 #define CS35L45_OTP_BUSY_MASK			BIT(0)
 
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT			31
+#define CS35L45_GPIO_DIR_MASK			BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT			15
+#define CS35L45_GPIO_LVL_MASK			BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT		14
+#define CS35L45_GPIO_OP_CFG_MASK		BIT(14)
+#define CS35L45_GPIO_POL_SHIFT			12
+#define CS35L45_GPIO_POL_MASK			BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT		20
+#define CS35L45_GPIO_CTRL_MASK			GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT		19
+#define CS35L45_GPIO_INVERT_MASK		BIT(19)
+
 /* Mixer sources */
 #define CS35L45_PCM_SRC_MASK			0x7F
 #define CS35L45_PCM_SRC_ZERO			0x00
-- 
2.25.1


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

* [PATCH 2/5] ASoC: dt-bindings: cs35l45: GPIOs configuration
  2023-03-02 17:11 [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration Vlad Karpovich
@ 2023-03-02 17:11 ` Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 3/5] ASoC: cs35l45: IRQ support Vlad Karpovich
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:11 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: alsa-devel, patches, linux-kernel, Vlad.Karpovich

From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>

This adds description of CS35L45 GPIOs configuration.

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
---
 .../bindings/sound/cirrus,cs35l45.yaml        | 78 +++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
index 184a1344ea76..34c2867e2fd9 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
@@ -47,6 +47,74 @@ properties:
     maximum: 3
     default: 2
 
+patternProperties:
+  "^cirrus,gpio-ctrl[1-3]$":
+    description:
+      GPIO pins configuration.
+    type: object
+    additionalProperties: false
+    properties:
+      gpio-dir:
+        description:
+          GPIO pin direction. Valid only when 'gpio-ctrl' is 1
+            0 = Output
+            1 = Input
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 1
+      gpio-lvl:
+        description:
+          GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
+            0 = Low
+            1 = High
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-op-cfg:
+        description:
+          GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0
+            0 = CMOS
+            1 = Open Drain
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-pol:
+        description:
+          GPIO output polarity select. Valid only when 'gpio-ctrl' is 1
+          and 'gpio-dir' is 0
+            0 = Non-inverted, Active High
+            1 = Inverted, Active Low
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 1
+        default: 0
+      gpio-ctrl:
+        description:
+          Defines the function of the GPIO pin.
+          GPIO1
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2 = Pin acts as MDSYNC, direction controlled by MDSYNC
+            3-7 = Reserved
+          GPIO2
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2 = Pin acts as open drain INT
+            3 = Reserved
+            4 = Pin acts as push-pull output INT. Active low.
+            5 = Pin acts as push-pull output INT. Active high.
+            6,7 = Reserved
+          GPIO3
+            0 = High impedance input
+            1 = Pin acts as a GPIO, direction controlled by 'gpio-dir'
+            2-7 = Reserved
+        $ref: "/schemas/types.yaml#/definitions/uint32"
+        minimum: 0
+        maximum: 7
+        default: 0
 required:
   - compatible
   - reg
@@ -71,5 +139,15 @@ examples:
           reset-gpios = <&gpio 110 0>;
           cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED |
                                         CS35L45_ASP_TX_HIZ_DISABLED)>;
+          cirrus,gpio-ctrl1 {
+             gpio-ctrl = <0x2>;
+          };
+          cirrus,gpio-ctrl2 {
+             gpio-ctrl = <0x2>;
+          };
+          cirrus,gpio-ctrl3 {
+             gpio-ctrl = <0x1>;
+             gpio-dir = <0x1>;
+          };
         };
     };
-- 
2.25.1


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

* [PATCH 3/5] ASoC: cs35l45: IRQ support
  2023-03-02 17:11 [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 2/5] ASoC: dt-bindings: cs35l45: GPIOs configuration Vlad Karpovich
@ 2023-03-02 17:11 ` Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 4/5] ASoC: cs35l45: DSP Support Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 5/5] ASoC: cs34l45: Hibernation support Vlad Karpovich
  3 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:11 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: alsa-devel, patches, linux-kernel, Vlad.Karpovich

From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>

Adds IRQ handlers

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
---
 sound/soc/codecs/cs35l45-i2c.c    |   1 +
 sound/soc/codecs/cs35l45-spi.c    |   1 +
 sound/soc/codecs/cs35l45-tables.c |  29 +++++++-
 sound/soc/codecs/cs35l45.c        | 111 +++++++++++++++++++++++++++++-
 sound/soc/codecs/cs35l45.h        | 107 ++++++++++++++++++++++++++++
 5 files changed, 245 insertions(+), 4 deletions(-)

diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index 39d28641429e..5224aaf584b8 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -32,6 +32,7 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
 	}
 
 	cs35l45->dev = dev;
+	cs35l45->irq = client->irq;
 
 	return cs35l45_probe(cs35l45);
 }
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
index baaf6e0f4fb9..b885ad3f3d4e 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -32,6 +32,7 @@ static int cs35l45_spi_probe(struct spi_device *spi)
 	}
 
 	cs35l45->dev = dev;
+	cs35l45->irq = spi->irq;
 
 	return cs35l45_probe(cs35l45);
 }
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index bca7e830821f..c39c1e6876a4 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -64,6 +64,25 @@ static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_ASPTX4_INPUT,			0x00000028 },
 	{ CS35L45_ASPTX5_INPUT,			0x00000048 },
 	{ CS35L45_AMP_PCM_CONTROL,		0x00100000 },
+	{ CS35L45_IRQ1_CFG,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_1,			0xBFEFFFBF },
+	{ CS35L45_IRQ1_MASK_2,			0xFFFFFFFF },
+	{ CS35L45_IRQ1_MASK_3,			0xFFFF87FF },
+	{ CS35L45_IRQ1_MASK_4,			0xF8FFFFFF },
+	{ CS35L45_IRQ1_MASK_5,			0x0EF80000 },
+	{ CS35L45_IRQ1_MASK_6,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_7,			0xFFFFFF78 },
+	{ CS35L45_IRQ1_MASK_8,			0x00003FFF },
+	{ CS35L45_IRQ1_MASK_9,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_10,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_11,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_12,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_13,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_14,			0x00000001 },
+	{ CS35L45_IRQ1_MASK_15,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_16,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_17,			0x00000000 },
+	{ CS35L45_IRQ1_MASK_18,			0x3FE5D0FF },
 	{ CS35L45_GPIO1_CTRL1,			0x81000001 },
 	{ CS35L45_GPIO2_CTRL1,			0x81000001 },
 	{ CS35L45_GPIO3_CTRL1,			0x81000001 },
@@ -100,7 +119,11 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_ASPTX5_INPUT:
 	case CS35L45_AMP_PCM_CONTROL:
 	case CS35L45_AMP_PCM_HPF_TST:
-	case CS35L45_IRQ1_EINT_4:
+	case CS35L45_IRQ1_CFG:
+	case CS35L45_IRQ1_STATUS:
+	case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+	case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+	case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
 	case CS35L45_GPIO_STATUS1:
 	case CS35L45_GPIO1_CTRL1:
 	case CS35L45_GPIO2_CTRL1:
@@ -119,7 +142,9 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
 	case CS35L45_GLOBAL_ENABLES:
 	case CS35L45_ERROR_RELEASE:
 	case CS35L45_AMP_PCM_HPF_TST:	/* not cachable */
-	case CS35L45_IRQ1_EINT_4:
+	case CS35L45_IRQ1_STATUS:
+	case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+	case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
 	case CS35L45_GPIO_STATUS1:
 		return true;
 	default:
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 901f3647fbda..0c3d01363c8d 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -586,10 +586,13 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
 					   val << CS35L45_GPIO_CTRL_SHIFT);
 
 		ret = of_property_read_u32(child, "gpio-invert", &val);
-		if (!ret)
+		if (!ret) {
 			regmap_update_bits(cs35l45->regmap, pad_regs[i],
 					   CS35L45_GPIO_INVERT_MASK,
 					   val << CS35L45_GPIO_INVERT_SHIFT);
+			if (i == 1)
+				cs35l45->irq_invert = val;
+		}
 
 		of_node_put(child);
 	}
@@ -604,6 +607,78 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
 	return 0;
 }
 
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+	struct cs35l45_private *cs35l45 = data;
+
+	dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+	struct cs35l45_private *cs35l45 = data;
+
+	dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+	CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+	CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+	CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+	CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+	struct cs35l45_private *cs35l45 = data;
+	int i;
+
+	i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+	dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+	return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+	CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+	CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+	CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+	CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+	CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+	.name = "cs35l45 IRQ1 Controller",
+	.main_status = CS35L45_IRQ1_STATUS,
+	.status_base = CS35L45_IRQ1_EINT_1,
+	.mask_base = CS35L45_IRQ1_MASK_1,
+	.ack_base = CS35L45_IRQ1_EINT_1,
+	.num_regs = 18,
+	.irqs = cs35l45_reg_irqs,
+	.num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+	.runtime_pm = true,
+};
+
 static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 {
 	struct device *dev = cs35l45->dev;
@@ -660,7 +735,8 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 int cs35l45_probe(struct cs35l45_private *cs35l45)
 {
 	struct device *dev = cs35l45->dev;
-	int ret;
+	unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+	int ret, i, irq;
 
 	cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
 	if (IS_ERR(cs35l45->vdd_batt))
@@ -705,6 +781,37 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 	if (ret < 0)
 		goto err_reset;
 
+	if (cs35l45->irq) {
+		if (cs35l45->irq_invert)
+			irq_pol |= IRQF_TRIGGER_HIGH;
+		else
+			irq_pol |= IRQF_TRIGGER_LOW;
+
+		ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+					       &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+		if (ret) {
+			dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+			goto err_reset;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+			irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+			if (irq < 0) {
+				dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+				ret = irq;
+				goto err_reset;
+			}
+
+			ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+							irq_pol, cs35l45_irqs[i].name, cs35l45);
+			if (ret) {
+				dev_err(dev, "Failed to request IRQ %s: %d\n",
+					cs35l45_irqs[i].name, ret);
+				goto err_reset;
+			}
+		}
+	}
+
 	ret = devm_snd_soc_register_component(dev, &cs35l45_component,
 					      cs35l45_dai,
 					      ARRAY_SIZE(cs35l45_dai));
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index f3a54fc57d53..ce92f5068ac5 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -51,7 +51,42 @@
 #define CS35L45_LDPM_CONFIG			0x00006404
 #define CS35L45_AMP_PCM_CONTROL			0x00007000
 #define CS35L45_AMP_PCM_HPF_TST			0x00007004
+#define CS35L45_IRQ1_CFG			0x0000E000
+#define CS35L45_IRQ1_STATUS			0x0000E004
+#define CS35L45_IRQ1_EINT_1			0x0000E010
+#define CS35L45_IRQ1_EINT_2			0x0000E014
+#define CS35L45_IRQ1_EINT_3			0x0000E018
 #define CS35L45_IRQ1_EINT_4			0x0000E01C
+#define CS35L45_IRQ1_EINT_5			0x0000E020
+#define CS35L45_IRQ1_EINT_7			0x0000E028
+#define CS35L45_IRQ1_EINT_8			0x0000E02C
+#define CS35L45_IRQ1_EINT_18			0x0000E054
+#define CS35L45_IRQ1_STS_1			0x0000E090
+#define CS35L45_IRQ1_STS_2			0x0000E094
+#define CS35L45_IRQ1_STS_3			0x0000E098
+#define CS35L45_IRQ1_STS_4			0x0000E09C
+#define CS35L45_IRQ1_STS_5			0x0000E0A0
+#define CS35L45_IRQ1_STS_7			0x0000E0A8
+#define CS35L45_IRQ1_STS_8			0x0000E0AC
+#define CS35L45_IRQ1_STS_18			0x0000E0D4
+#define CS35L45_IRQ1_MASK_1			0x0000E110
+#define CS35L45_IRQ1_MASK_2			0x0000E114
+#define CS35L45_IRQ1_MASK_3			0x0000E118
+#define CS35L45_IRQ1_MASK_4			0x0000E11C
+#define CS35L45_IRQ1_MASK_5			0x0000E120
+#define CS35L45_IRQ1_MASK_6			0x0000E124
+#define CS35L45_IRQ1_MASK_7			0x0000E128
+#define CS35L45_IRQ1_MASK_8			0x0000E12C
+#define CS35L45_IRQ1_MASK_9			0x0000E130
+#define CS35L45_IRQ1_MASK_10			0x0000E134
+#define CS35L45_IRQ1_MASK_11			0x0000E138
+#define CS35L45_IRQ1_MASK_12			0x0000E13C
+#define CS35L45_IRQ1_MASK_13			0x0000E140
+#define CS35L45_IRQ1_MASK_14			0x0000E144
+#define CS35L45_IRQ1_MASK_15			0x0000E148
+#define CS35L45_IRQ1_MASK_16			0x0000E14C
+#define CS35L45_IRQ1_MASK_17			0x0000E150
+#define CS35L45_IRQ1_MASK_18			0x0000E154
 #define CS35L45_GPIO_STATUS1			0x0000F000
 #define CS35L45_GPIO1_CTRL1			0x0000F008
 #define CS35L45_GPIO2_CTRL1			0x0000F00C
@@ -188,6 +223,38 @@
 #define CS35L45_GPIO_INVERT_SHIFT		19
 #define CS35L45_GPIO_INVERT_MASK		BIT(19)
 
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT		7
+#define CS35L45_BST_UVP_ERR_MASK		BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT		8
+#define CS35L45_BST_SHORT_ERR_MASK		BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT			17
+#define CS35L45_TEMP_ERR_MASK			BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT	22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK	BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT	29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK		BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT		31
+#define CS35L45_AMP_SHORT_ERR_MASK		BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT		4
+#define CS35L45_DSP_WDT_EXPIRE_MASK		BIT(4)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT		1
+#define CS35L45_PLL_LOCK_FLAG_MASK		BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT	4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK	BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT		25
+#define CS35L45_AMP_CAL_ERR_MASK		BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT		15
+#define CS35L45_GLOBAL_ERROR_MASK		BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT		16
+#define CS35L45_UVLO_VDDLV_ERR_MASK		BIT(16)
+
 /* Mixer sources */
 #define CS35L45_PCM_SRC_MASK			0x7F
 #define CS35L45_PCM_SRC_ZERO			0x00
@@ -217,6 +284,43 @@
 		       SNDRV_PCM_RATE_88200 | \
 		       SNDRV_PCM_RATE_96000)
 
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand)		\
+	{					\
+		.irq = CS35L45_ ## _irq ## _IRQ,\
+		.name = _name,			\
+		.handler = _hand,		\
+	}
+
+struct cs35l45_irq {
+	int irq;
+	const char *name;
+	irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq)					\
+	[CS35L45_ ## _irq ## _IRQ] = {					\
+		.reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1,	\
+		.mask = CS35L45_ ## _irq ## _MASK			\
+	}
+
+enum cs35l45_irq_list {
+	CS35L45_AMP_SHORT_ERR_IRQ,
+	CS35L45_UVLO_VDDBATT_ERR_IRQ,
+	CS35L45_BST_SHORT_ERR_IRQ,
+	CS35L45_BST_UVP_ERR_IRQ,
+	CS35L45_TEMP_ERR_IRQ,
+	CS35L45_AMP_CAL_ERR_IRQ,
+	CS35L45_UVLO_VDDLV_ERR_IRQ,
+	CS35L45_GLOBAL_ERROR_IRQ,
+	CS35L45_DSP_WDT_EXPIRE_IRQ,
+	CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+	CS35L45_PLL_LOCK_FLAG_IRQ,
+	CS35L45_NUM_IRQ
+};
+
 struct cs35l45_private {
 	struct device *dev;
 	struct regmap *regmap;
@@ -227,6 +331,9 @@ struct cs35l45_private {
 	bool sysclk_set;
 	u8 slot_width;
 	u8 slot_count;
+	int irq_invert;
+	int irq;
+	struct regmap_irq_chip_data *irq_data;
 };
 
 extern const struct dev_pm_ops cs35l45_pm_ops;
-- 
2.25.1


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

* [PATCH 4/5] ASoC: cs35l45: DSP Support
  2023-03-02 17:11 [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 2/5] ASoC: dt-bindings: cs35l45: GPIOs configuration Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 3/5] ASoC: cs35l45: IRQ support Vlad Karpovich
@ 2023-03-02 17:11 ` Vlad Karpovich
  2023-03-02 17:11 ` [PATCH 5/5] ASoC: cs34l45: Hibernation support Vlad Karpovich
  3 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:11 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: alsa-devel, patches, linux-kernel, Vlad.Karpovich

From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>

Adds HALO DSP support

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
---
 sound/soc/codecs/Kconfig          |   4 +
 sound/soc/codecs/cs35l45-spi.c    |   3 +
 sound/soc/codecs/cs35l45-tables.c |  86 +++++++
 sound/soc/codecs/cs35l45.c        | 392 +++++++++++++++++++++++++++++-
 sound/soc/codecs/cs35l45.h        | 108 +++++++-
 5 files changed, 580 insertions(+), 13 deletions(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7022e6286e6c..5a0287e3bbec 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -357,6 +357,8 @@ config SND_SOC_WM_ADSP
 	default y if SND_SOC_WM2200=y
 	default y if SND_SOC_CS35L41_SPI=y
 	default y if SND_SOC_CS35L41_I2C=y
+	default y if SND_SOC_CS35L45_SPI=y
+	default y if SND_SOC_CS35L45_I2C=y
 	default m if SND_SOC_MADERA=m
 	default m if SND_SOC_CS47L24=m
 	default m if SND_SOC_WM5102=m
@@ -364,6 +366,8 @@ config SND_SOC_WM_ADSP
 	default m if SND_SOC_WM2200=m
 	default m if SND_SOC_CS35L41_SPI=m
 	default m if SND_SOC_CS35L41_I2C=m
+	default m if SND_SOC_CS35L45_SPI=m
+	default m if SND_SOC_CS35L45_I2C=m
 
 config SND_SOC_AB8500_CODEC
 	tristate
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
index b885ad3f3d4e..bfb79255783e 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -23,6 +23,9 @@ static int cs35l45_spi_probe(struct spi_device *spi)
 	if (cs35l45 == NULL)
 		return -ENOMEM;
 
+	spi->max_speed_hz = CS35L45_SPI_MAX_FREQ;
+	spi_setup(spi);
+
 	spi_set_drvdata(spi, cs35l45);
 	cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
 	if (IS_ERR(cs35l45->regmap)) {
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index c39c1e6876a4..6b68773979ec 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -46,6 +46,7 @@ static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_SYNC_GPIO1,			0x00000007 },
 	{ CS35L45_INTB_GPIO2_MCLK_REF,		0x00000005 },
 	{ CS35L45_GPIO3,			0x00000005 },
+	{ CS35L45_PWRMGT_CTL,			0x00000000 },
 	{ CS35L45_REFCLK_INPUT,			0x00000510 },
 	{ CS35L45_GLOBAL_SAMPLE_RATE,		0x00000003 },
 	{ CS35L45_ASP_ENABLES1,			0x00000000 },
@@ -63,6 +64,30 @@ static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_ASPTX3_INPUT,			0x00000020 },
 	{ CS35L45_ASPTX4_INPUT,			0x00000028 },
 	{ CS35L45_ASPTX5_INPUT,			0x00000048 },
+	{ CS35L45_DSP1_RX1_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX2_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX3_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX4_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX5_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX6_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX7_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX8_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX1_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX2_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX3_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX4_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX5_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX6_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX7_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX8_RATE,		0x00000001 },
+	{ CS35L45_DSP1RX1_INPUT,		0x00000008 },
+	{ CS35L45_DSP1RX2_INPUT,		0x00000009 },
+	{ CS35L45_DSP1RX3_INPUT,		0x00000018 },
+	{ CS35L45_DSP1RX4_INPUT,		0x00000019 },
+	{ CS35L45_DSP1RX5_INPUT,		0x00000020 },
+	{ CS35L45_DSP1RX6_INPUT,		0x00000028 },
+	{ CS35L45_DSP1RX7_INPUT,		0x0000003A },
+	{ CS35L45_DSP1RX8_INPUT,		0x00000028 },
 	{ CS35L45_AMP_PCM_CONTROL,		0x00100000 },
 	{ CS35L45_IRQ1_CFG,			0x00000000 },
 	{ CS35L45_IRQ1_MASK_1,			0xBFEFFFBF },
@@ -100,6 +125,7 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_SYNC_GPIO1:
 	case CS35L45_INTB_GPIO2_MCLK_REF:
 	case CS35L45_GPIO3:
+	case CS35L45_PWRMGT_CTL:
 	case CS35L45_REFCLK_INPUT:
 	case CS35L45_GLOBAL_SAMPLE_RATE:
 	case CS35L45_ASP_ENABLES1:
@@ -117,6 +143,14 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_ASPTX3_INPUT:
 	case CS35L45_ASPTX4_INPUT:
 	case CS35L45_ASPTX5_INPUT:
+	case CS35L45_DSP1RX1_INPUT:
+	case CS35L45_DSP1RX2_INPUT:
+	case CS35L45_DSP1RX3_INPUT:
+	case CS35L45_DSP1RX4_INPUT:
+	case CS35L45_DSP1RX5_INPUT:
+	case CS35L45_DSP1RX6_INPUT:
+	case CS35L45_DSP1RX7_INPUT:
+	case CS35L45_DSP1RX8_INPUT:
 	case CS35L45_AMP_PCM_CONTROL:
 	case CS35L45_AMP_PCM_HPF_TST:
 	case CS35L45_IRQ1_CFG:
@@ -128,6 +162,40 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_GPIO1_CTRL1:
 	case CS35L45_GPIO2_CTRL1:
 	case CS35L45_GPIO3_CTRL1:
+	case CS35L45_DSP_MBOX_1:
+	case CS35L45_DSP_MBOX_2:
+	case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+	case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+	case CS35L45_DSP1_SYS_ID:
+	case CS35L45_DSP1_CLOCK_FREQ:
+	case CS35L45_DSP1_RX1_RATE:
+	case CS35L45_DSP1_RX2_RATE:
+	case CS35L45_DSP1_RX3_RATE:
+	case CS35L45_DSP1_RX4_RATE:
+	case CS35L45_DSP1_RX5_RATE:
+	case CS35L45_DSP1_RX6_RATE:
+	case CS35L45_DSP1_RX7_RATE:
+	case CS35L45_DSP1_RX8_RATE:
+	case CS35L45_DSP1_TX1_RATE:
+	case CS35L45_DSP1_TX2_RATE:
+	case CS35L45_DSP1_TX3_RATE:
+	case CS35L45_DSP1_TX4_RATE:
+	case CS35L45_DSP1_TX5_RATE:
+	case CS35L45_DSP1_TX6_RATE:
+	case CS35L45_DSP1_TX7_RATE:
+	case CS35L45_DSP1_TX8_RATE:
+	case CS35L45_DSP1_SCRATCH1:
+	case CS35L45_DSP1_SCRATCH2:
+	case CS35L45_DSP1_SCRATCH3:
+	case CS35L45_DSP1_SCRATCH4:
+	case CS35L45_DSP1_CCM_CORE_CONTROL:
+	case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+	case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+	case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+	case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+	case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+	case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+	case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
 		return true;
 	default:
 		return false;
@@ -146,6 +214,24 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
 	case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
 	case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
 	case CS35L45_GPIO_STATUS1:
+	case CS35L45_DSP_MBOX_1:
+	case CS35L45_DSP_MBOX_2:
+	case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+	case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+	case CS35L45_DSP1_SYS_ID:
+	case CS35L45_DSP1_CLOCK_FREQ:
+	case CS35L45_DSP1_SCRATCH1:
+	case CS35L45_DSP1_SCRATCH2:
+	case CS35L45_DSP1_SCRATCH3:
+	case CS35L45_DSP1_SCRATCH4:
+	case CS35L45_DSP1_CCM_CORE_CONTROL:
+	case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+	case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+	case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+	case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+	case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+	case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+	case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
 		return true;
 	default:
 		return false;
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 0c3d01363c8d..c5de2c8a9fe3 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/property.h>
+#include <linux/firmware.h>
 #include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -19,6 +20,69 @@
 
 #include "cs35l45.h"
 
+static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
+					enum cs35l45_cspl_mboxstate sts)
+{
+	switch (cmd) {
+	case CSPL_MBOX_CMD_NONE:
+	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);
+	case CSPL_MBOX_CMD_REINIT:
+		return (sts == CSPL_MBOX_STS_RUNNING);
+	case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+		return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+	default:
+		return false;
+	}
+}
+
+static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45,
+				      struct regmap *regmap,
+				      const enum cs35l45_cspl_mboxcmd cmd)
+{
+	unsigned int sts = 0, i;
+	int ret;
+
+	if (!cs35l45->dsp.cs_dsp.running) {
+		dev_err(cs35l45->dev, "DSP not running\n");
+		return -EPERM;
+	}
+
+	// Set mailbox cmd
+	ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd);
+	if (ret < 0) {
+		if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+			dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret);
+		return ret;
+	}
+
+	// Read mailbox status and verify it is appropriate for the given cmd
+	for (i = 0; i < 5; i++) {
+		usleep_range(1000, 1100);
+
+		ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts);
+		if (ret < 0) {
+			dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret);
+			continue;
+		}
+
+		if (!cs35l45_check_cspl_mbox_sts(cmd, sts))
+			dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+		else
+			return 0;
+	}
+
+	if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+		dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+	return -ENOMSG;
+}
+
 static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
 				struct snd_kcontrol *kcontrol, int event)
 {
@@ -46,10 +110,68 @@ static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int cs35l45_dsp_preload_ev(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (cs35l45->dsp.cs_dsp.booted)
+			return 0;
+
+		return wm_adsp_early_event(w, kcontrol, event);
+	case SND_SOC_DAPM_POST_PMU:
+		if (cs35l45->dsp.cs_dsp.running)
+			return 0;
+
+		regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL,
+				   CS35L45_MEM_RDY_MASK);
+
+		return wm_adsp_event(w, kcontrol, event);
+	case SND_SOC_DAPM_PRE_PMD:
+		if (cs35l45->dsp.preloaded)
+			return 0;
+
+		if (cs35l45->dsp.cs_dsp.running) {
+			ret = wm_adsp_event(w, kcontrol, event);
+			if (ret)
+				return ret;
+		}
+
+		return wm_adsp_early_event(w, kcontrol, event);
+	default:
+		return 0;
+	}
+}
+
+static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+						 CSPL_MBOX_CMD_RESUME);
+	case SND_SOC_DAPM_PRE_PMD:
+		return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+						 CSPL_MBOX_CMD_PAUSE);
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static const char * const cs35l45_asp_tx_txt[] = {
 	"Zero", "ASP_RX1", "ASP_RX2",
 	"VMON", "IMON", "ERR_VOL",
 	"VDD_BATTMON", "VDD_BSTMON",
+	"DSP_TX1", "DSP_TX2",
 	"Interpolator", "IL_TARGET",
 };
 
@@ -57,6 +179,7 @@ static const unsigned int cs35l45_asp_tx_val[] = {
 	CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
 	CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
 	CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+	CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2,
 	CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
 };
 
@@ -78,12 +201,54 @@ static const struct soc_enum cs35l45_asp_tx_enums[] = {
 			      cs35l45_asp_tx_val),
 };
 
+static const char * const cs35l45_dsp_rx_txt[] = {
+	"Zero", "ASP_RX1", "ASP_RX2",
+	"VMON", "IMON", "ERR_VOL",
+	"CLASSH_TGT", "VDD_BATTMON",
+	"VDD_BSTMON", "TEMPMON",
+};
+
+static const unsigned int cs35l45_dsp_rx_val[] = {
+	CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+	CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+	CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON,
+	CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON,
+};
+
+static const struct soc_enum cs35l45_dsp_rx_enums[] = {
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+	SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK,
+			      ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+			      cs35l45_dsp_rx_val),
+};
+
 static const char * const cs35l45_dac_txt[] = {
-	"Zero", "ASP_RX1", "ASP_RX2"
+	"Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2"
 };
 
 static const unsigned int cs35l45_dac_val[] = {
-	CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2
+	CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+	CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2
 };
 
 static const struct soc_enum cs35l45_dacpcm_enums[] = {
@@ -100,11 +265,29 @@ static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
 	SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
 };
 
+static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = {
+	SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]),
+	SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]),
+	SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]),
+	SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]),
+	SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]),
+	SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]),
+	SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]),
+	SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]),
+};
+
 static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
 	SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
 };
 
 static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+	SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+				cs35l45_dsp_preload_ev,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+				cs35l45_dsp_audio_ev,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 	SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
 			    cs35l45_global_en_ev,
 			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -139,6 +322,15 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
 	SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
 
+	SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]),
+	SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]),
+	SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]),
+	SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]),
+	SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]),
+	SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]),
+	SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]),
+	SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]),
+
 	SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
 
 	SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -149,6 +341,8 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
 #define CS35L45_ASP_MUX_ROUTE(name) \
 	{ name" Source", "ASP_RX1",	 "ASP_RX1" }, \
 	{ name" Source", "ASP_RX2",	 "ASP_RX2" }, \
+	{ name" Source", "DSP_TX1",	 "DSP1" }, \
+	{ name" Source", "DSP_TX2",	 "DSP1" }, \
 	{ name" Source", "VMON",	 "VMON" }, \
 	{ name" Source", "IMON",	 "IMON" }, \
 	{ name" Source", "ERR_VOL",	 "ERR_VOL" }, \
@@ -157,10 +351,17 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
 	{ name" Source", "Interpolator", "AMP_INTP" }, \
 	{ name" Source", "IL_TARGET",	 "IL_TARGET" }
 
-#define CS35L45_DAC_MUX_ROUTE(name) \
+#define CS35L45_DSP_MUX_ROUTE(name) \
 	{ name" Source", "ASP_RX1",	"ASP_RX1" }, \
 	{ name" Source", "ASP_RX2",	"ASP_RX2" }
 
+#define CS35L45_DAC_MUX_ROUTE(name) \
+	{ name" Source", "ASP_RX1",	"ASP_RX1" }, \
+	{ name" Source", "ASP_RX2",	"ASP_RX2" }, \
+	{ name" Source", "DSP_TX1",	"DSP1" }, \
+	{ name" Source", "DSP_TX2",	"DSP1" }
+
+
 static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
 	/* Feedback */
 	{ "VMON", NULL, "VMON_SRC" },
@@ -204,6 +405,27 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
 	{ "AMP", NULL, "DACPCM1 Source"},
 	{ "AMP", NULL, "GLOBAL_EN"},
 
+	CS35L45_DSP_MUX_ROUTE("DSP_RX1"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX2"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX3"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX4"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX5"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX6"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX7"),
+	CS35L45_DSP_MUX_ROUTE("DSP_RX8"),
+
+	{"DSP1", NULL, "DSP_RX1 Source"},
+	{"DSP1", NULL, "DSP_RX2 Source"},
+	{"DSP1", NULL, "DSP_RX3 Source"},
+	{"DSP1", NULL, "DSP_RX4 Source"},
+	{"DSP1", NULL, "DSP_RX5 Source"},
+	{"DSP1", NULL, "DSP_RX6 Source"},
+	{"DSP1", NULL, "DSP_RX7 Source"},
+	{"DSP1", NULL, "DSP_RX8 Source"},
+
+	{"DSP1 Preload", NULL, "DSP1 Preloader"},
+	{"DSP1", NULL, "DSP1 Preloader"},
+
 	CS35L45_DAC_MUX_ROUTE("DACPCM1"),
 
 	{ "SPK", NULL, "AMP"},
@@ -219,6 +441,8 @@ static const struct snd_kcontrol_new cs35l45_controls[] = {
 			 -409, 48,
 			 (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
 			 0, cs35l45_dig_pcm_vol_tlv),
+	WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+	WM_ADSP_FW_CONTROL("DSP1", 0),
 };
 
 static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
@@ -489,7 +713,24 @@ static struct snd_soc_dai_driver cs35l45_dai[] = {
 	},
 };
 
+static int cs35l45_component_probe(struct snd_soc_component *component)
+{
+	struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+	return wm_adsp2_component_probe(&cs35l45->dsp, component);
+}
+
+static void cs35l45_component_remove(struct snd_soc_component *component)
+{
+	struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+	wm_adsp2_component_remove(&cs35l45->dsp, component);
+}
+
 static const struct snd_soc_component_driver cs35l45_component = {
+	.probe = cs35l45_component_probe,
+	.remove = cs35l45_component_remove,
+
 	.dapm_widgets = cs35l45_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
 
@@ -607,6 +848,60 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
 	return 0;
 }
 
+static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45,
+					      const unsigned int cmd,
+					      unsigned int data)
+{
+	static char *speak_status = "Unknown";
+
+	switch (cmd) {
+	case EVENT_SPEAKER_STATUS:
+		switch (data) {
+		case 1:
+			speak_status = "All Clear";
+			break;
+		case 2:
+			speak_status = "Open Circuit";
+			break;
+		case 4:
+			speak_status = "Short Circuit";
+			break;
+		}
+
+		dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n",
+			 speak_status);
+		break;
+	case EVENT_BOOT_DONE:
+		dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n");
+		break;
+	default:
+		dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data)
+{
+	struct cs35l45_private *cs35l45 = data;
+	unsigned int mbox_val;
+	int ret = 0;
+
+	ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val);
+	if (!ret && mbox_val)
+		ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK,
+				(mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT);
+
+	/* Handle DSP trace log IRQ */
+	ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val);
+	if (!ret && mbox_val != 0) {
+		dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n");
+	}
+
+	return IRQ_RETVAL(ret);
+}
+
 static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
 {
 	struct cs35l45_private *cs35l45 = data;
@@ -639,6 +934,7 @@ static const struct cs35l45_irq cs35l45_irqs[] = {
 	CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
 	CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
 	CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+	CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb),
 };
 
 static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
@@ -665,6 +961,7 @@ static const struct regmap_irq cs35l45_reg_irqs[] = {
 	CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
 	CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
 	CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+	CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX),
 };
 
 static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
@@ -724,14 +1021,63 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 	if (ret < 0)
 		return ret;
 
-	pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
-	pm_runtime_use_autosuspend(cs35l45->dev);
-	pm_runtime_set_active(cs35l45->dev);
-	pm_runtime_enable(cs35l45->dev);
-
 	return 0;
 }
 
+static const struct reg_sequence cs35l45_fs_errata_patch[] = {
+	{0x02B80080,			0x00000001},
+	{0x02B80088,			0x00000001},
+	{0x02B80090,			0x00000001},
+	{0x02B80098,			0x00000001},
+	{0x02B800A0,			0x00000001},
+	{0x02B800A8,			0x00000001},
+	{0x02B800B0,			0x00000001},
+	{0x02B800B8,			0x00000001},
+	{0x02B80280,			0x00000001},
+	{0x02B80288,			0x00000001},
+	{0x02B80290,			0x00000001},
+	{0x02B80298,			0x00000001},
+	{0x02B802A0,			0x00000001},
+	{0x02B802A8,			0x00000001},
+	{0x02B802B0,			0x00000001},
+	{0x02B802B8,			0x00000001},
+};
+
+static const struct cs_dsp_region cs35l45_dsp1_regions[] = {
+	{ .type = WMFW_HALO_PM_PACKED,	.base = CS35L45_DSP1_PMEM_0 },
+	{ .type = WMFW_HALO_XM_PACKED,	.base = CS35L45_DSP1_XMEM_PACK_0 },
+	{ .type = WMFW_HALO_YM_PACKED,	.base = CS35L45_DSP1_YMEM_PACK_0 },
+	{. type = WMFW_ADSP2_XM,	.base = CS35L45_DSP1_XMEM_UNPACK24_0},
+	{. type = WMFW_ADSP2_YM,	.base = CS35L45_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l45_dsp_init(struct cs35l45_private *cs35l45)
+{
+	struct wm_adsp *dsp = &cs35l45->dsp;
+	int ret;
+
+	dsp->part = "cs35l45";
+	dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+	dsp->toggle_preload = true;
+	dsp->cs_dsp.num = 1;
+	dsp->cs_dsp.type = WMFW_HALO;
+	dsp->cs_dsp.rev = 0;
+	dsp->cs_dsp.dev = cs35l45->dev;
+	dsp->cs_dsp.regmap = cs35l45->regmap;
+	dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ;
+	dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID;
+	dsp->cs_dsp.mem = cs35l45_dsp1_regions;
+	dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions);
+	dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+	ret = wm_halo_init(dsp);
+
+	regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch,
+						   ARRAY_SIZE(cs35l45_fs_errata_patch));
+
+	return ret;
+}
+
 int cs35l45_probe(struct cs35l45_private *cs35l45)
 {
 	struct device *dev = cs35l45->dev;
@@ -781,6 +1127,17 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 	if (ret < 0)
 		goto err_reset;
 
+	ret = cs35l45_dsp_init(cs35l45);
+	if (ret < 0)
+		goto err_reset;
+
+	pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+	pm_runtime_use_autosuspend(cs35l45->dev);
+	pm_runtime_mark_last_busy(cs35l45->dev);
+	pm_runtime_set_active(cs35l45->dev);
+	pm_runtime_get_noresume(cs35l45->dev);
+	pm_runtime_enable(cs35l45->dev);
+
 	if (cs35l45->irq) {
 		if (cs35l45->irq_invert)
 			irq_pol |= IRQF_TRIGGER_HIGH;
@@ -791,7 +1148,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 					       &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
 		if (ret) {
 			dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
-			goto err_reset;
+			goto err_dsp;
 		}
 
 		for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
@@ -799,7 +1156,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 			if (irq < 0) {
 				dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
 				ret = irq;
-				goto err_reset;
+				goto err_dsp;
 			}
 
 			ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
@@ -807,7 +1164,7 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 			if (ret) {
 				dev_err(dev, "Failed to request IRQ %s: %d\n",
 					cs35l45_irqs[i].name, ret);
-				goto err_reset;
+				goto err_dsp;
 			}
 		}
 	}
@@ -816,10 +1173,17 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
 					      cs35l45_dai,
 					      ARRAY_SIZE(cs35l45_dai));
 	if (ret < 0)
-		goto err_reset;
+		goto err_dsp;
+
+	pm_runtime_put_autosuspend(cs35l45->dev);
 
 	return 0;
 
+err_dsp:
+	pm_runtime_disable(cs35l45->dev);
+	pm_runtime_put_noidle(cs35l45->dev);
+	wm_adsp2_remove(&cs35l45->dsp);
+
 err_reset:
 	gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
 err:
@@ -832,9 +1196,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
 
 void cs35l45_remove(struct cs35l45_private *cs35l45)
 {
+	pm_runtime_get_sync(cs35l45->dev);
 	pm_runtime_disable(cs35l45->dev);
+	wm_adsp2_remove(&cs35l45->dsp);
 
 	gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+
+	pm_runtime_put_noidle(cs35l45->dev);
 	regulator_disable(cs35l45->vdd_a);
 	/* VDD_BATT must be the last to power-off */
 	regulator_disable(cs35l45->vdd_batt);
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index ce92f5068ac5..87032619b341 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -15,6 +15,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <dt-bindings/sound/cs35l45.h>
+#include "wm_adsp.h"
 
 #define CS35L45_DEVID				0x00000000
 #define CS35L45_REVID				0x00000004
@@ -28,6 +29,7 @@
 #define CS35L45_SYNC_GPIO1			0x00002430
 #define CS35L45_INTB_GPIO2_MCLK_REF		0x00002434
 #define CS35L45_GPIO3				0x00002438
+#define CS35L45_PWRMGT_CTL			0x00002900
 #define CS35L45_REFCLK_INPUT			0x00002C04
 #define CS35L45_GLOBAL_SAMPLE_RATE		0x00002C0C
 #define CS35L45_BOOST_CCM_CFG			0x00003808
@@ -48,6 +50,14 @@
 #define CS35L45_ASPTX3_INPUT			0x00004C28
 #define CS35L45_ASPTX4_INPUT			0x00004C2C
 #define CS35L45_ASPTX5_INPUT			0x00004C30
+#define CS35L45_DSP1RX1_INPUT			0x00004C40
+#define CS35L45_DSP1RX2_INPUT			0x00004C44
+#define CS35L45_DSP1RX3_INPUT			0x00004C48
+#define CS35L45_DSP1RX4_INPUT			0x00004C4C
+#define CS35L45_DSP1RX5_INPUT			0x00004C50
+#define CS35L45_DSP1RX6_INPUT			0x00004C54
+#define CS35L45_DSP1RX7_INPUT			0x00004C58
+#define CS35L45_DSP1RX8_INPUT			0x00004C5C
 #define CS35L45_LDPM_CONFIG			0x00006404
 #define CS35L45_AMP_PCM_CONTROL			0x00007000
 #define CS35L45_AMP_PCM_HPF_TST			0x00007004
@@ -91,7 +101,55 @@
 #define CS35L45_GPIO1_CTRL1			0x0000F008
 #define CS35L45_GPIO2_CTRL1			0x0000F00C
 #define CS35L45_GPIO3_CTRL1			0x0000F010
-#define CS35L45_LASTREG			0x0000F010
+#define CS35L45_DSP_MBOX_1			0x00011000
+#define CS35L45_DSP_MBOX_2			0x00011004
+#define CS35L45_DSP_VIRT1_MBOX_1		0x00011020
+#define CS35L45_DSP_VIRT1_MBOX_2		0x00011024
+#define CS35L45_DSP_VIRT1_MBOX_3		0x00011028
+#define CS35L45_DSP_VIRT1_MBOX_4		0x0001102C
+#define CS35L45_DSP_VIRT2_MBOX_1		0x00011040
+#define CS35L45_DSP_VIRT2_MBOX_2		0x00011044
+#define CS35L45_DSP_VIRT2_MBOX_3		0x00011048
+#define CS35L45_DSP_VIRT2_MBOX_4		0x0001104C
+#define CS35L45_DSP1_XMEM_PACK_0		0x02000000
+#define CS35L45_DSP1_XMEM_PACK_4607		0x020047FC
+#define CS35L45_DSP1_XMEM_UNPACK32_0		0x02400000
+#define CS35L45_DSP1_XMEM_UNPACK32_3071	0x02402FFC
+#define CS35L45_DSP1_SYS_ID			0x025E0000
+#define CS35L45_DSP1_XMEM_UNPACK24_0		0x02800000
+#define CS35L45_DSP1_XMEM_UNPACK24_6143	0x02805FFC
+#define CS35L45_DSP1_CLOCK_FREQ		0x02B80000
+#define CS35L45_DSP1_RX1_RATE			0x02B80080
+#define CS35L45_DSP1_RX2_RATE			0x02B80088
+#define CS35L45_DSP1_RX3_RATE			0x02B80090
+#define CS35L45_DSP1_RX4_RATE			0x02B80098
+#define CS35L45_DSP1_RX5_RATE			0x02B800A0
+#define CS35L45_DSP1_RX6_RATE			0x02B800A8
+#define CS35L45_DSP1_RX7_RATE			0x02B800B0
+#define CS35L45_DSP1_RX8_RATE			0x02B800B8
+#define CS35L45_DSP1_TX1_RATE			0x02B80280
+#define CS35L45_DSP1_TX2_RATE			0x02B80288
+#define CS35L45_DSP1_TX3_RATE			0x02B80290
+#define CS35L45_DSP1_TX4_RATE			0x02B80298
+#define CS35L45_DSP1_TX5_RATE			0x02B802A0
+#define CS35L45_DSP1_TX6_RATE			0x02B802A8
+#define CS35L45_DSP1_TX7_RATE			0x02B802B0
+#define CS35L45_DSP1_TX8_RATE			0x02B802B8
+#define CS35L45_DSP1_SCRATCH1			0x02B805C0
+#define CS35L45_DSP1_SCRATCH2			0x02B805C8
+#define CS35L45_DSP1_SCRATCH3			0x02B805D0
+#define CS35L45_DSP1_SCRATCH4			0x02B805D8
+#define CS35L45_DSP1_CCM_CORE_CONTROL		0x02BC1000
+#define CS35L45_DSP1_YMEM_PACK_0		0x02C00000
+#define CS35L45_DSP1_YMEM_PACK_1532		0x02C017F0
+#define CS35L45_DSP1_YMEM_UNPACK32_0		0x03000000
+#define CS35L45_DSP1_YMEM_UNPACK32_1022	0x03000FF8
+#define CS35L45_DSP1_YMEM_UNPACK24_0		0x03400000
+#define CS35L45_DSP1_YMEM_UNPACK24_2043	0x03401FEC
+#define CS35L45_DSP1_PMEM_0			0x03800000
+#define CS35L45_DSP1_PMEM_3834			0x03803BE8
+#define CS35L45_LASTREG			0x03C6EFE8
+
 /* SFT_RESET */
 #define CS35L45_SOFT_RESET_TRIGGER		0x5A000000
 
@@ -112,9 +170,20 @@
 /* BLOCK_ENABLES2 */
 #define CS35L45_ASP_EN_SHIFT			27
 
+#define CS35L45_MEM_RDY_SHIFT			1
+#define CS35L45_MEM_RDY_MASK			BIT(1)
+
 /* ERROR_RELEASE */
 #define CS35L45_GLOBAL_ERR_RLS_MASK		BIT(11)
 
+/* CCM_CORE */
+#define CS35L45_CCM_CORE_RESET_SHIFT		9
+#define CS35L45_CCM_CORE_RESET_MASK		BIT(9)
+#define CS35L45_CCM_PM_REMAP_SHIFT		7
+#define CS35L45_CCM_PM_REMAP_MASK		BIT(7)
+#define CS35L45_CCM_CORE_EN_SHIFT		0
+#define CS35L45_CCM_CORE_EN_MASK		BIT(0)
+
 /* REFCLK_INPUT */
 #define CS35L45_PLL_FORCE_EN_SHIFT		16
 #define CS35L45_PLL_FORCE_EN_MASK		BIT(16)
@@ -240,6 +309,8 @@
 /* CS35L45_IRQ1_EINT_2 */
 #define CS35L45_DSP_WDT_EXPIRE_SHIFT		4
 #define CS35L45_DSP_WDT_EXPIRE_MASK		BIT(4)
+#define CS35L45_DSP_VIRT2_MBOX_SHIFT		21
+#define CS35L45_DSP_VIRT2_MBOX_MASK		BIT(21)
 
 /* CS35L45_IRQ1_EINT_3 */
 #define CS35L45_PLL_LOCK_FLAG_SHIFT		1
@@ -266,6 +337,8 @@
 #define CS35L45_PCM_SRC_CLASSH_TGT		0x21
 #define CS35L45_PCM_SRC_VDD_BATTMON		0x28
 #define CS35L45_PCM_SRC_VDD_BSTMON		0x29
+#define CS35L45_PCM_SRC_DSP_TX1			0x32
+#define CS35L45_PCM_SRC_DSP_TX2			0x33
 #define CS35L45_PCM_SRC_TEMPMON			0x3A
 #define CS35L45_PCM_SRC_INTERPOLATOR		0x40
 #define CS35L45_PCM_SRC_IL_TARGET		0x48
@@ -275,6 +348,27 @@
 #define CS35L45_POST_GLOBAL_EN_US		5000
 #define CS35L45_PRE_GLOBAL_DIS_US		3000
 
+#define CS35L45_SPI_MAX_FREQ			4000000
+
+enum cs35l45_cspl_mboxstate {
+	CSPL_MBOX_STS_RUNNING = 0,
+	CSPL_MBOX_STS_PAUSED = 1,
+	CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
+	CSPL_MBOX_STS_HIBERNATE = 3,
+};
+
+enum cs35l45_cspl_mboxcmd {
+	CSPL_MBOX_CMD_NONE = 0,
+	CSPL_MBOX_CMD_PAUSE = 1,
+	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,
+};
+
 #define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE| \
 			 SNDRV_PCM_FMTBIT_S24_LE)
@@ -318,10 +412,22 @@ enum cs35l45_irq_list {
 	CS35L45_DSP_WDT_EXPIRE_IRQ,
 	CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
 	CS35L45_PLL_LOCK_FLAG_IRQ,
+	CS35L45_DSP_VIRT2_MBOX_IRQ,
 	CS35L45_NUM_IRQ
 };
 
+#define CS35L45_MBOX3_CMD_MASK		0xFF
+#define CS35L45_MBOX3_CMD_SHIFT		0
+#define CS35L45_MBOX3_DATA_MASK		0xFFFFFF00
+#define CS35L45_MBOX3_DATA_SHIFT	8
+
+enum mbox3_events {
+	EVENT_SPEAKER_STATUS = 0x66,
+	EVENT_BOOT_DONE = 0x67,
+};
+
 struct cs35l45_private {
+	struct wm_adsp dsp; /* needs to be first member */
 	struct device *dev;
 	struct regmap *regmap;
 	struct gpio_desc *reset_gpio;
-- 
2.25.1


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

* [PATCH 5/5] ASoC: cs34l45: Hibernation support
  2023-03-02 17:11 [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration Vlad Karpovich
                   ` (2 preceding siblings ...)
  2023-03-02 17:11 ` [PATCH 4/5] ASoC: cs35l45: DSP Support Vlad Karpovich
@ 2023-03-02 17:11 ` Vlad Karpovich
  2023-03-02 17:19     ` Mark Brown
  3 siblings, 1 reply; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:11 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: alsa-devel, patches, linux-kernel, Vlad.Karpovich

From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>

Adds support for the amplifier hibernation controlled by
DSP firmware.

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
---
 sound/soc/codecs/cs35l45-i2c.c    |  2 +
 sound/soc/codecs/cs35l45-spi.c    |  1 +
 sound/soc/codecs/cs35l45-tables.c |  6 +++
 sound/soc/codecs/cs35l45.c        | 80 +++++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l45.h        | 29 +++++++++++
 5 files changed, 118 insertions(+)

diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index 5224aaf584b8..1c644ddf66bc 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -33,6 +33,8 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
 
 	cs35l45->dev = dev;
 	cs35l45->irq = client->irq;
+	cs35l45->bus_type = CONTROL_BUS_I2C;
+	cs35l45->i2c_addr = client->addr;
 
 	return cs35l45_probe(cs35l45);
 }
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
index bfb79255783e..3ea2e6fd2b88 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -36,6 +36,7 @@ static int cs35l45_spi_probe(struct spi_device *spi)
 
 	cs35l45->dev = dev;
 	cs35l45->irq = spi->irq;
+	cs35l45->bus_type = CONTROL_BUS_SPI;
 
 	return cs35l45_probe(cs35l45);
 }
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 6b68773979ec..212d9cb5a4fb 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -47,6 +47,8 @@ static const struct reg_default cs35l45_defaults[] = {
 	{ CS35L45_INTB_GPIO2_MCLK_REF,		0x00000005 },
 	{ CS35L45_GPIO3,			0x00000005 },
 	{ CS35L45_PWRMGT_CTL,			0x00000000 },
+	{ CS35L45_WAKESRC_CTL,			0x00000008 },
+	{ CS35L45_WKI2C_CTL,			0x00000030 },
 	{ CS35L45_REFCLK_INPUT,			0x00000510 },
 	{ CS35L45_GLOBAL_SAMPLE_RATE,		0x00000003 },
 	{ CS35L45_ASP_ENABLES1,			0x00000000 },
@@ -126,6 +128,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L45_INTB_GPIO2_MCLK_REF:
 	case CS35L45_GPIO3:
 	case CS35L45_PWRMGT_CTL:
+	case CS35L45_WAKESRC_CTL:
+	case CS35L45_WKI2C_CTL:
+	case CS35L45_PWRMGT_STS:
 	case CS35L45_REFCLK_INPUT:
 	case CS35L45_GLOBAL_SAMPLE_RATE:
 	case CS35L45_ASP_ENABLES1:
@@ -210,6 +215,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
 	case CS35L45_GLOBAL_ENABLES:
 	case CS35L45_ERROR_RELEASE:
 	case CS35L45_AMP_PCM_HPF_TST:	/* not cachable */
+	case CS35L45_PWRMGT_STS:
 	case CS35L45_IRQ1_STATUS:
 	case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
 	case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index c5de2c8a9fe3..a2bab613daa2 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -36,6 +36,8 @@ static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd 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_HIBERNATE:
+		return (sts == CSPL_MBOX_STS_HIBERNATE);
 	default:
 		return false;
 	}
@@ -745,11 +747,81 @@ static const struct snd_soc_component_driver cs35l45_component = {
 	.endianness = 1,
 };
 
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+	unsigned int wksrc;
+
+	if (cs35l45->bus_type == CONTROL_BUS_I2C)
+		wksrc = CS35L45_WKSRC_I2C;
+	else
+		wksrc = CS35L45_WKSRC_SPI;
+
+	regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+			   CS35L45_WKSRC_EN_MASK,
+			   wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+	regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+			   CS35L45_UPDT_WKCTL_MASK);
+
+	regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+			   CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+	regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+			   CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+	dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+	cs35l45_setup_hibernate(cs35l45);
+
+	// Don't wait for ACK since bus activity would wake the device
+	regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+	return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+	const int wake_retries = 20;
+	const int sleep_retries = 5;
+	int ret, i, j;
+
+	for (i = 0; i < sleep_retries; i++) {
+		dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+		for (j = 0; j < wake_retries; j++) {
+			ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+					  CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+			if (!ret) {
+				dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+				return 0;
+			}
+			usleep_range(100, 200);
+		}
+
+		dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+		cs35l45_setup_hibernate(cs35l45);
+	}
+
+	dev_err(cs35l45->dev, "Timed out waking device\n");
+
+	return -ETIMEDOUT;
+}
+
 static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
 {
 	struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
 
+	if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+		return 0;
+
+	cs35l45_enter_hibernate(cs35l45);
+
 	regcache_cache_only(cs35l45->regmap, true);
+	regcache_mark_dirty(cs35l45->regmap);
 
 	dev_dbg(cs35l45->dev, "Runtime suspended\n");
 
@@ -761,9 +833,17 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
 	struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
 	int ret;
 
+	if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+		return 0;
+
 	dev_dbg(cs35l45->dev, "Runtime resume\n");
 
 	regcache_cache_only(cs35l45->regmap, false);
+
+	ret = cs35l45_exit_hibernate(cs35l45);
+	if (ret)
+		return ret;
+
 	ret = regcache_sync(cs35l45->regmap);
 	if (ret != 0)
 		dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 87032619b341..0da28439f628 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -30,6 +30,9 @@
 #define CS35L45_INTB_GPIO2_MCLK_REF		0x00002434
 #define CS35L45_GPIO3				0x00002438
 #define CS35L45_PWRMGT_CTL			0x00002900
+#define CS35L45_WAKESRC_CTL			0x00002904
+#define CS35L45_WKI2C_CTL			0x00002908
+#define CS35L45_PWRMGT_STS			0x0000290C
 #define CS35L45_REFCLK_INPUT			0x00002C04
 #define CS35L45_GLOBAL_SAMPLE_RATE		0x00002C0C
 #define CS35L45_BOOST_CCM_CFG			0x00003808
@@ -348,6 +351,25 @@
 #define CS35L45_POST_GLOBAL_EN_US		5000
 #define CS35L45_PRE_GLOBAL_DIS_US		3000
 
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1		BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2			BIT(1)
+#define CS35L45_WKSRC_GPIO3			BIT(2)
+#define CS35L45_WKSRC_SPI			BIT(3)
+#define CS35L45_WKSRC_I2C			BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT		15
+#define CS35L45_UPDT_WKCTL_MASK			BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT			8
+#define CS35L45_WKSRC_EN_MASK			GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT			0
+#define CS35L45_WKSRC_POL_MASK			GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT		15
+#define CS35L45_UPDT_WKI2C_MASK			BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT		0
+#define CS35L45_WKI2C_ADDR_MASK			GENMASK(6, 0)
+
 #define CS35L45_SPI_MAX_FREQ			4000000
 
 enum cs35l45_cspl_mboxstate {
@@ -369,6 +391,11 @@ enum cs35l45_cspl_mboxcmd {
 	CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
 };
 
+enum control_bus_type {
+	CONTROL_BUS_I2C = 0,
+	CONTROL_BUS_SPI = 1,
+};
+
 #define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE| \
 			 SNDRV_PCM_FMTBIT_S24_LE)
@@ -439,6 +466,8 @@ struct cs35l45_private {
 	u8 slot_count;
 	int irq_invert;
 	int irq;
+	unsigned int i2c_addr;
+	enum control_bus_type bus_type;
 	struct regmap_irq_chip_data *irq_data;
 };
 
-- 
2.25.1


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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
  2023-03-02 17:11 ` [PATCH 5/5] ASoC: cs34l45: Hibernation support Vlad Karpovich
@ 2023-03-02 17:19     ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2023-03-02 17:19 UTC (permalink / raw)
  To: Vlad Karpovich
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	patches, linux-kernel

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

On Thu, Mar 02, 2023 at 11:11:54AM -0600, Vlad Karpovich wrote:
> From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>
> 
> Adds support for the amplifier hibernation controlled by
> DSP firmware.

What is amplifier hibernation?

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

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
@ 2023-03-02 17:19     ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2023-03-02 17:19 UTC (permalink / raw)
  To: Vlad Karpovich
  Cc: Liam Girdwood, Takashi Iwai, alsa-devel, patches, linux-kernel

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

On Thu, Mar 02, 2023 at 11:11:54AM -0600, Vlad Karpovich wrote:
> From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>
> 
> Adds support for the amplifier hibernation controlled by
> DSP firmware.

What is amplifier hibernation?

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

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
  2023-03-02 17:19     ` Mark Brown
@ 2023-03-02 17:59       ` Vlad Karpovich
  -1 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	patches, linux-kernel

The CS35L45 features a low-power Hibernation State. In this state, all 
register contents are lost, but the contents of
RAM are retained. In the Hibernation State, only always-on digital 
functions to support wake-up are enabled.
Entry to this state is achieved via the register interface (either by an 
external driver using the control port, or the
programmable DSP). Exit from this state is triggered by activity on 
device GPIO pins, intended SPI transaction, or I2C
transaction with intended slave address

On 3/2/23 11:19, Mark Brown wrote:
> On Thu, Mar 02, 2023 at 11:11:54AM -0600, Vlad Karpovich wrote:
>> From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>
>>
>> Adds support for the amplifier hibernation controlled by
>> DSP firmware.
> What is amplifier hibernation?

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
@ 2023-03-02 17:59       ` Vlad Karpovich
  0 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 17:59 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, Takashi Iwai, alsa-devel, patches, linux-kernel

The CS35L45 features a low-power Hibernation State. In this state, all 
register contents are lost, but the contents of
RAM are retained. In the Hibernation State, only always-on digital 
functions to support wake-up are enabled.
Entry to this state is achieved via the register interface (either by an 
external driver using the control port, or the
programmable DSP). Exit from this state is triggered by activity on 
device GPIO pins, intended SPI transaction, or I2C
transaction with intended slave address

On 3/2/23 11:19, Mark Brown wrote:
> On Thu, Mar 02, 2023 at 11:11:54AM -0600, Vlad Karpovich wrote:
>> From: "Vlad.Karpovich" <vkarpovi@opensource.cirrus.com>
>>
>> Adds support for the amplifier hibernation controlled by
>> DSP firmware.
> What is amplifier hibernation?

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
  2023-03-02 17:59       ` Vlad Karpovich
@ 2023-03-02 18:08         ` Mark Brown
  -1 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2023-03-02 18:08 UTC (permalink / raw)
  To: Vlad Karpovich
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	patches, linux-kernel

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

On Thu, Mar 02, 2023 at 11:59:05AM -0600, Vlad Karpovich wrote:
> The CS35L45 features a low-power Hibernation State. In this state, all
> register contents are lost, but the contents of
> RAM are retained. In the Hibernation State, only always-on digital functions
> to support wake-up are enabled.
> Entry to this state is achieved via the register interface (either by an
> external driver using the control port, or the
> programmable DSP). Exit from this state is triggered by activity on device
> GPIO pins, intended SPI transaction, or I2C
> transaction with intended slave address

OK, so it's essentially just a faster mechanism for bringing the device
out of runtime suspend?  I would suggest doing something in the code to
clarify that this is not the same thing as system level hibernation,
having references to hibernate in the driver is likely to lead to
confusion down the line.  I'd also include a bit more description in the
commit message too.

Please don't top post, reply in line with needed context.  This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.

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

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
@ 2023-03-02 18:08         ` Mark Brown
  0 siblings, 0 replies; 13+ messages in thread
From: Mark Brown @ 2023-03-02 18:08 UTC (permalink / raw)
  To: Vlad Karpovich
  Cc: Liam Girdwood, Takashi Iwai, alsa-devel, patches, linux-kernel

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

On Thu, Mar 02, 2023 at 11:59:05AM -0600, Vlad Karpovich wrote:
> The CS35L45 features a low-power Hibernation State. In this state, all
> register contents are lost, but the contents of
> RAM are retained. In the Hibernation State, only always-on digital functions
> to support wake-up are enabled.
> Entry to this state is achieved via the register interface (either by an
> external driver using the control port, or the
> programmable DSP). Exit from this state is triggered by activity on device
> GPIO pins, intended SPI transaction, or I2C
> transaction with intended slave address

OK, so it's essentially just a faster mechanism for bringing the device
out of runtime suspend?  I would suggest doing something in the code to
clarify that this is not the same thing as system level hibernation,
having references to hibernate in the driver is likely to lead to
confusion down the line.  I'd also include a bit more description in the
commit message too.

Please don't top post, reply in line with needed context.  This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.

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

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
  2023-03-02 18:08         ` Mark Brown
@ 2023-03-02 20:03           ` Vlad Karpovich
  -1 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 20:03 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	patches, linux-kernel


On 3/2/23 12:08, Mark Brown wrote:
> On Thu, Mar 02, 2023 at 11:59:05AM -0600, Vlad Karpovich wrote:
>> The CS35L45 features a low-power Hibernation State. In this state, all
>> register contents are lost, but the contents of
>> RAM are retained. In the Hibernation State, only always-on digital functions
>> to support wake-up are enabled.
>> Entry to this state is achieved via the register interface (either by an
>> external driver using the control port, or the
>> programmable DSP). Exit from this state is triggered by activity on device
>> GPIO pins, intended SPI transaction, or I2C
>> transaction with intended slave address
> OK, so it's essentially just a faster mechanism for bringing the device
> out of runtime suspend?

I don't think it is a faster way since it requires interaction with DSP 
and restoring all wiped registers.

But it saves a some power comparing a low power state in the current driver

>   I would suggest doing something in the code to
> clarify that this is not the same thing as system level hibernation,
> having references to hibernate in the driver is likely to lead to
> confusion down the line.

The feature is named hibernation in the data sheet.  Renaming it in the 
driver will add confusing for the driver user.

>   I'd also include a bit more description in the
> commit message too.
I will do in next version.
> Please don't top post, reply in line with needed context.  This allows
> readers to readily follow the flow of conversation and understand what
> you are talking about and also helps ensure that everything in the
> discussion is being addressed.
Thanks. I will do.

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

* Re: [PATCH 5/5] ASoC: cs34l45: Hibernation support
@ 2023-03-02 20:03           ` Vlad Karpovich
  0 siblings, 0 replies; 13+ messages in thread
From: Vlad Karpovich @ 2023-03-02 20:03 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, Takashi Iwai, alsa-devel, patches, linux-kernel


On 3/2/23 12:08, Mark Brown wrote:
> On Thu, Mar 02, 2023 at 11:59:05AM -0600, Vlad Karpovich wrote:
>> The CS35L45 features a low-power Hibernation State. In this state, all
>> register contents are lost, but the contents of
>> RAM are retained. In the Hibernation State, only always-on digital functions
>> to support wake-up are enabled.
>> Entry to this state is achieved via the register interface (either by an
>> external driver using the control port, or the
>> programmable DSP). Exit from this state is triggered by activity on device
>> GPIO pins, intended SPI transaction, or I2C
>> transaction with intended slave address
> OK, so it's essentially just a faster mechanism for bringing the device
> out of runtime suspend?

I don't think it is a faster way since it requires interaction with DSP 
and restoring all wiped registers.

But it saves a some power comparing a low power state in the current driver

>   I would suggest doing something in the code to
> clarify that this is not the same thing as system level hibernation,
> having references to hibernate in the driver is likely to lead to
> confusion down the line.

The feature is named hibernation in the data sheet.  Renaming it in the 
driver will add confusing for the driver user.

>   I'd also include a bit more description in the
> commit message too.
I will do in next version.
> Please don't top post, reply in line with needed context.  This allows
> readers to readily follow the flow of conversation and understand what
> you are talking about and also helps ensure that everything in the
> discussion is being addressed.
Thanks. I will do.

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

end of thread, other threads:[~2023-03-06 17:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-02 17:11 [PATCH 1/5] ASoC: cs35l45: Support for GPIO pins configuration Vlad Karpovich
2023-03-02 17:11 ` [PATCH 2/5] ASoC: dt-bindings: cs35l45: GPIOs configuration Vlad Karpovich
2023-03-02 17:11 ` [PATCH 3/5] ASoC: cs35l45: IRQ support Vlad Karpovich
2023-03-02 17:11 ` [PATCH 4/5] ASoC: cs35l45: DSP Support Vlad Karpovich
2023-03-02 17:11 ` [PATCH 5/5] ASoC: cs34l45: Hibernation support Vlad Karpovich
2023-03-02 17:19   ` Mark Brown
2023-03-02 17:19     ` Mark Brown
2023-03-02 17:59     ` Vlad Karpovich
2023-03-02 17:59       ` Vlad Karpovich
2023-03-02 18:08       ` Mark Brown
2023-03-02 18:08         ` Mark Brown
2023-03-02 20:03         ` Vlad Karpovich
2023-03-02 20:03           ` Vlad Karpovich

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.