| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | // |
| 3 | // Realtek ALC260 codec |
| 4 | // |
| 5 | |
| 6 | #include <linux/init.h> |
| 7 | #include <linux/module.h> |
| 8 | #include "realtek.h" |
| 9 | |
| 10 | static int alc260_parse_auto_config(struct hda_codec *codec) |
| 11 | { |
| 12 | static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; |
| 13 | static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; |
| 14 | return alc_parse_auto_config(codec, ignore_nids: alc260_ignore, ssid_nids: alc260_ssids); |
| 15 | } |
| 16 | |
| 17 | /* |
| 18 | * Pin config fixes |
| 19 | */ |
| 20 | enum { |
| 21 | ALC260_FIXUP_HP_DC5750, |
| 22 | ALC260_FIXUP_HP_PIN_0F, |
| 23 | ALC260_FIXUP_COEF, |
| 24 | ALC260_FIXUP_GPIO1, |
| 25 | ALC260_FIXUP_GPIO1_TOGGLE, |
| 26 | ALC260_FIXUP_REPLACER, |
| 27 | ALC260_FIXUP_HP_B1900, |
| 28 | ALC260_FIXUP_KN1, |
| 29 | ALC260_FIXUP_FSC_S7020, |
| 30 | ALC260_FIXUP_FSC_S7020_JWSE, |
| 31 | ALC260_FIXUP_VAIO_PINS, |
| 32 | }; |
| 33 | |
| 34 | static void alc260_gpio1_automute(struct hda_codec *codec) |
| 35 | { |
| 36 | struct alc_spec *spec = codec->spec; |
| 37 | |
| 38 | alc_update_gpio_data(codec, mask: 0x01, on: spec->gen.hp_jack_present); |
| 39 | } |
| 40 | |
| 41 | static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, |
| 42 | const struct hda_fixup *fix, int action) |
| 43 | { |
| 44 | struct alc_spec *spec = codec->spec; |
| 45 | if (action == HDA_FIXUP_ACT_PROBE) { |
| 46 | /* although the machine has only one output pin, we need to |
| 47 | * toggle GPIO1 according to the jack state |
| 48 | */ |
| 49 | spec->gen.automute_hook = alc260_gpio1_automute; |
| 50 | spec->gen.detect_hp = 1; |
| 51 | spec->gen.automute_speaker = 1; |
| 52 | spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ |
| 53 | snd_hda_jack_detect_enable_callback(codec, nid: 0x0f, |
| 54 | cb: snd_hda_gen_hp_automute); |
| 55 | alc_setup_gpio(codec, mask: 0x01); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | static void alc260_fixup_kn1(struct hda_codec *codec, |
| 60 | const struct hda_fixup *fix, int action) |
| 61 | { |
| 62 | struct alc_spec *spec = codec->spec; |
| 63 | static const struct hda_pintbl pincfgs[] = { |
| 64 | { 0x0f, 0x02214000 }, /* HP/speaker */ |
| 65 | { 0x12, 0x90a60160 }, /* int mic */ |
| 66 | { 0x13, 0x02a19000 }, /* ext mic */ |
| 67 | { 0x18, 0x01446000 }, /* SPDIF out */ |
| 68 | /* disable bogus I/O pins */ |
| 69 | { 0x10, 0x411111f0 }, |
| 70 | { 0x11, 0x411111f0 }, |
| 71 | { 0x14, 0x411111f0 }, |
| 72 | { 0x15, 0x411111f0 }, |
| 73 | { 0x16, 0x411111f0 }, |
| 74 | { 0x17, 0x411111f0 }, |
| 75 | { 0x19, 0x411111f0 }, |
| 76 | { } |
| 77 | }; |
| 78 | |
| 79 | switch (action) { |
| 80 | case HDA_FIXUP_ACT_PRE_PROBE: |
| 81 | snd_hda_apply_pincfgs(codec, cfg: pincfgs); |
| 82 | spec->init_amp = ALC_INIT_NONE; |
| 83 | break; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | static void alc260_fixup_fsc_s7020(struct hda_codec *codec, |
| 88 | const struct hda_fixup *fix, int action) |
| 89 | { |
| 90 | struct alc_spec *spec = codec->spec; |
| 91 | if (action == HDA_FIXUP_ACT_PRE_PROBE) |
| 92 | spec->init_amp = ALC_INIT_NONE; |
| 93 | } |
| 94 | |
| 95 | static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, |
| 96 | const struct hda_fixup *fix, int action) |
| 97 | { |
| 98 | struct alc_spec *spec = codec->spec; |
| 99 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
| 100 | spec->gen.add_jack_modes = 1; |
| 101 | spec->gen.hp_mic = 1; |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | static const struct hda_fixup alc260_fixups[] = { |
| 106 | [ALC260_FIXUP_HP_DC5750] = { |
| 107 | .type = HDA_FIXUP_PINS, |
| 108 | .v.pins = (const struct hda_pintbl[]) { |
| 109 | { 0x11, 0x90130110 }, /* speaker */ |
| 110 | { } |
| 111 | } |
| 112 | }, |
| 113 | [ALC260_FIXUP_HP_PIN_0F] = { |
| 114 | .type = HDA_FIXUP_PINS, |
| 115 | .v.pins = (const struct hda_pintbl[]) { |
| 116 | { 0x0f, 0x01214000 }, /* HP */ |
| 117 | { } |
| 118 | } |
| 119 | }, |
| 120 | [ALC260_FIXUP_COEF] = { |
| 121 | .type = HDA_FIXUP_VERBS, |
| 122 | .v.verbs = (const struct hda_verb[]) { |
| 123 | { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, |
| 124 | { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, |
| 125 | { } |
| 126 | }, |
| 127 | }, |
| 128 | [ALC260_FIXUP_GPIO1] = { |
| 129 | .type = HDA_FIXUP_FUNC, |
| 130 | .v.func = alc_fixup_gpio1, |
| 131 | }, |
| 132 | [ALC260_FIXUP_GPIO1_TOGGLE] = { |
| 133 | .type = HDA_FIXUP_FUNC, |
| 134 | .v.func = alc260_fixup_gpio1_toggle, |
| 135 | .chained = true, |
| 136 | .chain_id = ALC260_FIXUP_HP_PIN_0F, |
| 137 | }, |
| 138 | [ALC260_FIXUP_REPLACER] = { |
| 139 | .type = HDA_FIXUP_VERBS, |
| 140 | .v.verbs = (const struct hda_verb[]) { |
| 141 | { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, |
| 142 | { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, |
| 143 | { } |
| 144 | }, |
| 145 | .chained = true, |
| 146 | .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, |
| 147 | }, |
| 148 | [ALC260_FIXUP_HP_B1900] = { |
| 149 | .type = HDA_FIXUP_FUNC, |
| 150 | .v.func = alc260_fixup_gpio1_toggle, |
| 151 | .chained = true, |
| 152 | .chain_id = ALC260_FIXUP_COEF, |
| 153 | }, |
| 154 | [ALC260_FIXUP_KN1] = { |
| 155 | .type = HDA_FIXUP_FUNC, |
| 156 | .v.func = alc260_fixup_kn1, |
| 157 | }, |
| 158 | [ALC260_FIXUP_FSC_S7020] = { |
| 159 | .type = HDA_FIXUP_FUNC, |
| 160 | .v.func = alc260_fixup_fsc_s7020, |
| 161 | }, |
| 162 | [ALC260_FIXUP_FSC_S7020_JWSE] = { |
| 163 | .type = HDA_FIXUP_FUNC, |
| 164 | .v.func = alc260_fixup_fsc_s7020_jwse, |
| 165 | .chained = true, |
| 166 | .chain_id = ALC260_FIXUP_FSC_S7020, |
| 167 | }, |
| 168 | [ALC260_FIXUP_VAIO_PINS] = { |
| 169 | .type = HDA_FIXUP_PINS, |
| 170 | .v.pins = (const struct hda_pintbl[]) { |
| 171 | /* Pin configs are missing completely on some VAIOs */ |
| 172 | { 0x0f, 0x01211020 }, |
| 173 | { 0x10, 0x0001003f }, |
| 174 | { 0x11, 0x411111f0 }, |
| 175 | { 0x12, 0x01a15930 }, |
| 176 | { 0x13, 0x411111f0 }, |
| 177 | { 0x14, 0x411111f0 }, |
| 178 | { 0x15, 0x411111f0 }, |
| 179 | { 0x16, 0x411111f0 }, |
| 180 | { 0x17, 0x411111f0 }, |
| 181 | { 0x18, 0x411111f0 }, |
| 182 | { 0x19, 0x411111f0 }, |
| 183 | { } |
| 184 | } |
| 185 | }, |
| 186 | }; |
| 187 | |
| 188 | static const struct hda_quirk alc260_fixup_tbl[] = { |
| 189 | SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x" , ALC260_FIXUP_GPIO1), |
| 190 | SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500" , ALC260_FIXUP_COEF), |
| 191 | SND_PCI_QUIRK(0x1025, 0x008f, "Acer" , ALC260_FIXUP_GPIO1), |
| 192 | SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750" , ALC260_FIXUP_HP_DC5750), |
| 193 | SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900" , ALC260_FIXUP_HP_B1900), |
| 194 | SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO" , ALC260_FIXUP_VAIO_PINS), |
| 195 | SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX" , ALC260_FIXUP_HP_PIN_0F), |
| 196 | SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020" , ALC260_FIXUP_FSC_S7020), |
| 197 | SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS" , ALC260_FIXUP_GPIO1), |
| 198 | SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1" , ALC260_FIXUP_KN1), |
| 199 | SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V" , ALC260_FIXUP_REPLACER), |
| 200 | SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900" , ALC260_FIXUP_COEF), |
| 201 | {} |
| 202 | }; |
| 203 | |
| 204 | static const struct hda_model_fixup alc260_fixup_models[] = { |
| 205 | {.id = ALC260_FIXUP_GPIO1, .name = "gpio1" }, |
| 206 | {.id = ALC260_FIXUP_COEF, .name = "coef" }, |
| 207 | {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu" }, |
| 208 | {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse" }, |
| 209 | {} |
| 210 | }; |
| 211 | |
| 212 | /* |
| 213 | */ |
| 214 | static int alc260_probe(struct hda_codec *codec, const struct hda_device_id *id) |
| 215 | { |
| 216 | struct alc_spec *spec; |
| 217 | int err; |
| 218 | |
| 219 | err = alc_alloc_spec(codec, mixer_nid: 0x07); |
| 220 | if (err < 0) |
| 221 | return err; |
| 222 | |
| 223 | spec = codec->spec; |
| 224 | /* as quite a few machines require HP amp for speaker outputs, |
| 225 | * it's easier to enable it unconditionally; even if it's unneeded, |
| 226 | * it's almost harmless. |
| 227 | */ |
| 228 | spec->gen.prefer_hp_amp = 1; |
| 229 | spec->gen.beep_nid = 0x01; |
| 230 | |
| 231 | spec->shutup = alc_eapd_shutup; |
| 232 | |
| 233 | alc_pre_init(codec); |
| 234 | |
| 235 | snd_hda_pick_fixup(codec, models: alc260_fixup_models, quirk: alc260_fixup_tbl, |
| 236 | fixlist: alc260_fixups); |
| 237 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PRE_PROBE); |
| 238 | |
| 239 | /* automatic parse from the BIOS config */ |
| 240 | err = alc260_parse_auto_config(codec); |
| 241 | if (err < 0) |
| 242 | goto error; |
| 243 | |
| 244 | if (!spec->gen.no_analog) { |
| 245 | err = set_beep_amp(spec, nid: 0x07, idx: 0x05, dir: HDA_INPUT); |
| 246 | if (err < 0) |
| 247 | goto error; |
| 248 | } |
| 249 | |
| 250 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PROBE); |
| 251 | |
| 252 | return 0; |
| 253 | |
| 254 | error: |
| 255 | snd_hda_gen_remove(codec); |
| 256 | return err; |
| 257 | } |
| 258 | |
| 259 | static const struct hda_codec_ops alc260_codec_ops = { |
| 260 | .probe = alc260_probe, |
| 261 | .remove = snd_hda_gen_remove, |
| 262 | .build_controls = alc_build_controls, |
| 263 | .build_pcms = snd_hda_gen_build_pcms, |
| 264 | .init = alc_init, |
| 265 | .unsol_event = snd_hda_jack_unsol_event, |
| 266 | .resume = alc_resume, |
| 267 | .suspend = alc_suspend, |
| 268 | .check_power_status = snd_hda_gen_check_power_status, |
| 269 | .stream_pm = snd_hda_gen_stream_pm, |
| 270 | }; |
| 271 | |
| 272 | /* |
| 273 | * driver entries |
| 274 | */ |
| 275 | static const struct hda_device_id snd_hda_id_alc260[] = { |
| 276 | HDA_CODEC_ID(0x10ec0260, "ALC260" ), |
| 277 | {} /* terminator */ |
| 278 | }; |
| 279 | MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260); |
| 280 | |
| 281 | MODULE_LICENSE("GPL" ); |
| 282 | MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec" ); |
| 283 | MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK" ); |
| 284 | |
| 285 | static struct hda_codec_driver alc260_driver = { |
| 286 | .id = snd_hda_id_alc260, |
| 287 | .ops = &alc260_codec_ops, |
| 288 | }; |
| 289 | |
| 290 | module_hda_codec_driver(alc260_driver); |
| 291 | |