]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: hda/ca0132: add QUIRK_GENERIC path for Gigabyte GA-Z170X-Gaming G1
authorEzio Galeazzi <eziogale@gmail.com>
Sun, 17 May 2026 15:42:36 +0000 (17:42 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 19 May 2026 05:34:50 +0000 (07:34 +0200)
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.
This patch applies against v6.18.24.

Signed-off-by: Ezio Galeazzi <eziogale@gmail.com>
Link: https://lore.kernel.org/0c0b781f-1595-4595-921a-66d83cf5930b@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/hda/codecs/Kconfig
sound/hda/codecs/ca0132.c

index addbc94243365b69cc43afd8065f18a9c4b9e92d..dcf340e5a0c1adf98f5a2e9e96bcf4f07984dafe 100644 (file)
@@ -69,6 +69,7 @@ comment "Set to Y if you want auto-loading the codec driver"
 
 config SND_HDA_CODEC_CA0132
        tristate "Build Creative CA0132 codec support"
+       select SND_HDA_GENERIC
        help
          Say Y or M here to include Creative CA0132 codec support in
          snd-hda-intel driver.
index ad533b04ab29cf277ef86b8a9129beefde39b4ed..7d165f2dbd41909833fabed1f4d705b0006ce5fe 100644 (file)
@@ -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 @@ enum dsp_download_state {
  */
 
 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;
@@ -1174,6 +1177,7 @@ enum {
        QUIRK_R3D,
        QUIRK_AE5,
        QUIRK_AE7,
+       QUIRK_GENERIC,
        QUIRK_NONE = HDA_FIXUP_ID_NOT_SET,
 };
 
@@ -1292,6 +1296,20 @@ static const struct hda_pintbl ae7_pincfgs[] = {
        {}
 };
 
+static const struct hda_pintbl ca0132_generic_pincfgs[] = {
+       { 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 },
+       {}
+};
+
 static const struct hda_quirk ca0132_quirks[] = {
        SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4),
        SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
@@ -1304,6 +1322,7 @@ static const struct hda_quirk ca0132_quirks[] = {
        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,6 +1344,7 @@ static const struct hda_model_fixup ca0132_quirk_models[] = {
        { .id = QUIRK_R3D, .name = "r3d" },
        { .id = QUIRK_AE5, .name = "ae5" },
        { .id = QUIRK_AE7, .name = "ae7" },
+       { .id = QUIRK_GENERIC, .name = "generic" },
        {}
 };
 
@@ -9879,14 +9899,57 @@ static void sbz_detect_quirk(struct hda_codec *codec)
        }
 }
 
+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 int ca0132_generic_probe(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+       int err;
+
+       snd_hda_gen_spec_init(&spec->gen);
+
+       snd_hda_apply_pincfgs(codec, ca0132_generic_pincfgs);
+
+       ca0132_init_chip(codec);
+
+       err = ca0132_prepare_verbs(codec);
+       if (err < 0)
+               return err;
+
+       err = snd_hda_parse_pin_def_config(codec, cfg, NULL);
+       if (err < 0)
+               return err;
+       err = snd_hda_gen_parse_auto_config(codec, cfg);
+       if (err < 0)
+               return err;
+
+       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;
+}
+
 static void ca0132_codec_remove(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
-       if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+       switch (ca0132_quirk(spec)) {
+       case QUIRK_GENERIC:
+               snd_hda_gen_remove(codec);
+               return;
+       case QUIRK_ZXR_DBPRO:
                return dbpro_free(codec);
-       else
+       default:
                return ca0132_free(codec);
+       }
 }
 
 static int ca0132_codec_probe(struct hda_codec *codec,
@@ -9903,14 +9966,21 @@ static int ca0132_codec_probe(struct hda_codec *codec,
        codec->spec = spec;
        spec->codec = codec;
 
-       /* Detect codec quirk */
-       snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL);
-       if (ca0132_quirk(spec) == QUIRK_SBZ)
-               sbz_detect_quirk(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);
+       switch (ca0132_quirk(spec)) {
+       case QUIRK_SBZ:
+               sbz_detect_quirk(codec);
+               break;
+       case QUIRK_GENERIC:
+               return ca0132_generic_probe(codec);
+       default:
+               break;
+       }
 
        spec->dsp_state = DSP_DOWNLOAD_INIT;
        spec->num_mixers = 1;
@@ -10011,36 +10081,51 @@ static int ca0132_codec_build_controls(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
-       if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+       switch (ca0132_quirk(spec)) {
+       case QUIRK_GENERIC:
+               return snd_hda_gen_build_controls(codec);
+       case QUIRK_ZXR_DBPRO:
                return dbpro_build_controls(codec);
-       else
+       default:
                return ca0132_build_controls(codec);
+       }
 }
 
 static int ca0132_codec_build_pcms(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
-       if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+       switch (ca0132_quirk(spec)) {
+       case QUIRK_GENERIC:
+               return snd_hda_gen_build_pcms(codec);
+       case QUIRK_ZXR_DBPRO:
                return dbpro_build_pcms(codec);
-       else
+       default:
                return ca0132_build_pcms(codec);
+       }
 }
 
 static int ca0132_codec_init(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
-       if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+       switch (ca0132_quirk(spec)) {
+       case QUIRK_GENERIC:
+               return snd_hda_gen_init(codec);
+       case QUIRK_ZXR_DBPRO:
                return dbpro_init(codec);
-       else
+       default:
                return ca0132_init(codec);
+       }
 }
 
 static int ca0132_codec_suspend(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
+       if (ca0132_quirk(spec) == QUIRK_GENERIC)
+               return 0;
+
        cancel_delayed_work_sync(&spec->unsol_hp_work);
        return 0;
 }