[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20260112142850.243054-1-rf@opensource.cirrus.com>
Date: Mon, 12 Jan 2026 14:28:50 +0000
From: Richard Fitzgerald <rf@...nsource.cirrus.com>
To: tiwai@...e.com
Cc: linux-sound@...r.kernel.org, linux-kernel@...r.kernel.org,
patches@...nsource.cirrus.com
Subject: [PATCH] ALSA: hda/cs8409: Add quirk for CDB35L56-FOUR-HD
From: Simon Trimmer <simont@...nsource.cirrus.com>
Adds quirkiness for the Cirrus Logic CDB35L56-FOUR-HD board.
The quirk must be forced by model name "CDB35L56-FOUR-HD"
because there isn't a unique SSID that can be used. For example
in /etc/modprobe.d:
options snd-hda-intel model="CDB35L56-FOUR-HD"
The CDB35L56-FOUR-HD is not a complete PC. It is an add-on audio
board that requires a host system and replaces the normal HDA codec
on the host. Because of this there isn't an SSID that uniquely
identifies this configuration. Also, the usual host board is an
Aaeon UpXtreme, which doesn't have a unique SSID.
Because of this, the quirk must be forced by a module param.
This is acceptable because it is a development board, not an
end-user system, so there is no need for it to be detected
automatically.
Signed-off-by: Simon Trimmer <simont@...nsource.cirrus.com>
Co-developed-by: Richard Fitzgerald <rf@...nsource.cirrus.com>
Signed-off-by: Richard Fitzgerald <rf@...nsource.cirrus.com>
---
sound/hda/codecs/cirrus/Kconfig | 1 +
sound/hda/codecs/cirrus/cs8409-tables.c | 76 ++++++++++-
sound/hda/codecs/cirrus/cs8409.c | 172 ++++++++++++++++++++++++
sound/hda/codecs/cirrus/cs8409.h | 13 ++
4 files changed, 261 insertions(+), 1 deletion(-)
diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig
index ec6cbcaf64f0..d7a1b619d243 100644
--- a/sound/hda/codecs/cirrus/Kconfig
+++ b/sound/hda/codecs/cirrus/Kconfig
@@ -34,6 +34,7 @@ comment "Set to Y if you want auto-loading the codec driver"
config SND_HDA_CODEC_CS8409
tristate "Build Cirrus Logic HDA bridge support"
select SND_HDA_GENERIC
+ select SND_HDA_SCODEC_COMPONENT
help
Say Y or M here to include Cirrus Logic HDA bridge support
such as CS8409.
diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c
index 8c703b714a71..b9ec8fb8eab7 100644
--- a/sound/hda/codecs/cirrus/cs8409-tables.c
+++ b/sound/hda/codecs/cirrus/cs8409-tables.c
@@ -468,6 +468,70 @@ struct sub_codec dolphin_cs42l42_1 = {
.no_type_dect = 1,
};
+/******************************************************************************
+ * CDB35L56-FOUR-HD Specific Arrays
+ ******************************************************************************/
+const struct hda_verb cs8409_cdb35l56_four_init_verbs[] = {
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cdb35l56_four_pincfgs[] = {
+ /* 0xPPLLLLLLDDDDTTTTCCCCMMMMAAAASSSS
+ * P = PCON: AC_JACK_PORT_*
+ * L = LOC: AC_JACK_LOC_*
+ * D = DD: device type AC_JACK_*
+ * T = CTYP: AC_JACK_CONN_*
+ * C = COL: AC_JACK_COLOR_*
+ * M = MISC: ?
+ * A = DA: AC_DEFCFG_DEF_ASSOC
+ * S = SEQ: Sequence number in DA group
+ */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ /* "Mic" */
+ { CS8409_PIN_ASP2_RECEIVER_A, 0x04a12050 }, /* ASP-2-RX */
+ {} /* terminator */
+};
+
+const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1/2_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 },
+ /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 },
+ /* ASP2.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL1, 0x0800 },
+ /* ASP2.A: RX.RAP=1, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL2, 0x2800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP2: LCHI=1Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f },
+ /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f },
+ /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1/2_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, /* TX2.A: pre-scale att.=0 dB */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 },
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 },
+ {} /* Terminator */
+};
+
/******************************************************************************
* CS8409 Patch Driver Structs
* Arrays Used for all projects using CS8409
@@ -557,7 +621,6 @@ const struct hda_quirk cs8409_fixup_tbl[] = {
{} /* terminator */
};
-/* Dell Inspiron models with cs8409/cs42l42 */
const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_BULLSEYE, .name = "bullseye" },
{ .id = CS8409_WARLOCK, .name = "warlock" },
@@ -566,6 +629,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
{ .id = CS8409_ODIN, .name = "odin" },
+ { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" },
{}
};
@@ -620,4 +684,14 @@ const struct hda_fixup cs8409_fixups[] = {
.chained = true,
.chain_id = CS8409_FIXUPS,
},
+ [CS8409_CDB35L56_FOUR_HD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cdb35l56_four_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_CDB35L56_FOUR_HD_FIXUP,
+ },
+ [CS8409_CDB35L56_FOUR_HD_FIXUP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cdb35l56_four_autodet_fixup,
+ },
};
diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c
index 2c02d3be89ee..61b6a15d6291 100644
--- a/sound/hda/codecs/cirrus/cs8409.c
+++ b/sound/hda/codecs/cirrus/cs8409.c
@@ -6,14 +6,19 @@
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <linux/mutex.h>
#include <linux/iopoll.h>
#include "cs8409.h"
+#include "../side-codecs/hda_component.h"
/******************************************************************************
* CS8409 Specific Functions
@@ -1216,6 +1221,172 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
}
}
+static int cs8409_comp_bind(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct cs8409_spec *spec = codec->spec;
+
+ return hda_component_manager_bind(codec, &spec->comps);
+}
+
+static void cs8409_comp_unbind(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct cs8409_spec *spec = codec->spec;
+
+ hda_component_manager_unbind(codec, &spec->comps);
+}
+
+static const struct component_master_ops cs8409_comp_master_ops = {
+ .bind = cs8409_comp_bind,
+ .unbind = cs8409_comp_unbind,
+};
+
+static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ hda_component_manager_playback_hook(&spec->comps, action);
+}
+
+static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg;
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+}
+
+static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !spec->speaker_muted;
+
+ return 0;
+}
+
+static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ bool muted = !ucontrol->value.integer.value[0];
+
+ if (muted == spec->speaker_muted)
+ return 0;
+
+ spec->speaker_muted = muted;
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_get,
+ .put = cs8409_spk_sw_put,
+};
+
+void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct device *dev = hda_codec_dev(codec);
+ struct cs8409_spec *spec = codec->spec;
+ struct acpi_device *adev;
+ const char *bus = NULL;
+ static const struct {
+ const char *hid;
+ const char *name;
+ } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" },
+ { "CSC3556", "cs35l56-hda" },
+ { "CSC3557", "cs35l57-hda" }};
+ char *match;
+ int i, count = 0, count_devindex = 0;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE: {
+ for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) {
+ adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1);
+ if (adev)
+ break;
+ }
+ if (!adev) {
+ dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n");
+ return;
+ }
+
+ count = i2c_acpi_client_count(adev);
+ if (count > 0) {
+ bus = "i2c";
+ } else {
+ count = acpi_spi_count_resources(adev);
+ if (count > 0)
+ bus = "spi";
+ }
+
+ struct fwnode_handle *fwnode __free(fwnode_handle) =
+ fwnode_handle_get(acpi_fwnode_handle(adev));
+ acpi_dev_put(adev);
+
+ if (!bus) {
+ dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ if (!fwnode) {
+ dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ /*
+ * When available the cirrus,dev-index property is an accurate
+ * count of the amps in a system and is used in preference to
+ * the count of bus devices that can contain additional address
+ * alias entries.
+ */
+ count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index");
+ if (count_devindex > 0)
+ count = count_devindex;
+
+ match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name);
+ if (!match)
+ return;
+ dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match);
+
+ ret = hda_component_manager_init(codec, &spec->comps, count, bus,
+ acpi_ids[i].hid, match,
+ &cs8409_comp_master_ops);
+ if (ret)
+ return;
+
+ spec->gen.pcm_playback_hook = cs8409_comp_playback_hook;
+
+ snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs);
+ snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs);
+ break;
+ }
+ case HDA_FIXUP_ACT_PROBE:
+ spec->speaker_muted = 0; /* speakers begin enabled */
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_component_ctrl);
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ snd_hda_codec_set_name(codec, "CS8409/CS35L56");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cs8409_cdb35l56_four_hw_init(codec);
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops);
+ break;
+ }
+}
+
/******************************************************************************
* Dolphin Specific Functions
* CS8409/ 2 X CS42L42
@@ -1473,3 +1644,4 @@ module_hda_codec_driver(cs8409_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h
index 7fe56f4a73bc..be1714a84fff 100644
--- a/sound/hda/codecs/cirrus/cs8409.h
+++ b/sound/hda/codecs/cirrus/cs8409.h
@@ -18,6 +18,7 @@
#include "hda_auto_parser.h"
#include "hda_jack.h"
#include "../generic.h"
+#include "../side-codecs/hda_component.h"
/* CS8409 Specific Definitions */
@@ -271,6 +272,8 @@ enum {
CS8409_DOLPHIN,
CS8409_DOLPHIN_FIXUPS,
CS8409_ODIN,
+ CS8409_CDB35L56_FOUR_HD,
+ CS8409_CDB35L56_FOUR_HD_FIXUP,
};
enum {
@@ -341,12 +344,17 @@ struct cs8409_spec {
unsigned int capture_started:1;
unsigned int init_done:1;
unsigned int build_ctrl_done:1;
+ unsigned int speaker_muted:1;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
unsigned int *res);
/* unsol_event op override */
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+
+ /* component binding */
+ struct component_match *match;
+ struct hda_component_parent comps;
};
extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
@@ -374,4 +382,9 @@ extern struct sub_codec dolphin_cs42l42_1;
void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+extern const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[];
+extern const struct hda_verb cs8409_cdb35l56_four_init_verbs[];
+void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, const struct hda_fixup *fix,
+ int action);
+
#endif
--
2.47.3
Powered by blists - more mailing lists