// SPDX-License-Identifier: GPL-2.0-or-later // // Realtek ALC880 codec // #include #include #include "realtek.h" static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) { /* For some reason, the res given from ALC880 is broken. Here we adjust it properly. */ snd_hda_jack_unsol_event(codec, res >> 2); } static int alc880_parse_auto_config(struct hda_codec *codec) { static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); } /* * ALC880 fix-ups */ enum { ALC880_FIXUP_GPIO1, ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, ALC880_FIXUP_LG, ALC880_FIXUP_LG_LW25, ALC880_FIXUP_W810, ALC880_FIXUP_EAPD_COEF, ALC880_FIXUP_TCL_S700, ALC880_FIXUP_VOL_KNOB, ALC880_FIXUP_FUJITSU, ALC880_FIXUP_F1734, ALC880_FIXUP_UNIWILL, ALC880_FIXUP_UNIWILL_DIG, ALC880_FIXUP_Z71V, ALC880_FIXUP_ASUS_W5A, ALC880_FIXUP_3ST_BASE, ALC880_FIXUP_3ST, ALC880_FIXUP_3ST_DIG, ALC880_FIXUP_5ST_BASE, ALC880_FIXUP_5ST, ALC880_FIXUP_5ST_DIG, ALC880_FIXUP_6ST_BASE, ALC880_FIXUP_6ST, ALC880_FIXUP_6ST_DIG, ALC880_FIXUP_6ST_AUTOMUTE, }; /* enable the volume-knob widget support on NID 0x21 */ static void alc880_fixup_vol_knob(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_detect_enable_callback(codec, 0x21, alc_update_knob_master); } static const struct hda_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_gpio1, }, [ALC880_FIXUP_GPIO2] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_gpio2, }, [ALC880_FIXUP_MEDION_RIM] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, { } }, .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_LG] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x16, 0x411111f0 }, { 0x18, 0x411111f0 }, { 0x1a, 0x411111f0 }, { } } }, [ALC880_FIXUP_LG_LW25] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1a, 0x0181344f }, /* line-in */ { 0x1b, 0x0321403f }, /* headphone */ { } } }, [ALC880_FIXUP_W810] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { } }, .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_EAPD_COEF] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, {} }, }, [ALC880_FIXUP_TCL_S700] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, {} }, .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_VOL_KNOB] = { .type = HDA_FIXUP_FUNC, .v.func = alc880_fixup_vol_knob, }, [ALC880_FIXUP_FUJITSU] = { /* override all pins as BIOS on old Amilo is broken */ .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121401f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ { 0x17, 0x411111f0 }, /* N/A */ { 0x18, 0x411111f0 }, /* N/A */ { 0x19, 0x01a19950 }, /* mic-in */ { 0x1a, 0x411111f0 }, /* N/A */ { 0x1b, 0x411111f0 }, /* N/A */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ { 0x1e, 0x01454140 }, /* SPDIF out */ { } }, .chained = true, .chain_id = ALC880_FIXUP_VOL_KNOB, }, [ALC880_FIXUP_F1734] = { /* almost compatible with FUJITSU, but no bass and SPDIF */ .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121401f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x411111f0 }, /* N/A */ { 0x17, 0x411111f0 }, /* N/A */ { 0x18, 0x411111f0 }, /* N/A */ { 0x19, 0x01a19950 }, /* mic-in */ { 0x1a, 0x411111f0 }, /* N/A */ { 0x1b, 0x411111f0 }, /* N/A */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ { 0x1e, 0x411111f0 }, /* N/A */ { } }, .chained = true, .chain_id = ALC880_FIXUP_VOL_KNOB, }, [ALC880_FIXUP_UNIWILL] = { /* need to fix HP and speaker pins to be parsed correctly */ .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ { } }, }, [ALC880_FIXUP_UNIWILL_DIG] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { 0x19, 0x411111f0 }, { 0x1b, 0x411111f0 }, { 0x1f, 0x411111f0 }, { } } }, [ALC880_FIXUP_Z71V] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { /* set up the whole pins as BIOS is utterly broken */ { 0x14, 0x99030120 }, /* speaker */ { 0x15, 0x0121411f }, /* HP */ { 0x16, 0x411111f0 }, /* N/A */ { 0x17, 0x411111f0 }, /* N/A */ { 0x18, 0x01a19950 }, /* mic-in */ { 0x19, 0x411111f0 }, /* N/A */ { 0x1a, 0x01813031 }, /* line-in */ { 0x1b, 0x411111f0 }, /* N/A */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ { 0x1e, 0x0144111e }, /* SPDIF */ { } } }, [ALC880_FIXUP_ASUS_W5A] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { /* set up the whole pins as BIOS is utterly broken */ { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ { 0x17, 0x411111f0 }, /* N/A */ { 0x18, 0x90a60160 }, /* mic */ { 0x19, 0x411111f0 }, /* N/A */ { 0x1a, 0x411111f0 }, /* N/A */ { 0x1b, 0x411111f0 }, /* N/A */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ { 0x1e, 0xb743111e }, /* SPDIF out */ { } }, .chained = true, .chain_id = ALC880_FIXUP_GPIO1, }, [ALC880_FIXUP_3ST_BASE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* line-out */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ { 0x17, 0x411111f0 }, /* N/A */ { 0x18, 0x01a19c30 }, /* mic-in */ { 0x19, 0x0121411f }, /* HP */ { 0x1a, 0x01813031 }, /* line-in */ { 0x1b, 0x02a19c40 }, /* front-mic */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ /* 0x1e is filled in below */ { 0x1f, 0x411111f0 }, /* N/A */ { } } }, [ALC880_FIXUP_3ST] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, .chained = true, .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_3ST_DIG] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, .chained = true, .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_5ST_BASE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x01011411 }, /* CLFE */ { 0x17, 0x01016412 }, /* surr */ { 0x18, 0x01a19c30 }, /* mic-in */ { 0x19, 0x0121411f }, /* HP */ { 0x1a, 0x01813031 }, /* line-in */ { 0x1b, 0x02a19c40 }, /* front-mic */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ /* 0x1e is filled in below */ { 0x1f, 0x411111f0 }, /* N/A */ { } } }, [ALC880_FIXUP_5ST] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, .chained = true, .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_5ST_DIG] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, .chained = true, .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_6ST_BASE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x01016412 }, /* surr */ { 0x16, 0x01011411 }, /* CLFE */ { 0x17, 0x01012414 }, /* side */ { 0x18, 0x01a19c30 }, /* mic-in */ { 0x19, 0x02a19c40 }, /* front-mic */ { 0x1a, 0x01813031 }, /* line-in */ { 0x1b, 0x0121411f }, /* HP */ { 0x1c, 0x411111f0 }, /* N/A */ { 0x1d, 0x411111f0 }, /* N/A */ /* 0x1e is filled in below */ { 0x1f, 0x411111f0 }, /* N/A */ { } } }, [ALC880_FIXUP_6ST] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, .chained = true, .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_DIG] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, .chained = true, .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_AUTOMUTE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x1b, 0x0121401f }, /* HP with jack detect */ { } }, .chained_before = true, .chain_id = ALC880_FIXUP_6ST_BASE, }, }; static const struct hda_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), /* Below is the copied entries from alc880_quirks.c. * It's not quite sure whether BIOS sets the correct pin-config table * on these machines, thus they are kept to be compatible with * the old static quirks. Once when it's confirmed to work without * these overrides, it'd be better to remove. */ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), /* default Intel */ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), {} }; static const struct hda_model_fixup alc880_fixup_models[] = { {.id = ALC880_FIXUP_3ST, .name = "3stack"}, {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, {.id = ALC880_FIXUP_5ST, .name = "5stack"}, {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, {.id = ALC880_FIXUP_6ST, .name = "6stack"}, {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, {} }; /* * OK, here we have finally the probe for ALC880 */ static int alc880_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; err = alc_alloc_spec(codec, 0x0b); if (err < 0) return err; spec = codec->spec; spec->gen.need_dac_fix = 1; spec->gen.beep_nid = 0x01; alc_pre_init(codec); snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc880_parse_auto_config(codec); if (err < 0) goto error; if (!spec->gen.no_analog) { err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); if (err < 0) goto error; } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; error: snd_hda_gen_remove(codec); return err; } static const struct hda_codec_ops alc880_codec_ops = { .probe = alc880_probe, .remove = snd_hda_gen_remove, .build_controls = alc_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = alc_init, .unsol_event = alc880_unsol_event, .resume = alc_resume, .suspend = alc_suspend, .check_power_status = snd_hda_gen_check_power_status, .stream_pm = snd_hda_gen_stream_pm, }; /* * driver entries */ static const struct hda_device_id snd_hda_id_alc880[] = { HDA_CODEC_ID(0x10ec0880, "ALC880"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek ALC880 HD-audio codec"); MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc880_driver = { .id = snd_hda_id_alc880, .ops = &alc880_codec_ops, }; module_hda_codec_driver(alc880_driver);