diff mbox series

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

Message ID 20211020085944.17577-2-tanureal@opensource.cirrus.com (mailing list archive)
State New, archived
Headers show
Series Add support for Legion 7 16ACHg6 laptop | expand

Commit Message

Lucas Tanure Oct. 20, 2021, 8:59 a.m. UTC
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@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(-)

Comments

Mark Brown Oct. 20, 2021, 12:30 p.m. UTC | #1
On Wed, Oct 20, 2021 at 09:59:42AM +0100, Lucas Tanure wrote:

Please submit patches using subject lines reflecting the style for the
subsystem, this makes it easier for people to identify relevant patches.
Look at what existing commits in the area you're changing are doing and
make sure your subject lines visually resemble what they're doing.

> 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.

It will find the registered DAIs in what way exactly?

This patch is doing a whole bunch of stuff which isn't described at all
in the changelog which makes it very hard to review, I can't tell what
it's supposed to do.
diff mbox series

Patch

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,