ca0132: add generic parser fallback for broken DSP path

"[email protected]" <[email protected]>
Newsgroups org.alsa-project.alsa-devel,org.kernel.vger.linux-sound
Message-ID <[email protected]>
Some CA0132 implementations (e.g. Gigabyte GA-Z170X-Gaming G1)
produce white noise when using the DSP firmware path. Add a
QUIRK_GENERIC path that uses the standard HDA generic parser
instead, with custom pin configs that disable the broken auto-mute
from HP jack detection.

Also applies VREF_50 on the mic pin and adds an init_hook for
suspend/resume stability.

Kernel version: 6.18.24
The PCI SSID this was tested on: `1458:a046`
it's been tested with PulseAudio and speaker-test
That the DSP path (QUIRK_SBZ) remains unchanged for cards that work with it.

Thanks
ca0132-generic-parser.patch (text/x-patch, 5 KB)
--- /home/ezio/tmp/kernel/linux-6.18.24/sound/hda/codecs/ca0132.c.0	2026-05-02 09:19:21.217137158 +0200
+++ /home/ezio/tmp/kernel/linux-6.18.24/sound/hda/codecs/ca0132.c	2026-05-10 11:21:41.933355825 +0200
@@ -24,6 +24,7 @@
 #include "hda_local.h"
 #include "hda_auto_parser.h"
 #include "hda_jack.h"
+#include "generic.h"
 
 #include "ca0132_regs.h"
 
@@ -1060,6 +1061,8 @@
  */
 
 struct ca0132_spec {
+	struct hda_gen_spec gen;
+
 	const struct snd_kcontrol_new *mixers[5];
 	unsigned int num_mixers;
 	const struct hda_verb *base_init_verbs;
@@ -1175,6 +1178,7 @@
 	QUIRK_AE5,
 	QUIRK_AE7,
 	QUIRK_NONE = HDA_FIXUP_ID_NOT_SET,
+	QUIRK_GENERIC,
 };
 
 #ifdef CONFIG_PCI
@@ -1304,6 +1308,7 @@
 	SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
 	SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
 	SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
+	SND_PCI_QUIRK(0x1458, 0xA046, "Gigabyte GA-Z170X-Gaming G1", QUIRK_GENERIC),
 	SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI),
 	SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI),
 	SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI),
@@ -1325,11 +1330,13 @@
 	{ .id = QUIRK_R3D, .name = "r3d" },
 	{ .id = QUIRK_AE5, .name = "ae5" },
 	{ .id = QUIRK_AE7, .name = "ae7" },
+	{ .id = QUIRK_GENERIC, .name = "generic" },
 	{}
 };
 
 /* Output selection quirk info structures. */
 #define MAX_QUIRK_MMIO_GPIO_SET_VALS 3
+
 #define MAX_QUIRK_SCP_SET_VALS 2
 struct ca0132_alt_out_set_info {
 	unsigned int dac2port; /* ParamID 0x0d value. */
@@ -9821,7 +9828,7 @@
 static int ca0132_prepare_verbs(struct hda_codec *codec)
 {
 /* Verbs + terminator (an empty element) */
-#define NUM_SPEC_VERBS 2
+#define NUM_SPEC_VERBS 3
 	struct ca0132_spec *spec = codec->spec;
 
 	spec->chip_init_verbs = ca0132_init_verbs0;
@@ -9842,6 +9849,11 @@
 	spec->spec_init_verbs[0].param = 0x78D;
 	spec->spec_init_verbs[0].verb = 0x00;
 
+	/* VREF_50 for mic pin */
+	spec->spec_init_verbs[1].nid = 0x12;
+	spec->spec_init_verbs[1].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+	spec->spec_init_verbs[1].param = 0x21; /* IN | VREF_50 */
+
 	/* Previously commented configuration */
 	/*
 	spec->spec_init_verbs[2].nid = 0x0b;
@@ -9882,10 +9894,20 @@
 	}
 }
 
+static void ca0132_generic_init_hook(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	snd_hda_sequence_write(codec, spec->spec_init_verbs);
+}
+
 static void ca0132_codec_remove(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec = codec->spec;
 
+	if (ca0132_quirk(spec) == QUIRK_GENERIC) {
+		snd_hda_gen_remove(codec);
+		return;
+	}
 	if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
 		return dbpro_free(codec);
 	else
@@ -9906,14 +9928,55 @@
 	codec->spec = spec;
 	spec->codec = codec;
 
+	/* These must be set before any path is taken */
+	codec->pcm_format_first = 1;
+	codec->no_sticky_stream = 1;
+
 	/* Detect codec quirk */
 	snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL);
 	if (ca0132_quirk(spec) == QUIRK_SBZ)
 		sbz_detect_quirk(codec);
 
-	codec->pcm_format_first = 1;
-	codec->no_sticky_stream = 1;
+	if (ca0132_quirk(spec) == QUIRK_GENERIC) {
+		struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+		snd_hda_gen_spec_init(&spec->gen);
+
+		snd_hda_apply_pincfgs(codec,
+			(const struct hda_pintbl[]) {
+				{ 0x0b, 0x41014111 },
+				{ 0x0c, 0x414520f0 }, /* SPDIF out */
+				{ 0x0d, 0x01014010 }, /* lineout */
+				{ 0x0e, 0x41c501f0 },
+				{ 0x0f, 0x411111f0 }, /* disabled */
+				{ 0x10, 0x411111f0 }, /* disabled */
+				{ 0x11, 0x41012014 },
+				{ 0x12, 0x37a790f0 }, /* mic */
+				{ 0x13, 0x77a701f0 },
+				{ 0x18, 0x500000f0 },
+				{}
+			});
 
+		ca0132_init_chip(codec);
+
+		err = ca0132_prepare_verbs(codec);
+		if (err < 0)
+			goto error;
+
+		err = snd_hda_parse_pin_def_config(codec, cfg, NULL);
+		if (err < 0)
+			goto error;
+		err = snd_hda_gen_parse_auto_config(codec, cfg);
+		if (err < 0)
+			goto error;
+
+		spec->gen.init_hook = ca0132_generic_init_hook;
+		spec->gen.automute_speaker = 0;
+		spec->gen.automute_lo = 0;
+
+		snd_hda_sequence_write(codec, spec->spec_init_verbs);
+		return 0;
+	}
 
 	spec->dsp_state = DSP_DOWNLOAD_INIT;
 	spec->num_mixers = 1;
@@ -10014,6 +10077,10 @@
 {
 	struct ca0132_spec *spec = codec->spec;
 
+	if (ca0132_quirk(spec) == QUIRK_GENERIC) {
+		return snd_hda_gen_build_controls(codec);
+	}
+
 	if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
 		return dbpro_build_controls(codec);
 	else
@@ -10024,6 +10091,10 @@
 {
 	struct ca0132_spec *spec = codec->spec;
 
+	if (ca0132_quirk(spec) == QUIRK_GENERIC) {
+		return snd_hda_gen_build_pcms(codec);
+	}
+
 	if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
 		return dbpro_build_pcms(codec);
 	else
@@ -10034,6 +10105,10 @@
 {
 	struct ca0132_spec *spec = codec->spec;
 
+	if (ca0132_quirk(spec) == QUIRK_GENERIC) {
+		return snd_hda_gen_init(codec);
+	}
+
 	if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
 		return dbpro_init(codec);
 	else
@@ -10044,6 +10119,9 @@
 {
 	struct ca0132_spec *spec = codec->spec;
 
+	if (ca0132_quirk(spec) == QUIRK_GENERIC)
+		return 0;
+
 	cancel_delayed_work_sync(&spec->unsol_hp_work);
 	return 0;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.