ALSA: hda: cs35l56: Add support for speaker id
authorRichard Fitzgerald <rf@opensource.cirrus.com>
Mon, 18 Sep 2023 09:51:28 +0000 (10:51 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 18 Sep 2023 15:49:26 +0000 (17:49 +0200)
Add handling of the "spk-id-gpios" _DSD property. If present, the
value indicated by the GPIOs is appended to the subsystem-id
part of the firmware name to load the appropriate tunings for that
speaker.

Some manufacturers use multiple sources of speakers, which need
different tunings for best performance. On these models the type
of speaker fitted is indicated by the values of one or more GPIOs.
The number formed by the GPIOs identifies the tuning required.

The speaker ID is only used in combination with a _SUB identifier
because the value is only meaningful if the exact model is known.

The code to get the speaker ID value has been implemented as a
new library so that the cs35l41_hda driver can be switched in
future to share common code. This library can be extended for
other common functionality shared by Cirrus Logic amp drivers.

Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20230918095129.440-2-rf@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
MAINTAINERS
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/cirrus_scodec.c [new file with mode: 0644]
sound/pci/hda/cirrus_scodec.h [new file with mode: 0644]
sound/pci/hda/cs35l56_hda.c

index 90f13281d29708439ba448d26308109a3cfd747b..23e73d19f3474b3dc32db680844703eb2dfa6bfb 100644 (file)
@@ -4912,6 +4912,7 @@ F:        drivers/spi/spi-cs42l43*
 F:     include/dt-bindings/sound/cs*
 F:     include/linux/mfd/cs42l43*
 F:     include/sound/cs*
+F:     sound/pci/hda/cirrus*
 F:     sound/pci/hda/cs*
 F:     sound/pci/hda/hda_cs_dsp_ctl.*
 F:     sound/soc/codecs/cs*
index 0d7502d6e0604962a999893f369bad52979cb232..2980bfef0a4ce946364538a2da9f218621194c59 100644 (file)
@@ -91,6 +91,9 @@ config SND_HDA_PATCH_LOADER
          start up.  The "patch" file can be specified via patch module
          option, such as patch=hda-init.
 
+config SND_HDA_CIRRUS_SCODEC
+       tristate
+
 config SND_HDA_SCODEC_CS35L41
        tristate
        select SND_HDA_GENERIC
@@ -144,6 +147,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
        select SND_HDA_GENERIC
        select SND_SOC_CS35L56_SHARED
        select SND_HDA_SCODEC_CS35L56
+       select SND_HDA_CIRRUS_SCODEC
        select SND_HDA_CS_DSP_CONTROLS
        help
          Say Y or M here to include CS35L56 amplifier support with
@@ -158,6 +162,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
        select SND_HDA_GENERIC
        select SND_SOC_CS35L56_SHARED
        select SND_HDA_SCODEC_CS35L56
+       select SND_HDA_CIRRUS_SCODEC
        select SND_HDA_CS_DSP_CONTROLS
        help
          Say Y or M here to include CS35L56 amplifier support with
index f00fc9ed6096314f90ccc45797b2078c8e0fd9ec..aa445af0cf9ae4529c0bd2ace484d4a2945119cd 100644 (file)
@@ -28,6 +28,7 @@ snd-hda-codec-via-objs :=     patch_via.o
 snd-hda-codec-hdmi-objs :=     patch_hdmi.o hda_eld.o
 
 # side codecs
+snd-hda-cirrus-scodec-objs :=          cirrus_scodec.o
 snd-hda-scodec-cs35l41-objs :=         cs35l41_hda.o cs35l41_hda_property.o
 snd-hda-scodec-cs35l41-i2c-objs :=     cs35l41_hda_i2c.o
 snd-hda-scodec-cs35l41-spi-objs :=     cs35l41_hda_spi.o
@@ -56,6 +57,7 @@ obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
 obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
 
 # side codecs
+obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o
 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o
 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o
 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o
diff --git a/sound/pci/hda/cirrus_scodec.c b/sound/pci/hda/cirrus_scodec.c
new file mode 100644 (file)
index 0000000..8de3bc7
--- /dev/null
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus side-codecs.
+//
+// Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+//               Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/dev_printk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+
+#include "cirrus_scodec.h"
+
+int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index,
+                                int num_amps, int fixed_gpio_id)
+{
+       struct gpio_desc *speaker_id_desc;
+       int speaker_id = -ENOENT;
+
+       if (fixed_gpio_id >= 0) {
+               dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
+               speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
+               if (IS_ERR(speaker_id_desc)) {
+                       speaker_id = PTR_ERR(speaker_id_desc);
+                       return speaker_id;
+               }
+               speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
+               gpiod_put(speaker_id_desc);
+       } else {
+               int base_index;
+               int gpios_per_amp;
+               int count;
+               int tmp;
+               int i;
+
+               count = gpiod_count(dev, "spk-id");
+               if (count > 0) {
+                       speaker_id = 0;
+                       gpios_per_amp = count / num_amps;
+                       base_index = gpios_per_amp * amp_index;
+
+                       if (count % num_amps)
+                               return -EINVAL;
+
+                       dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
+
+                       for (i = 0; i < gpios_per_amp; i++) {
+                               speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
+                                                                 GPIOD_IN);
+                               if (IS_ERR(speaker_id_desc)) {
+                                       speaker_id = PTR_ERR(speaker_id_desc);
+                                       break;
+                               }
+                               tmp = gpiod_get_value_cansleep(speaker_id_desc);
+                               gpiod_put(speaker_id_desc);
+                               if (tmp < 0) {
+                                       speaker_id = tmp;
+                                       break;
+                               }
+                               speaker_id |= tmp << i;
+                       }
+               }
+       }
+
+       dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+
+       return speaker_id;
+}
+EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, SND_HDA_CIRRUS_SCODEC);
+
+MODULE_DESCRIPTION("HDA Cirrus side-codec library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cirrus_scodec.h b/sound/pci/hda/cirrus_scodec.h
new file mode 100644 (file)
index 0000000..ba2041d
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CIRRUS_SCODEC_H
+#define CIRRUS_SCODEC_H
+
+int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index,
+                                int num_amps, int fixed_gpio_id);
+
+#endif /* CIRRUS_SCODEC_H */
index 87ffe8fbff991f042b862f93deb91659a46777bb..44f5ca0e73e376cd57512153e98b91fd7a94636a 100644 (file)
@@ -16,6 +16,7 @@
 #include <sound/core.h>
 #include <sound/hda_codec.h>
 #include <sound/tlv.h>
+#include "cirrus_scodec.h"
 #include "cs35l56_hda.h"
 #include "hda_component.h"
 #include "hda_cs_dsp_ctl.h"
@@ -869,7 +870,17 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
                         "Read ACPI _SUB failed(%ld): fallback to generic firmware\n",
                         PTR_ERR(sub));
        } else {
-               cs35l56->system_name = sub;
+               ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1);
+               if (ret == -ENOENT) {
+                       cs35l56->system_name = sub;
+               } else if (ret >= 0) {
+                       cs35l56->system_name = kasprintf(GFP_KERNEL, "%s-spkid%d", sub, ret);
+                       kfree(sub);
+                       if (!cs35l56->system_name)
+                               return -ENOMEM;
+               } else {
+                       return ret;
+               }
        }
 
        cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev,
@@ -1025,6 +1036,7 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
 EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
 
 MODULE_DESCRIPTION("CS35L56 HDA Driver");
+MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
 MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
 MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");