]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: hda/realtek: fix LG Gram Style 14 speakers
authorDamien Dagorn <damien.dagorn29@gmail.com>
Fri, 23 Jan 2026 17:14:52 +0000 (18:14 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 27 Jan 2026 08:58:37 +0000 (09:58 +0100)
The LG Gram Style 14 (14Z90RS-G.AD77F, SSID 1854:0490) with Realtek ALC298
shows normal routing and volume changes, but internal speakers stay silent
unless a userland HDA-verb workaround is applied.

Add a dedicated quirk for the LG Gram Style 14 that programs the codec
coefficient sequence used by the known workaround and enables the speaker
amps only during playback.

Tested-by: Damien Dagorn <damien.dagorn29@gmail.com>
Signed-off-by: Damien Dagorn <damien.dagorn29@gmail.com>
Link: https://lore.kernel.org/CAN59QMUhd4kHrkRoJA6VzEr2VKezN2yjHnANaQoZn2-Bnwe3bQ@mail.gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/hda/codecs/realtek/alc269.c

index cf263a164987843a144d8c13b0359c79477166c2..3d5a977bb40a07a4bf2c6e4e7258d451c489c76e 100644 (file)
@@ -1854,6 +1854,163 @@ static void alc298_samsung_v2_init_amps(struct hda_codec *codec,
        spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook;
 }
 
+/* LG Gram Style 14: program vendor coef sequence used by HDA-verb workaround */
+struct alc298_lg_gram_style_seq {
+       unsigned short verb;
+       unsigned short idx;
+       unsigned short val;
+};
+
+static void alc298_lg_gram_style_coef_write(struct hda_codec *codec,
+                                           unsigned int verb,
+                                           unsigned int idx,
+                                           unsigned int val)
+{
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x23);
+       snd_hda_codec_write(codec, 0x20, 0, verb, idx);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0x00);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, val);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb011);
+}
+
+static void alc298_lg_gram_style_run_seq(struct hda_codec *codec,
+                                        const struct alc298_lg_gram_style_seq *seq,
+                                        int seq_size)
+{
+       int i;
+
+       for (i = 0; i < seq_size; i++)
+               alc298_lg_gram_style_coef_write(codec, seq[i].verb,
+                                               seq[i].idx, seq[i].val);
+}
+
+/* Coef sequences derived from the HDA-verb workaround for this model. */
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_preinit_seq[] = {
+       { 0x420, 0x00, 0x01 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_disable_seq[] = {
+       { 0x423, 0xff, 0x00 },
+       { 0x420, 0x3a, 0x80 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_enable_seq[] = {
+       { 0x420, 0x3a, 0x81 },
+       { 0x423, 0xff, 0x01 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_38[] = {
+       { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+       { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe },
+       { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+       { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+       { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 },
+       { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_39[] = {
+       { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+       { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd },
+       { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+       { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+       { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0x99, 0x03 },
+       { 0x423, 0xa4, 0xb5 }, { 0x423, 0xa5, 0x01 }, { 0x423, 0xba, 0x94 },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3c[] = {
+       { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+       { 0x420, 0x1b, 0x01 }, { 0x420, 0x1d, 0x01 }, { 0x420, 0x1f, 0xfe },
+       { 0x420, 0x21, 0x00 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+       { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+       { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d },
+};
+
+static const struct alc298_lg_gram_style_seq alc298_lg_gram_style_init_seq_3d[] = {
+       { 0x423, 0xe1, 0x00 }, { 0x420, 0x12, 0x6f }, { 0x420, 0x14, 0x00 },
+       { 0x420, 0x1b, 0x02 }, { 0x420, 0x1d, 0x02 }, { 0x420, 0x1f, 0xfd },
+       { 0x420, 0x21, 0x01 }, { 0x420, 0x22, 0x10 }, { 0x420, 0x3d, 0x05 },
+       { 0x420, 0x3f, 0x03 }, { 0x420, 0x50, 0x2c }, { 0x420, 0x76, 0x0e },
+       { 0x420, 0x7c, 0x4a }, { 0x420, 0x81, 0x03 }, { 0x423, 0xba, 0x8d },
+};
+
+struct alc298_lg_gram_style_amp_desc {
+       unsigned char nid;
+       const struct alc298_lg_gram_style_seq *init_seq;
+       int init_seq_size;
+};
+
+static const struct alc298_lg_gram_style_amp_desc alc298_lg_gram_style_amps[] = {
+       { 0x38, alc298_lg_gram_style_init_seq_38,
+               ARRAY_SIZE(alc298_lg_gram_style_init_seq_38) },
+       { 0x39, alc298_lg_gram_style_init_seq_39,
+               ARRAY_SIZE(alc298_lg_gram_style_init_seq_39) },
+       { 0x3c, alc298_lg_gram_style_init_seq_3c,
+               ARRAY_SIZE(alc298_lg_gram_style_init_seq_3c) },
+       { 0x3d, alc298_lg_gram_style_init_seq_3d,
+               ARRAY_SIZE(alc298_lg_gram_style_init_seq_3d) },
+};
+
+static void alc298_lg_gram_style_enable_amps(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_speaker_amps; i++) {
+               alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+               alc298_lg_gram_style_run_seq(codec,
+                                            alc298_lg_gram_style_enable_seq,
+                                            ARRAY_SIZE(alc298_lg_gram_style_enable_seq));
+       }
+}
+
+static void alc298_lg_gram_style_disable_amps(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_speaker_amps; i++) {
+               alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+               alc298_lg_gram_style_run_seq(codec,
+                                            alc298_lg_gram_style_disable_seq,
+                                            ARRAY_SIZE(alc298_lg_gram_style_disable_seq));
+       }
+}
+
+static void alc298_lg_gram_style_playback_hook(struct hda_pcm_stream *hinfo,
+                                              struct hda_codec *codec,
+                                              struct snd_pcm_substream *substream,
+                                              int action)
+{
+       if (action == HDA_GEN_PCM_ACT_OPEN)
+               alc298_lg_gram_style_enable_amps(codec);
+       if (action == HDA_GEN_PCM_ACT_CLOSE)
+               alc298_lg_gram_style_disable_amps(codec);
+}
+
+static void alc298_lg_gram_style_init_amps(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       spec->num_speaker_amps = ARRAY_SIZE(alc298_lg_gram_style_amps);
+
+       for (i = 0; i < spec->num_speaker_amps; i++) {
+               alc_write_coef_idx(codec, 0x22, alc298_lg_gram_style_amps[i].nid);
+               alc298_lg_gram_style_run_seq(codec,
+                                            alc298_lg_gram_style_preinit_seq,
+                                            ARRAY_SIZE(alc298_lg_gram_style_preinit_seq));
+               alc298_lg_gram_style_run_seq(codec,
+                                            alc298_lg_gram_style_disable_seq,
+                                            ARRAY_SIZE(alc298_lg_gram_style_disable_seq));
+               alc298_lg_gram_style_run_seq(codec,
+                                            alc298_lg_gram_style_amps[i].init_seq,
+                                            alc298_lg_gram_style_amps[i].init_seq_size);
+               alc_write_coef_idx(codec, 0x89, 0x0);
+       }
+
+       spec->gen.pcm_playback_hook = alc298_lg_gram_style_playback_hook;
+}
+
 static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec,
                                const struct hda_fixup *fix, int action)
 {
@@ -1868,6 +2025,13 @@ static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec,
                alc298_samsung_v2_init_amps(codec, 4);
 }
 
+static void alc298_fixup_lg_gram_style_14(struct hda_codec *codec,
+                                         const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PROBE)
+               alc298_lg_gram_style_init_amps(codec);
+}
+
 static void gpio2_mic_hotkey_event(struct hda_codec *codec,
                                   struct hda_jack_callback *event)
 {
@@ -3753,6 +3917,7 @@ enum {
        ALC298_FIXUP_SAMSUNG_AMP,
        ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS,
        ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS,
+       ALC298_FIXUP_LG_GRAM_STYLE_14,
        ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
        ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
        ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -5430,6 +5595,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc298_fixup_samsung_amp_v2_4_amps
        },
+       [ALC298_FIXUP_LG_GRAM_STYLE_14] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc298_fixup_lg_gram_style_14
+       },
        [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
@@ -7368,6 +7537,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
        SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
        SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+       SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14),
        SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
        SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC),