Add support for laptops that have CS35L41 connected to an HDA
codec by I2S and direct I2C connection to the CPU.
Laptops that use CS35L41 and are SPI will be added in the future,
after the support for it is resolved at i2c-multi-instantiate driver.
i2c-multi-instantiate thread: https://lkml.org/lkml/2021/12/10/557
Hardware:
- Some laptops have two CS35L41 amplifiers, connected to Realtek ALC287
by an I2S bus and by and direct I2C to the CPU.
- The ALC287 codec is connected to the CPU by an HDA bus.
- The CS35L41 has a DSP which will require firmware to be loaded.
Architecture:
- Creation of a library of shared functions for CS35L41 ASoC and HDA
- HDA codec driver (RealTek) and CS35L41 HDA driver are combined
by using component binding, where it uses device names to find the
components and bind to the master
- The HDA CS35L41 driver applies pre-defined registers sequences
for each action in playback for HDA Sound card
Changes from V5:
- Fix build issues reported by Intel Test Bot
- Check devm_kasprintf return
- Add ACPI dependency to HDA drivers
V5: https://lkml.org/lkml/2021/12/16/430
Changes from V4:
- Save index received from ACPI for latter use in DSP Support
- Move CS35L41_DSP1_CCM_CORE_CTRL to regmap_write so doesn't
affect DSP hibernation feature
V4: https://lkml.org/lkml/2021/12/14/487
Changes from V3:
- SPI bus driver starter added
- Use separate modules approach instead of liking library
- Add CSC3551 ACPI _HID for more I2C laptops
- Removed regulators from HDA driver
- Add note about Non-conforming _HID
V3: https://lkml.org/lkml/2021/11/23/723
Changes from V2:
- Not an RFC
- Create a new HDA driver for CS35L41 instead of using the ASoC one
- Use component binding and device names to find the CS35L41 driver
- Create a shared library for ASoC and HDA CS35L41 drivers
v2: https://lkml.org/lkml/2021/10/8/344
Lucas Tanure (9):
ASoC: cs35l41: Convert tables to shared source code
ASoC: cs35l41: Move cs35l41_otp_unpack to shared code
ASoC: cs35l41: Move power initializations to reg_sequence
ASoC: cs35l41: Create shared function for errata patches
ASoC: cs35l41: Create shared function for setting channels
ASoC: cs35l41: Create shared function for boost configuration
hda: cs35l41: Add support for CS35L41 in HDA systems
ACPI / scan: Create platform device for CLSA0100 and CSC3551 ACPI
nodes
ALSA: hda/realtek: Add support for Legion 7 16ACHg6 laptop
Stefan Binding (1):
ALSA: hda/realtek: Add CS35L41 support for Thinkpad laptops
MAINTAINERS | 2 +
drivers/acpi/scan.c | 3 +
drivers/platform/x86/i2c-multi-instantiate.c | 11 +
include/sound/cs35l41.h | 739 ++++++++++++++++++
sound/pci/hda/Kconfig | 29 +
sound/pci/hda/Makefile | 10 +
sound/pci/hda/cs35l41_hda.c | 527 +++++++++++++
sound/pci/hda/cs35l41_hda.h | 69 ++
sound/pci/hda/cs35l41_hda_i2c.c | 66 ++
sound/pci/hda/cs35l41_hda_spi.c | 63 ++
sound/pci/hda/hda_component.h | 20 +
sound/pci/hda/patch_realtek.c | 146 ++++
sound/soc/codecs/Kconfig | 11 +-
sound/soc/codecs/Makefile | 4 +-
sound/soc/codecs/cs35l41-i2c.c | 1 -
.../{cs35l41-tables.c => cs35l41-lib.c} | 355 ++++++++-
sound/soc/codecs/cs35l41-spi.c | 1 -
sound/soc/codecs/cs35l41.c | 360 +--------
sound/soc/codecs/cs35l41.h | 734 -----------------
19 files changed, 2055 insertions(+), 1096 deletions(-)
create mode 100644 sound/pci/hda/cs35l41_hda.c
create mode 100644 sound/pci/hda/cs35l41_hda.h
create mode 100644 sound/pci/hda/cs35l41_hda_i2c.c
create mode 100644 sound/pci/hda/cs35l41_hda_spi.c
create mode 100644 sound/pci/hda/hda_component.h
rename sound/soc/codecs/{cs35l41-tables.c => cs35l41-lib.c} (71%)
--
2.34.1
ASoC and HDA will do the same cs35l41_otp_unpack, so move it
to shared code
Signed-off-by: Lucas Tanure <[email protected]>
---
include/sound/cs35l41.h | 4 +-
sound/soc/codecs/cs35l41-lib.c | 121 ++++++++++++++++++++++++++++++-
sound/soc/codecs/cs35l41.c | 125 +--------------------------------
3 files changed, 122 insertions(+), 128 deletions(-)
diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index aac3ffb9bc89..6cf3ef02b26a 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -534,7 +534,6 @@
#define CS35L41_MAX_CACHE_REG 36
#define CS35L41_OTP_SIZE_WORDS 32
#define CS35L41_NUM_OTP_ELEM 100
-#define CS35L41_NUM_OTP_MAPS 5
#define CS35L41_VALID_PDATA 0x80000000
#define CS35L41_NUM_SUPPLIES 2
@@ -760,8 +759,9 @@ struct cs35l41_otp_map_element_t {
u32 word_offset;
};
-extern const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS];
extern struct regmap_config cs35l41_regmap_i2c;
extern struct regmap_config cs35l41_regmap_spi;
+int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap);
+
#endif /* __CS35L41_H */
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index f19531ebf729..dc5f502447a2 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -7,8 +7,11 @@
// Author: David Rhodes <[email protected]>
// Author: Lucas Tanure <[email protected]>
+#include <linux/dev_printk.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/cs35l41.h>
@@ -655,7 +658,7 @@ static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM]
{ 0x00017044, 0, 24 }, /*LOT_NUMBER*/
};
-const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = {
+static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = {
{
.id = 0x01,
.map = otp_map_1,
@@ -692,7 +695,6 @@ const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]
.word_offset = 2,
},
};
-EXPORT_SYMBOL_GPL(cs35l41_otp_map_map);
struct regmap_config cs35l41_regmap_i2c = {
.reg_bits = 32,
@@ -727,6 +729,121 @@ struct regmap_config cs35l41_regmap_spi = {
};
EXPORT_SYMBOL_GPL(cs35l41_regmap_spi);
+static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
+ if (cs35l41_otp_map_map[i].id == otp_id)
+ return &cs35l41_otp_map_map[i];
+ }
+
+ return NULL;
+}
+
+int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap)
+{
+ const struct cs35l41_otp_map_element_t *otp_map_match;
+ const struct cs35l41_otp_packed_element_t *otp_map;
+ int bit_offset, word_offset, ret, i;
+ unsigned int bit_sum = 8;
+ u32 otp_val, otp_id_reg;
+ u32 *otp_mem;
+
+ otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL);
+ if (!otp_mem)
+ return -ENOMEM;
+
+ ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg);
+ if (ret) {
+ dev_err(dev, "Read OTP ID failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map_match = cs35l41_find_otp_map(otp_id_reg);
+
+ if (!otp_map_match) {
+ dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg);
+ ret = -EINVAL;
+ goto err_otp_unpack;
+ }
+
+ ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS);
+ if (ret) {
+ dev_err(dev, "Read OTP Mem failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ otp_map = otp_map_match->map;
+
+ bit_offset = otp_map_match->bit_offset;
+ word_offset = otp_map_match->word_offset;
+
+ ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000055);
+ if (ret) {
+ dev_err(dev, "Write Unlock key failed 1/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000AA);
+ if (ret) {
+ dev_err(dev, "Write Unlock key failed 2/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+
+ for (i = 0; i < otp_map_match->num_elements; i++) {
+ dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n",
+ bit_offset, word_offset, bit_sum % 32);
+ if (bit_offset + otp_map[i].size - 1 >= 32) {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(31, bit_offset)) >> bit_offset;
+ otp_val |= (otp_mem[++word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 33, 0)) <<
+ (32 - bit_offset);
+ bit_offset += otp_map[i].size - 32;
+ } else {
+ otp_val = (otp_mem[word_offset] &
+ GENMASK(bit_offset + otp_map[i].size - 1, bit_offset)
+ ) >> bit_offset;
+ bit_offset += otp_map[i].size;
+ }
+ bit_sum += otp_map[i].size;
+
+ if (bit_offset == 32) {
+ bit_offset = 0;
+ word_offset++;
+ }
+
+ if (otp_map[i].reg != 0) {
+ ret = regmap_update_bits(regmap, otp_map[i].reg,
+ GENMASK(otp_map[i].shift + otp_map[i].size - 1,
+ otp_map[i].shift),
+ otp_val << otp_map[i].shift);
+ if (ret < 0) {
+ dev_err(dev, "Write OTP val failed: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ }
+ }
+
+ ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x000000CC);
+ if (ret) {
+ dev_err(dev, "Write Lock key failed 1/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = regmap_write(regmap, CS35L41_TEST_KEY_CTL, 0x00000033);
+ if (ret) {
+ dev_err(dev, "Write Lock key failed 2/2: %d\n", ret);
+ goto err_otp_unpack;
+ }
+ ret = 0;
+
+err_otp_unpack:
+ kfree(otp_mem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs35l41_otp_unpack);
+
MODULE_DESCRIPTION("CS35L41 library");
MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <[email protected]>");
MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <[email protected]>");
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 60332eae1162..aa57c59b334d 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -14,7 +14,6 @@
#include <linux/moduleparam.h>
#include <linux/of_device.h>
#include <linux/property.h>
-#include <linux/slab.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -420,128 +419,6 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
WM_ADSP_FW_CONTROL("DSP1", 0),
};
-static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) {
- if (cs35l41_otp_map_map[i].id == otp_id)
- return &cs35l41_otp_map_map[i];
- }
-
- return NULL;
-}
-
-static int cs35l41_otp_unpack(void *data)
-{
- const struct cs35l41_otp_map_element_t *otp_map_match;
- const struct cs35l41_otp_packed_element_t *otp_map;
- struct cs35l41_private *cs35l41 = data;
- int bit_offset, word_offset, ret, i;
- unsigned int bit_sum = 8;
- u32 otp_val, otp_id_reg;
- u32 *otp_mem;
-
- otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL);
- if (!otp_mem)
- return -ENOMEM;
-
- ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Read OTP ID failed: %d\n", ret);
- goto err_otp_unpack;
- }
-
- otp_map_match = cs35l41_find_otp_map(otp_id_reg);
-
- if (!otp_map_match) {
- dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n",
- otp_id_reg);
- ret = -EINVAL;
- goto err_otp_unpack;
- }
-
- ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem,
- CS35L41_OTP_SIZE_WORDS);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Read OTP Mem failed: %d\n", ret);
- goto err_otp_unpack;
- }
-
- otp_map = otp_map_match->map;
-
- bit_offset = otp_map_match->bit_offset;
- word_offset = otp_map_match->word_offset;
-
- ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Write Unlock key failed 1/2: %d\n", ret);
- goto err_otp_unpack;
- }
- ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Write Unlock key failed 2/2: %d\n", ret);
- goto err_otp_unpack;
- }
-
- for (i = 0; i < otp_map_match->num_elements; i++) {
- dev_dbg(cs35l41->dev,
- "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n",
- bit_offset, word_offset, bit_sum % 32);
- if (bit_offset + otp_map[i].size - 1 >= 32) {
- otp_val = (otp_mem[word_offset] &
- GENMASK(31, bit_offset)) >>
- bit_offset;
- otp_val |= (otp_mem[++word_offset] &
- GENMASK(bit_offset +
- otp_map[i].size - 33, 0)) <<
- (32 - bit_offset);
- bit_offset += otp_map[i].size - 32;
- } else {
- otp_val = (otp_mem[word_offset] &
- GENMASK(bit_offset + otp_map[i].size - 1,
- bit_offset)) >> bit_offset;
- bit_offset += otp_map[i].size;
- }
- bit_sum += otp_map[i].size;
-
- if (bit_offset == 32) {
- bit_offset = 0;
- word_offset++;
- }
-
- if (otp_map[i].reg != 0) {
- ret = regmap_update_bits(cs35l41->regmap,
- otp_map[i].reg,
- GENMASK(otp_map[i].shift +
- otp_map[i].size - 1,
- otp_map[i].shift),
- otp_val << otp_map[i].shift);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Write OTP val failed: %d\n",
- ret);
- goto err_otp_unpack;
- }
- }
- }
-
- ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Write Lock key failed 1/2: %d\n", ret);
- goto err_otp_unpack;
- }
- ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033);
- if (ret < 0) {
- dev_err(cs35l41->dev, "Write Lock key failed 2/2: %d\n", ret);
- goto err_otp_unpack;
- }
- ret = 0;
-
-err_otp_unpack:
- kfree(otp_mem);
- return ret;
-}
-
static irqreturn_t cs35l41_irq(int irq, void *data)
{
struct cs35l41_private *cs35l41 = data;
@@ -1667,7 +1544,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41,
goto err;
}
- ret = cs35l41_otp_unpack(cs35l41);
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
if (ret < 0) {
dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
goto err;
--
2.34.1
On Fri, 17 Dec 2021 11:56:58 +0000, Lucas Tanure wrote:
> Add support for laptops that have CS35L41 connected to an HDA
> codec by I2S and direct I2C connection to the CPU.
>
> Laptops that use CS35L41 and are SPI will be added in the future,
> after the support for it is resolved at i2c-multi-instantiate driver.
> i2c-multi-instantiate thread: https://lkml.org/lkml/2021/12/10/557
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/10] ASoC: cs35l41: Convert tables to shared source code
commit: a87d42227cf5614fe0040ddd1fe642c54298b42c
[02/10] ASoC: cs35l41: Move cs35l41_otp_unpack to shared code
commit: fe120d4cb6f6cd03007239e7c578b8703fe6d336
[03/10] ASoC: cs35l41: Move power initializations to reg_sequence
commit: 062ce0593315e22aac527389dd6dd4328c49f0fb
[04/10] ASoC: cs35l41: Create shared function for errata patches
commit: 8b2278604b6de27329ec7ed82ca696c4751111b6
[05/10] ASoC: cs35l41: Create shared function for setting channels
commit: 3bc3e3da657f17c14df8ae8fab58183407bd7521
[06/10] ASoC: cs35l41: Create shared function for boost configuration
commit: e8e4fcc047c6e0c5411faeb8cc29aed2e5036a00
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
On Fri, 31 Dec 2021 15:39:54 +0100,
Mark Brown wrote:
>
> On Fri, 17 Dec 2021 11:56:58 +0000, Lucas Tanure wrote:
> > Add support for laptops that have CS35L41 connected to an HDA
> > codec by I2S and direct I2C connection to the CPU.
> >
> > Laptops that use CS35L41 and are SPI will be added in the future,
> > after the support for it is resolved at i2c-multi-instantiate driver.
> > i2c-multi-instantiate thread: https://lkml.org/lkml/2021/12/10/557
> >
> > [...]
>
> Applied to
>
> https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
>
> Thanks!
>
> [01/10] ASoC: cs35l41: Convert tables to shared source code
> commit: a87d42227cf5614fe0040ddd1fe642c54298b42c
> [02/10] ASoC: cs35l41: Move cs35l41_otp_unpack to shared code
> commit: fe120d4cb6f6cd03007239e7c578b8703fe6d336
> [03/10] ASoC: cs35l41: Move power initializations to reg_sequence
> commit: 062ce0593315e22aac527389dd6dd4328c49f0fb
> [04/10] ASoC: cs35l41: Create shared function for errata patches
> commit: 8b2278604b6de27329ec7ed82ca696c4751111b6
> [05/10] ASoC: cs35l41: Create shared function for setting channels
> commit: 3bc3e3da657f17c14df8ae8fab58183407bd7521
> [06/10] ASoC: cs35l41: Create shared function for boost configuration
> commit: e8e4fcc047c6e0c5411faeb8cc29aed2e5036a00
Mark, could you send a PR including those for 5.17?
The rest HD-audio part of the patch set depends on this new ASoC codec
stuff (at least Kconfig), so I can't apply the patches before merging
those. The ACPI patch might be still not applicable through my tree,
but it can be taken independently.
thanks,
Takashi
On Tue, 04 Jan 2022 14:07:51 +0100,
Takashi Iwai wrote:
>
> On Fri, 31 Dec 2021 15:39:54 +0100,
> Mark Brown wrote:
> >
> > On Fri, 17 Dec 2021 11:56:58 +0000, Lucas Tanure wrote:
> > > Add support for laptops that have CS35L41 connected to an HDA
> > > codec by I2S and direct I2C connection to the CPU.
> > >
> > > Laptops that use CS35L41 and are SPI will be added in the future,
> > > after the support for it is resolved at i2c-multi-instantiate driver.
> > > i2c-multi-instantiate thread: https://lkml.org/lkml/2021/12/10/557
> > >
> > > [...]
> >
> > Applied to
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
> >
> > Thanks!
> >
> > [01/10] ASoC: cs35l41: Convert tables to shared source code
> > commit: a87d42227cf5614fe0040ddd1fe642c54298b42c
> > [02/10] ASoC: cs35l41: Move cs35l41_otp_unpack to shared code
> > commit: fe120d4cb6f6cd03007239e7c578b8703fe6d336
> > [03/10] ASoC: cs35l41: Move power initializations to reg_sequence
> > commit: 062ce0593315e22aac527389dd6dd4328c49f0fb
> > [04/10] ASoC: cs35l41: Create shared function for errata patches
> > commit: 8b2278604b6de27329ec7ed82ca696c4751111b6
> > [05/10] ASoC: cs35l41: Create shared function for setting channels
> > commit: 3bc3e3da657f17c14df8ae8fab58183407bd7521
> > [06/10] ASoC: cs35l41: Create shared function for boost configuration
> > commit: e8e4fcc047c6e0c5411faeb8cc29aed2e5036a00
>
> Mark, could you send a PR including those for 5.17?
> The rest HD-audio part of the patch set depends on this new ASoC codec
> stuff (at least Kconfig), so I can't apply the patches before merging
> those. The ACPI patch might be still not applicable through my tree,
> but it can be taken independently.
Now I merged Mark's asoc tree, and applied the patches 7, 9 and 10.
ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems
ALSA: hda/realtek: Add support for Legion 7 16ACHg6 laptop
ALSA: hda/realtek: Add CS35L41 support for Thinkpad laptops
The patches 9 and 10 have been slightly modified to adjust the quirk
entry positions.
The only missing patch is the ACPI patch. I'm open in which way to
take; it's fine to be applied via other trees.
Let me know your favorite.
thanks,
Takashi