1 // SPDX-License-Identifier: GPL-2.0-or-later 2 // 3 // Realtek ALC861 codec 4 // 5 6 #include <linux/init.h> 7 #include <linux/module.h> 8 #include "realtek.h" 9 10 static int alc861_parse_auto_config(struct hda_codec *codec) 11 { 12 static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; 13 static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; 14 return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); 15 } 16 17 /* Pin config fixes */ 18 enum { 19 ALC861_FIXUP_FSC_AMILO_PI1505, 20 ALC861_FIXUP_AMP_VREF_0F, 21 ALC861_FIXUP_NO_JACK_DETECT, 22 ALC861_FIXUP_ASUS_A6RP, 23 ALC660_FIXUP_ASUS_W7J, 24 }; 25 26 /* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ 27 static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, 28 const struct hda_fixup *fix, int action) 29 { 30 struct alc_spec *spec = codec->spec; 31 unsigned int val; 32 33 if (action != HDA_FIXUP_ACT_INIT) 34 return; 35 val = snd_hda_codec_get_pin_target(codec, 0x0f); 36 if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) 37 val |= AC_PINCTL_IN_EN; 38 val |= AC_PINCTL_VREF_50; 39 snd_hda_set_pin_ctl(codec, 0x0f, val); 40 spec->gen.keep_vref_in_automute = 1; 41 } 42 43 static const struct hda_fixup alc861_fixups[] = { 44 [ALC861_FIXUP_FSC_AMILO_PI1505] = { 45 .type = HDA_FIXUP_PINS, 46 .v.pins = (const struct hda_pintbl[]) { 47 { 0x0b, 0x0221101f }, /* HP */ 48 { 0x0f, 0x90170310 }, /* speaker */ 49 { } 50 } 51 }, 52 [ALC861_FIXUP_AMP_VREF_0F] = { 53 .type = HDA_FIXUP_FUNC, 54 .v.func = alc861_fixup_asus_amp_vref_0f, 55 }, 56 [ALC861_FIXUP_NO_JACK_DETECT] = { 57 .type = HDA_FIXUP_FUNC, 58 .v.func = alc_fixup_no_jack_detect, 59 }, 60 [ALC861_FIXUP_ASUS_A6RP] = { 61 .type = HDA_FIXUP_FUNC, 62 .v.func = alc861_fixup_asus_amp_vref_0f, 63 .chained = true, 64 .chain_id = ALC861_FIXUP_NO_JACK_DETECT, 65 }, 66 [ALC660_FIXUP_ASUS_W7J] = { 67 .type = HDA_FIXUP_VERBS, 68 .v.verbs = (const struct hda_verb[]) { 69 /* ASUS W7J needs a magic pin setup on unused NID 0x10 70 * for enabling outputs 71 */ 72 {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, 73 { } 74 }, 75 } 76 }; 77 78 static const struct hda_quirk alc861_fixup_tbl[] = { 79 SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), 80 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), 81 SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), 82 SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), 83 SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), 84 SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), 85 SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), 86 {} 87 }; 88 89 /* 90 */ 91 static int alc861_probe(struct hda_codec *codec, const struct hda_device_id *id) 92 { 93 struct alc_spec *spec; 94 int err; 95 96 err = alc_alloc_spec(codec, 0x15); 97 if (err < 0) 98 return err; 99 100 spec = codec->spec; 101 if (has_cdefine_beep(codec)) 102 spec->gen.beep_nid = 0x23; 103 104 spec->power_hook = alc_power_eapd; 105 106 alc_pre_init(codec); 107 108 snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); 109 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 110 111 /* automatic parse from the BIOS config */ 112 err = alc861_parse_auto_config(codec); 113 if (err < 0) 114 goto error; 115 116 if (!spec->gen.no_analog) { 117 err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); 118 if (err < 0) 119 goto error; 120 } 121 122 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 123 124 return 0; 125 126 error: 127 snd_hda_gen_remove(codec); 128 return err; 129 } 130 131 static const struct hda_codec_ops alc861_codec_ops = { 132 .probe = alc861_probe, 133 .remove = snd_hda_gen_remove, 134 .build_controls = alc_build_controls, 135 .build_pcms = snd_hda_gen_build_pcms, 136 .init = alc_init, 137 .unsol_event = snd_hda_jack_unsol_event, 138 .resume = alc_resume, 139 .suspend = alc_suspend, 140 .check_power_status = snd_hda_gen_check_power_status, 141 .stream_pm = snd_hda_gen_stream_pm, 142 }; 143 144 /* 145 * driver entries 146 */ 147 static const struct hda_device_id snd_hda_id_alc861[] = { 148 HDA_CODEC_ID_REV(0x10ec0861, 0x100340, "ALC660"), 149 HDA_CODEC_ID(0x10ec0861, "ALC861"), 150 {} /* terminator */ 151 }; 152 MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861); 153 154 MODULE_LICENSE("GPL"); 155 MODULE_DESCRIPTION("Realtek ALC861 HD-audio codec"); 156 MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); 157 158 static struct hda_codec_driver alc861_driver = { 159 .id = snd_hda_id_alc861, 160 .ops = &alc861_codec_ops, 161 }; 162 163 module_hda_codec_driver(alc861_driver); 164