From cc051fbd7f40226cc407558bc97c5099513e8657 Mon Sep 17 00:00:00 2001 From: Damien Dagorn Date: Fri, 23 Jan 2026 18:14:52 +0100 Subject: [PATCH] ALSA: hda/realtek: fix LG Gram Style 14 speakers 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 Signed-off-by: Damien Dagorn Link: https://lore.kernel.org/CAN59QMUhd4kHrkRoJA6VzEr2VKezN2yjHnANaQoZn2-Bnwe3bQ@mail.gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index cf263a164987..3d5a977bb40a 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -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), -- 2.47.3