[RFC PATCH v2 1/3] sound: cs35l41: Allow HDA systems to use CS35l41 ASoC driver

Lucas Tanure tanureal at opensource.cirrus.com
Wed Oct 20 10:59:42 CEST 2021


Re-use ASoC driver for supporting for Legion 7 16ACHg6
laptop.
HDA machine driver will find the registered dais for the
Amp and use snd_soc_dai_ops to configure it.

Signed-off-by: Lucas Tanure <tanureal at opensource.cirrus.com>
---
 include/sound/cs35l41.h    |   1 +
 sound/soc/codecs/cs35l41.c | 139 ++++++++++++++++++++++++++++++++++---
 sound/soc/codecs/cs35l41.h |   1 +
 3 files changed, 133 insertions(+), 8 deletions(-)

diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 1f1e3c6c9be1..e250d31d4b04 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -23,6 +23,7 @@ struct cs35l41_irq_cfg {
 };
 
 struct cs35l41_platform_data {
+	bool vspk_always_on;
 	int bst_ind;
 	int bst_ipk;
 	int bst_cap;
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index b16eb6610c0e..e6bb5c047d89 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -21,6 +21,7 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <linux/acpi.h>
 
 #include "cs35l41.h"
 
@@ -1039,9 +1040,7 @@ static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
 {
 	int ret;
 
-	/* Set Platform Data */
-	/* Required */
-	if (cs35l41->pdata.bst_ipk &&
+	if (!cs35l41->pdata.vspk_always_on && cs35l41->pdata.bst_ipk &&
 	    cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) {
 		ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind,
 					   cs35l41->pdata.bst_cap,
@@ -1051,8 +1050,7 @@ static int cs35l41_set_pdata(struct cs35l41_private *cs35l41)
 			return ret;
 		}
 	} else {
-		dev_err(cs35l41->dev, "Incomplete Boost component DT config\n");
-		return -EINVAL;
+		dev_info(cs35l41->dev, "Boost disabled\n");
 	}
 
 	/* Optional */
@@ -1098,12 +1096,92 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41)
 	return irq_pol;
 }
 
+static const struct reg_sequence cs35l41_safe_to_global_enable[] = {
+	{ 0x00000040,			0x00000055 },
+	{ 0x00000040,			0x000000AA },
+	{ 0x0000742C,			0x0000000F },
+	{ 0x0000742C,			0x00000079 },
+	{ 0x00007438,			0x00585941 },
+	{ CS35L41_PLL_CLK_CTRL,		0x00000420 }, //3200000Hz ,BCLK Input ,PLL_REFCLK_EN = 0
+	{ CS35L41_PLL_CLK_CTRL,		0x00000430 }, //3200000Hz ,BCLK Input ,PLL_REFCLK_EN = 1
+	{ CS35L41_GLOBAL_CLK_CTRL,	0x00000003 }, //GLOBAL_FS = 48 kHz
+	{ CS35L41_SP_ENABLES,		0x00010000 }, //ASP_RX1_EN = 1
+	{ CS35L41_SP_RATE_CTRL,		0x00000021 }, //ASP_BCLK_FREQ = 3.072 MHz
+	{ CS35L41_SP_FORMAT,		0x18180200 }, //ASP_RX_WIDTH = 24 bits, ASP_TX_WIDTH = 24 bits, ASP_FMT=I2S, BCLK Slave, FSYNC Slave
+	{ CS35L41_DAC_PCM1_SRC,		0x00000008 }, //DACPCM1_SRC = ASPRX1
+	{ CS35L41_AMP_DIG_VOL_CTRL,	0x00000000 }, //AMP_VOL_PCM  0.0 dB
+	{ CS35L41_AMP_GAIN_CTRL,	0x00000260 }, //AMP_GAIN_PCM 19.5 dB
+	{ CS35L41_PWR_CTRL2,		0x00000001 }, //AMP_EN = 1
+	{ CS35L41_PWR_CTRL1,		0x00000001 }, //GLOBAL_EN = 1
+	{ 0x00000040,			0x000000CC },
+	{ 0x00000040,			0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_global_enable_to_active[] = {
+	{ 0x00000040,			0x00000055 },
+	{ 0x00000040,			0x000000AA },
+	{ 0x0000742C,			0x000000F9 },
+	{ 0x00007438,			0x00580941 },
+	{ 0x00000040,			0x000000CC },
+	{ 0x00000040,			0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_first[] = {
+	{ 0x00000040,			0x00000055 },
+	{ 0x00000040,			0x000000AA },
+	{ 0x00007438,			0x00585941 },
+	{ CS35L41_AMP_DIG_VOL_CTRL,	0x0000A678 }, //AMP_VOL_PCM Mute
+	{ CS35L41_PWR_CTRL2,		0x00000000 }, //AMP_EN = 0
+	{ CS35L41_PWR_CTRL1,		0x00000000 },
+	{ 0x0000742C,			0x00000009 },
+	{ 0x00000040,			0x000000CC },
+	{ 0x00000040,			0x00000033 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_second[] = {
+	{ 0x00000040,			0x00000055 },
+	{ 0x00000040,			0x000000AA },
+	{ 0x00007438,			0x00580941 },
+	{ 0x00000040,			0x000000CC },
+	{ 0x00000040,			0x00000033 },
+};
+
+static void cs35l41_dai_shutdown(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+	struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+	if (cs35l41->hda) {
+		regmap_multi_reg_write(cs35l41->regmap, cs35l41_active_to_safe_first,
+				       ARRAY_SIZE(cs35l41_active_to_safe_first));
+		usleep_range(1500, 2000);
+		regmap_multi_reg_write(cs35l41->regmap, cs35l41_active_to_safe_second,
+				       ARRAY_SIZE(cs35l41_active_to_safe_second));
+	}
+}
+
+static int cs35l41_dai_prepare(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+	struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+	if (cs35l41->hda) {
+		regmap_multi_reg_write(cs35l41->regmap, cs35l41_safe_to_global_enable,
+				       ARRAY_SIZE(cs35l41_safe_to_global_enable));
+		usleep_range(1500, 2000);
+		regmap_multi_reg_write(cs35l41->regmap, cs35l41_global_enable_to_active,
+				      ARRAY_SIZE(cs35l41_global_enable_to_active));
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dai_ops cs35l41_ops = {
 	.startup = cs35l41_pcm_startup,
 	.set_fmt = cs35l41_set_dai_fmt,
 	.hw_params = cs35l41_pcm_hw_params,
 	.set_sysclk = cs35l41_dai_set_sysclk,
 	.set_channel_map = cs35l41_set_channel_map,
+	.shutdown = cs35l41_dai_shutdown,
+	.prepare = cs35l41_dai_prepare,
 };
 
 static struct snd_soc_dai_driver cs35l41_dai[] = {
@@ -1126,6 +1204,7 @@ static struct snd_soc_dai_driver cs35l41_dai[] = {
 		},
 		.ops = &cs35l41_ops,
 		.symmetric_rate = 1,
+		.symmetric_sample_bits = 1,
 	},
 };
 
@@ -1148,9 +1227,31 @@ static int cs35l41_handle_pdata(struct device *dev,
 {
 	struct cs35l41_irq_cfg *irq_gpio1_config = &pdata->irq_config1;
 	struct cs35l41_irq_cfg *irq_gpio2_config = &pdata->irq_config2;
+	struct acpi_device *adev;
+	struct device *phys_dev;
 	unsigned int val;
 	int ret;
 
+	if (memcmp(dev_name(cs35l41->dev), "i2c-CLSA0100", 12) == 0) {
+		pdata->vspk_always_on = true;
+		cs35l41->hda = true;
+		adev = acpi_dev_get_first_match_dev("CLSA0100", "1", -1);
+		if (!adev) {
+			dev_err(dev, "Failed to find an ACPI device\n");
+			return -ENODEV;
+		}
+
+		phys_dev = get_device(acpi_get_first_physical_node(adev));
+		acpi_dev_put(adev);
+
+		if (!phys_dev) {
+			dev_err(dev, "Failed to find a physical device\n");
+			return -ENODEV;
+		}
+		cs35l41->reset_gpio = gpiod_get_index(phys_dev, NULL, 0, GPIOD_ASIS);
+		return 0;
+	}
+
 	ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
 	if (ret >= 0)
 		pdata->bst_ipk = val;
@@ -1237,6 +1338,16 @@ static const struct reg_sequence cs35l41_revb2_errata_patch[] = {
 	{ 0x00000040,			 0x00003333 },
 };
 
+static const struct reg_sequence cs35l41_reset_to_enabled[] = {
+	{ 0x00000040,			0x00000055 },
+	{ 0x00000040,			0x000000AA },
+	{ 0x00007438,			0x00585941 },
+	{ 0x00007414,			0x08C82222 },
+	{ 0x0000742C,			0x00000009 },
+	{ 0x00000040,			0x000000CC },
+	{ 0x00000040,			0x00000033 },
+};
+
 int cs35l41_probe(struct cs35l41_private *cs35l41,
 		  struct cs35l41_platform_data *pdata)
 {
@@ -1269,8 +1380,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
 	}
 
 	/* returning NULL can be an option if in stereo mode */
-	cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
-						      GPIOD_OUT_LOW);
+	if (!cs35l41->reset_gpio)
+		cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(cs35l41->reset_gpio)) {
 		ret = PTR_ERR(cs35l41->reset_gpio);
 		cs35l41->reset_gpio = NULL;
@@ -1365,6 +1476,16 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
 		break;
 	}
 
+	if (cs35l41->pdata.vspk_always_on) {
+		ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_reset_to_enabled,
+					     ARRAY_SIZE(cs35l41_reset_to_enabled));
+		if (ret < 0) {
+			dev_err(cs35l41->dev, "Failed to apply reset to enabled patch: %d\n", ret);
+			goto err;
+		}
+		dev_info(cs35l41->dev, "Safe mode enabled\n");
+	}
+
 	irq_pol = cs35l41_irq_gpio_config(cs35l41);
 
 	/* Set interrupt masks for critical errors */
@@ -1437,7 +1558,9 @@ int cs35l41_remove(struct cs35l41_private *cs35l41)
 {
 	regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
 	regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
-	gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+
+	if (cs35l41->reset_gpio && !cs35l41->pdata.vspk_always_on)
+		gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
index 0e2639d6ef19..bb1f08e36c04 100644
--- a/sound/soc/codecs/cs35l41.h
+++ b/sound/soc/codecs/cs35l41.h
@@ -762,6 +762,7 @@ struct cs35l41_private {
 	struct regmap *regmap;
 	struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES];
 	int irq;
+	bool hda;
 	/* GPIO for /RST */
 	struct gpio_desc *reset_gpio;
 	void (*otp_setup)(struct cs35l41_private *cs35l41, bool is_pre_setup,
-- 
2.33.1



More information about the Alsa-devel mailing list