1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, |
4 | * AD1986A, AD1988 |
5 | * |
6 | * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de> |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/module.h> |
12 | |
13 | #include <sound/core.h> |
14 | #include <sound/hda_codec.h> |
15 | #include "hda_local.h" |
16 | #include "hda_auto_parser.h" |
17 | #include "hda_beep.h" |
18 | #include "hda_jack.h" |
19 | #include "hda_generic.h" |
20 | |
21 | |
22 | struct ad198x_spec { |
23 | struct hda_gen_spec gen; |
24 | |
25 | /* for auto parser */ |
26 | int smux_paths[4]; |
27 | unsigned int cur_smux; |
28 | hda_nid_t eapd_nid; |
29 | |
30 | unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ |
31 | int num_smux_conns; |
32 | }; |
33 | |
34 | |
35 | #ifdef CONFIG_SND_HDA_INPUT_BEEP |
36 | /* additional beep mixers; the actual parameters are overwritten at build */ |
37 | static const struct snd_kcontrol_new ad_beep_mixer[] = { |
38 | HDA_CODEC_VOLUME("Beep Playback Volume" , 0, 0, HDA_OUTPUT), |
39 | HDA_CODEC_MUTE_BEEP("Beep Playback Switch" , 0, 0, HDA_OUTPUT), |
40 | { } /* end */ |
41 | }; |
42 | |
43 | #define set_beep_amp(spec, nid, idx, dir) \ |
44 | ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ |
45 | #else |
46 | #define set_beep_amp(spec, nid, idx, dir) /* NOP */ |
47 | #endif |
48 | |
49 | #ifdef CONFIG_SND_HDA_INPUT_BEEP |
50 | static int create_beep_ctls(struct hda_codec *codec) |
51 | { |
52 | struct ad198x_spec *spec = codec->spec; |
53 | const struct snd_kcontrol_new *knew; |
54 | |
55 | if (!spec->beep_amp) |
56 | return 0; |
57 | |
58 | for (knew = ad_beep_mixer ; knew->name; knew++) { |
59 | int err; |
60 | struct snd_kcontrol *kctl; |
61 | kctl = snd_ctl_new1(kcontrolnew: knew, private_data: codec); |
62 | if (!kctl) |
63 | return -ENOMEM; |
64 | kctl->private_value = spec->beep_amp; |
65 | err = snd_hda_ctl_add(codec, nid: 0, kctl); |
66 | if (err < 0) |
67 | return err; |
68 | } |
69 | return 0; |
70 | } |
71 | #else |
72 | #define create_beep_ctls(codec) 0 |
73 | #endif |
74 | |
75 | #ifdef CONFIG_PM |
76 | static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, |
77 | hda_nid_t hp) |
78 | { |
79 | if (snd_hda_query_pin_caps(codec, nid: front) & AC_PINCAP_EAPD) |
80 | snd_hda_codec_write(codec, nid: front, flags: 0, AC_VERB_SET_EAPD_BTLENABLE, |
81 | parm: !codec->inv_eapd ? 0x00 : 0x02); |
82 | if (snd_hda_query_pin_caps(codec, nid: hp) & AC_PINCAP_EAPD) |
83 | snd_hda_codec_write(codec, nid: hp, flags: 0, AC_VERB_SET_EAPD_BTLENABLE, |
84 | parm: !codec->inv_eapd ? 0x00 : 0x02); |
85 | } |
86 | |
87 | static void ad198x_power_eapd(struct hda_codec *codec) |
88 | { |
89 | /* We currently only handle front, HP */ |
90 | switch (codec->core.vendor_id) { |
91 | case 0x11d41882: |
92 | case 0x11d4882a: |
93 | case 0x11d41884: |
94 | case 0x11d41984: |
95 | case 0x11d41883: |
96 | case 0x11d4184a: |
97 | case 0x11d4194a: |
98 | case 0x11d4194b: |
99 | case 0x11d41988: |
100 | case 0x11d4198b: |
101 | case 0x11d4989a: |
102 | case 0x11d4989b: |
103 | ad198x_power_eapd_write(codec, front: 0x12, hp: 0x11); |
104 | break; |
105 | case 0x11d41981: |
106 | case 0x11d41983: |
107 | ad198x_power_eapd_write(codec, front: 0x05, hp: 0x06); |
108 | break; |
109 | case 0x11d41986: |
110 | ad198x_power_eapd_write(codec, front: 0x1b, hp: 0x1a); |
111 | break; |
112 | } |
113 | } |
114 | |
115 | static int ad198x_suspend(struct hda_codec *codec) |
116 | { |
117 | snd_hda_shutup_pins(codec); |
118 | ad198x_power_eapd(codec); |
119 | return 0; |
120 | } |
121 | #endif |
122 | |
123 | /* follow EAPD via vmaster hook */ |
124 | static void ad_vmaster_eapd_hook(void *private_data, int enabled) |
125 | { |
126 | struct hda_codec *codec = private_data; |
127 | struct ad198x_spec *spec = codec->spec; |
128 | |
129 | if (!spec->eapd_nid) |
130 | return; |
131 | if (codec->inv_eapd) |
132 | enabled = !enabled; |
133 | snd_hda_codec_write_cache(codec, nid: spec->eapd_nid, flags: 0, |
134 | AC_VERB_SET_EAPD_BTLENABLE, |
135 | parm: enabled ? 0x02 : 0x00); |
136 | } |
137 | |
138 | /* |
139 | * Automatic parse of I/O pins from the BIOS configuration |
140 | */ |
141 | |
142 | static int ad198x_auto_build_controls(struct hda_codec *codec) |
143 | { |
144 | int err; |
145 | |
146 | err = snd_hda_gen_build_controls(codec); |
147 | if (err < 0) |
148 | return err; |
149 | err = create_beep_ctls(codec); |
150 | if (err < 0) |
151 | return err; |
152 | return 0; |
153 | } |
154 | |
155 | static const struct hda_codec_ops ad198x_auto_patch_ops = { |
156 | .build_controls = ad198x_auto_build_controls, |
157 | .build_pcms = snd_hda_gen_build_pcms, |
158 | .init = snd_hda_gen_init, |
159 | .free = snd_hda_gen_free, |
160 | .unsol_event = snd_hda_jack_unsol_event, |
161 | #ifdef CONFIG_PM |
162 | .check_power_status = snd_hda_gen_check_power_status, |
163 | .suspend = ad198x_suspend, |
164 | #endif |
165 | }; |
166 | |
167 | |
168 | static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) |
169 | { |
170 | struct ad198x_spec *spec = codec->spec; |
171 | struct auto_pin_cfg *cfg = &spec->gen.autocfg; |
172 | int err; |
173 | |
174 | codec->spdif_status_reset = 1; |
175 | codec->no_trigger_sense = 1; |
176 | codec->no_sticky_stream = 1; |
177 | |
178 | spec->gen.indep_hp = indep_hp; |
179 | if (!spec->gen.add_stereo_mix_input) |
180 | spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; |
181 | |
182 | err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, cond_flags: 0); |
183 | if (err < 0) |
184 | return err; |
185 | err = snd_hda_gen_parse_auto_config(codec, cfg); |
186 | if (err < 0) |
187 | return err; |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | /* |
193 | * AD1986A specific |
194 | */ |
195 | |
196 | static int alloc_ad_spec(struct hda_codec *codec) |
197 | { |
198 | struct ad198x_spec *spec; |
199 | |
200 | spec = kzalloc(size: sizeof(*spec), GFP_KERNEL); |
201 | if (!spec) |
202 | return -ENOMEM; |
203 | codec->spec = spec; |
204 | snd_hda_gen_spec_init(spec: &spec->gen); |
205 | codec->patch_ops = ad198x_auto_patch_ops; |
206 | return 0; |
207 | } |
208 | |
209 | /* |
210 | * AD1986A fixup codes |
211 | */ |
212 | |
213 | /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ |
214 | static void ad_fixup_inv_jack_detect(struct hda_codec *codec, |
215 | const struct hda_fixup *fix, int action) |
216 | { |
217 | struct ad198x_spec *spec = codec->spec; |
218 | |
219 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
220 | codec->inv_jack_detect = 1; |
221 | spec->gen.keep_eapd_on = 1; |
222 | spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; |
223 | spec->eapd_nid = 0x1b; |
224 | } |
225 | } |
226 | |
227 | /* Toshiba Satellite L40 implements EAPD in a standard way unlike others */ |
228 | static void ad1986a_fixup_eapd(struct hda_codec *codec, |
229 | const struct hda_fixup *fix, int action) |
230 | { |
231 | struct ad198x_spec *spec = codec->spec; |
232 | |
233 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
234 | codec->inv_eapd = 0; |
235 | spec->gen.keep_eapd_on = 1; |
236 | spec->eapd_nid = 0x1b; |
237 | } |
238 | } |
239 | |
240 | /* enable stereo-mix input for avoiding regression on KDE (bko#88251) */ |
241 | static void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec, |
242 | const struct hda_fixup *fix, int action) |
243 | { |
244 | struct ad198x_spec *spec = codec->spec; |
245 | |
246 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
247 | ad1986a_fixup_eapd(codec, fix, action); |
248 | spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE; |
249 | } |
250 | } |
251 | |
252 | enum { |
253 | AD1986A_FIXUP_INV_JACK_DETECT, |
254 | AD1986A_FIXUP_ULTRA, |
255 | AD1986A_FIXUP_SAMSUNG, |
256 | AD1986A_FIXUP_3STACK, |
257 | AD1986A_FIXUP_LAPTOP, |
258 | AD1986A_FIXUP_LAPTOP_IMIC, |
259 | AD1986A_FIXUP_EAPD, |
260 | AD1986A_FIXUP_EAPD_MIX_IN, |
261 | AD1986A_FIXUP_EASYNOTE, |
262 | }; |
263 | |
264 | static const struct hda_fixup ad1986a_fixups[] = { |
265 | [AD1986A_FIXUP_INV_JACK_DETECT] = { |
266 | .type = HDA_FIXUP_FUNC, |
267 | .v.func = ad_fixup_inv_jack_detect, |
268 | }, |
269 | [AD1986A_FIXUP_ULTRA] = { |
270 | .type = HDA_FIXUP_PINS, |
271 | .v.pins = (const struct hda_pintbl[]) { |
272 | { 0x1b, 0x90170110 }, /* speaker */ |
273 | { 0x1d, 0x90a7013e }, /* int mic */ |
274 | {} |
275 | }, |
276 | }, |
277 | [AD1986A_FIXUP_SAMSUNG] = { |
278 | .type = HDA_FIXUP_PINS, |
279 | .v.pins = (const struct hda_pintbl[]) { |
280 | { 0x1b, 0x90170110 }, /* speaker */ |
281 | { 0x1d, 0x90a7013e }, /* int mic */ |
282 | { 0x20, 0x411111f0 }, /* N/A */ |
283 | { 0x24, 0x411111f0 }, /* N/A */ |
284 | {} |
285 | }, |
286 | }, |
287 | [AD1986A_FIXUP_3STACK] = { |
288 | .type = HDA_FIXUP_PINS, |
289 | .v.pins = (const struct hda_pintbl[]) { |
290 | { 0x1a, 0x02214021 }, /* headphone */ |
291 | { 0x1b, 0x01014011 }, /* front */ |
292 | { 0x1c, 0x01813030 }, /* line-in */ |
293 | { 0x1d, 0x01a19020 }, /* rear mic */ |
294 | { 0x1e, 0x411111f0 }, /* N/A */ |
295 | { 0x1f, 0x02a190f0 }, /* mic */ |
296 | { 0x20, 0x411111f0 }, /* N/A */ |
297 | {} |
298 | }, |
299 | }, |
300 | [AD1986A_FIXUP_LAPTOP] = { |
301 | .type = HDA_FIXUP_PINS, |
302 | .v.pins = (const struct hda_pintbl[]) { |
303 | { 0x1a, 0x02214021 }, /* headphone */ |
304 | { 0x1b, 0x90170110 }, /* speaker */ |
305 | { 0x1c, 0x411111f0 }, /* N/A */ |
306 | { 0x1d, 0x411111f0 }, /* N/A */ |
307 | { 0x1e, 0x411111f0 }, /* N/A */ |
308 | { 0x1f, 0x02a191f0 }, /* mic */ |
309 | { 0x20, 0x411111f0 }, /* N/A */ |
310 | {} |
311 | }, |
312 | }, |
313 | [AD1986A_FIXUP_LAPTOP_IMIC] = { |
314 | .type = HDA_FIXUP_PINS, |
315 | .v.pins = (const struct hda_pintbl[]) { |
316 | { 0x1d, 0x90a7013e }, /* int mic */ |
317 | {} |
318 | }, |
319 | .chained_before = 1, |
320 | .chain_id = AD1986A_FIXUP_LAPTOP, |
321 | }, |
322 | [AD1986A_FIXUP_EAPD] = { |
323 | .type = HDA_FIXUP_FUNC, |
324 | .v.func = ad1986a_fixup_eapd, |
325 | }, |
326 | [AD1986A_FIXUP_EAPD_MIX_IN] = { |
327 | .type = HDA_FIXUP_FUNC, |
328 | .v.func = ad1986a_fixup_eapd_mix_in, |
329 | }, |
330 | [AD1986A_FIXUP_EASYNOTE] = { |
331 | .type = HDA_FIXUP_PINS, |
332 | .v.pins = (const struct hda_pintbl[]) { |
333 | { 0x1a, 0x0421402f }, /* headphone */ |
334 | { 0x1b, 0x90170110 }, /* speaker */ |
335 | { 0x1c, 0x411111f0 }, /* N/A */ |
336 | { 0x1d, 0x90a70130 }, /* int mic */ |
337 | { 0x1e, 0x411111f0 }, /* N/A */ |
338 | { 0x1f, 0x04a19040 }, /* mic */ |
339 | { 0x20, 0x411111f0 }, /* N/A */ |
340 | { 0x21, 0x411111f0 }, /* N/A */ |
341 | { 0x22, 0x411111f0 }, /* N/A */ |
342 | { 0x23, 0x411111f0 }, /* N/A */ |
343 | { 0x24, 0x411111f0 }, /* N/A */ |
344 | { 0x25, 0x411111f0 }, /* N/A */ |
345 | {} |
346 | }, |
347 | .chained = true, |
348 | .chain_id = AD1986A_FIXUP_EAPD_MIX_IN, |
349 | }, |
350 | }; |
351 | |
352 | static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { |
353 | SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800" , AD1986A_FIXUP_LAPTOP_IMIC), |
354 | SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V" , AD1986A_FIXUP_LAPTOP_IMIC), |
355 | SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He" , AD1986A_FIXUP_EAPD), |
356 | SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN" , AD1986A_FIXUP_EAPD), |
357 | SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5" , AD1986A_FIXUP_3STACK), |
358 | SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2" , AD1986A_FIXUP_3STACK), |
359 | SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM" , AD1986A_FIXUP_3STACK), |
360 | SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40" , AD1986A_FIXUP_EAPD), |
361 | SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060" , AD1986A_FIXUP_LAPTOP), |
362 | SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung" , AD1986A_FIXUP_SAMSUNG), |
363 | SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1" , AD1986A_FIXUP_ULTRA), |
364 | SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65" , AD1986A_FIXUP_EASYNOTE), |
365 | SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100" , AD1986A_FIXUP_INV_JACK_DETECT), |
366 | SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55" , AD1986A_FIXUP_3STACK), |
367 | SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60" , AD1986A_FIXUP_3STACK), |
368 | {} |
369 | }; |
370 | |
371 | static const struct hda_model_fixup ad1986a_fixup_models[] = { |
372 | { .id = AD1986A_FIXUP_3STACK, .name = "3stack" }, |
373 | { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, |
374 | { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, |
375 | { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ |
376 | { .id = AD1986A_FIXUP_EAPD, .name = "eapd" }, |
377 | {} |
378 | }; |
379 | |
380 | /* |
381 | */ |
382 | static int patch_ad1986a(struct hda_codec *codec) |
383 | { |
384 | int err; |
385 | struct ad198x_spec *spec; |
386 | static const hda_nid_t preferred_pairs[] = { |
387 | 0x1a, 0x03, |
388 | 0x1b, 0x03, |
389 | 0x1c, 0x04, |
390 | 0x1d, 0x05, |
391 | 0x1e, 0x03, |
392 | 0 |
393 | }; |
394 | |
395 | err = alloc_ad_spec(codec); |
396 | if (err < 0) |
397 | return err; |
398 | spec = codec->spec; |
399 | |
400 | /* AD1986A has the inverted EAPD implementation */ |
401 | codec->inv_eapd = 1; |
402 | |
403 | spec->gen.mixer_nid = 0x07; |
404 | spec->gen.beep_nid = 0x19; |
405 | set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); |
406 | |
407 | /* AD1986A has a hardware problem that it can't share a stream |
408 | * with multiple output pins. The copy of front to surrounds |
409 | * causes noisy or silent outputs at a certain timing, e.g. |
410 | * changing the volume. |
411 | * So, let's disable the shared stream. |
412 | */ |
413 | spec->gen.multiout.no_share_stream = 1; |
414 | /* give fixed DAC/pin pairs */ |
415 | spec->gen.preferred_dacs = preferred_pairs; |
416 | |
417 | /* AD1986A can't manage the dynamic pin on/off smoothly */ |
418 | spec->gen.auto_mute_via_amp = 1; |
419 | |
420 | snd_hda_pick_fixup(codec, models: ad1986a_fixup_models, quirk: ad1986a_fixup_tbl, |
421 | fixlist: ad1986a_fixups); |
422 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PRE_PROBE); |
423 | |
424 | err = ad198x_parse_auto_config(codec, indep_hp: false); |
425 | if (err < 0) { |
426 | snd_hda_gen_free(codec); |
427 | return err; |
428 | } |
429 | |
430 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PROBE); |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | |
436 | /* |
437 | * AD1983 specific |
438 | */ |
439 | |
440 | /* |
441 | * SPDIF mux control for AD1983 auto-parser |
442 | */ |
443 | static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol, |
444 | struct snd_ctl_elem_info *uinfo) |
445 | { |
446 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
447 | struct ad198x_spec *spec = codec->spec; |
448 | static const char * const texts2[] = { "PCM" , "ADC" }; |
449 | static const char * const texts3[] = { "PCM" , "ADC1" , "ADC2" }; |
450 | int num_conns = spec->num_smux_conns; |
451 | |
452 | if (num_conns == 2) |
453 | return snd_hda_enum_helper_info(kcontrol, uinfo, num_items: 2, texts: texts2); |
454 | else if (num_conns == 3) |
455 | return snd_hda_enum_helper_info(kcontrol, uinfo, num_items: 3, texts: texts3); |
456 | else |
457 | return -EINVAL; |
458 | } |
459 | |
460 | static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol, |
461 | struct snd_ctl_elem_value *ucontrol) |
462 | { |
463 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
464 | struct ad198x_spec *spec = codec->spec; |
465 | |
466 | ucontrol->value.enumerated.item[0] = spec->cur_smux; |
467 | return 0; |
468 | } |
469 | |
470 | static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, |
471 | struct snd_ctl_elem_value *ucontrol) |
472 | { |
473 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
474 | struct ad198x_spec *spec = codec->spec; |
475 | unsigned int val = ucontrol->value.enumerated.item[0]; |
476 | hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; |
477 | int num_conns = spec->num_smux_conns; |
478 | |
479 | if (val >= num_conns) |
480 | return -EINVAL; |
481 | if (spec->cur_smux == val) |
482 | return 0; |
483 | spec->cur_smux = val; |
484 | snd_hda_codec_write_cache(codec, nid: dig_out, flags: 0, |
485 | AC_VERB_SET_CONNECT_SEL, parm: val); |
486 | return 1; |
487 | } |
488 | |
489 | static const struct snd_kcontrol_new ad1983_auto_smux_mixer = { |
490 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
491 | .name = "IEC958 Playback Source" , |
492 | .info = ad1983_auto_smux_enum_info, |
493 | .get = ad1983_auto_smux_enum_get, |
494 | .put = ad1983_auto_smux_enum_put, |
495 | }; |
496 | |
497 | static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) |
498 | { |
499 | struct ad198x_spec *spec = codec->spec; |
500 | hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; |
501 | int num_conns; |
502 | |
503 | if (!dig_out) |
504 | return 0; |
505 | num_conns = snd_hda_get_num_conns(codec, nid: dig_out); |
506 | if (num_conns != 2 && num_conns != 3) |
507 | return 0; |
508 | spec->num_smux_conns = num_conns; |
509 | if (!snd_hda_gen_add_kctl(spec: &spec->gen, NULL, temp: &ad1983_auto_smux_mixer)) |
510 | return -ENOMEM; |
511 | return 0; |
512 | } |
513 | |
514 | static int patch_ad1983(struct hda_codec *codec) |
515 | { |
516 | static const hda_nid_t conn_0c[] = { 0x08 }; |
517 | static const hda_nid_t conn_0d[] = { 0x09 }; |
518 | struct ad198x_spec *spec; |
519 | int err; |
520 | |
521 | err = alloc_ad_spec(codec); |
522 | if (err < 0) |
523 | return err; |
524 | spec = codec->spec; |
525 | |
526 | spec->gen.mixer_nid = 0x0e; |
527 | spec->gen.beep_nid = 0x10; |
528 | set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); |
529 | |
530 | /* limit the loopback routes not to confuse the parser */ |
531 | snd_hda_override_conn_list(codec, nid: 0x0c, ARRAY_SIZE(conn_0c), list: conn_0c); |
532 | snd_hda_override_conn_list(codec, nid: 0x0d, ARRAY_SIZE(conn_0d), list: conn_0d); |
533 | |
534 | err = ad198x_parse_auto_config(codec, indep_hp: false); |
535 | if (err < 0) |
536 | goto error; |
537 | err = ad1983_add_spdif_mux_ctl(codec); |
538 | if (err < 0) |
539 | goto error; |
540 | return 0; |
541 | |
542 | error: |
543 | snd_hda_gen_free(codec); |
544 | return err; |
545 | } |
546 | |
547 | |
548 | /* |
549 | * AD1981 HD specific |
550 | */ |
551 | |
552 | static void ad1981_fixup_hp_eapd(struct hda_codec *codec, |
553 | const struct hda_fixup *fix, int action) |
554 | { |
555 | struct ad198x_spec *spec = codec->spec; |
556 | |
557 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
558 | spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; |
559 | spec->eapd_nid = 0x05; |
560 | } |
561 | } |
562 | |
563 | /* set the upper-limit for mixer amp to 0dB for avoiding the possible |
564 | * damage by overloading |
565 | */ |
566 | static void ad1981_fixup_amp_override(struct hda_codec *codec, |
567 | const struct hda_fixup *fix, int action) |
568 | { |
569 | if (action == HDA_FIXUP_ACT_PRE_PROBE) |
570 | snd_hda_override_amp_caps(codec, nid: 0x11, dir: HDA_INPUT, |
571 | caps: (0x17 << AC_AMPCAP_OFFSET_SHIFT) | |
572 | (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | |
573 | (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | |
574 | (1 << AC_AMPCAP_MUTE_SHIFT)); |
575 | } |
576 | |
577 | enum { |
578 | AD1981_FIXUP_AMP_OVERRIDE, |
579 | AD1981_FIXUP_HP_EAPD, |
580 | }; |
581 | |
582 | static const struct hda_fixup ad1981_fixups[] = { |
583 | [AD1981_FIXUP_AMP_OVERRIDE] = { |
584 | .type = HDA_FIXUP_FUNC, |
585 | .v.func = ad1981_fixup_amp_override, |
586 | }, |
587 | [AD1981_FIXUP_HP_EAPD] = { |
588 | .type = HDA_FIXUP_FUNC, |
589 | .v.func = ad1981_fixup_hp_eapd, |
590 | .chained = true, |
591 | .chain_id = AD1981_FIXUP_AMP_OVERRIDE, |
592 | }, |
593 | }; |
594 | |
595 | static const struct snd_pci_quirk ad1981_fixup_tbl[] = { |
596 | SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo" , AD1981_FIXUP_AMP_OVERRIDE), |
597 | SND_PCI_QUIRK_VENDOR(0x103c, "HP" , AD1981_FIXUP_HP_EAPD), |
598 | SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo" , AD1981_FIXUP_AMP_OVERRIDE), |
599 | /* HP nx6320 (reversed SSID, H/W bug) */ |
600 | SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320" , AD1981_FIXUP_HP_EAPD), |
601 | {} |
602 | }; |
603 | |
604 | static int patch_ad1981(struct hda_codec *codec) |
605 | { |
606 | struct ad198x_spec *spec; |
607 | int err; |
608 | |
609 | err = alloc_ad_spec(codec); |
610 | if (err < 0) |
611 | return -ENOMEM; |
612 | spec = codec->spec; |
613 | |
614 | spec->gen.mixer_nid = 0x0e; |
615 | spec->gen.beep_nid = 0x10; |
616 | set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); |
617 | |
618 | snd_hda_pick_fixup(codec, NULL, quirk: ad1981_fixup_tbl, fixlist: ad1981_fixups); |
619 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PRE_PROBE); |
620 | |
621 | err = ad198x_parse_auto_config(codec, indep_hp: false); |
622 | if (err < 0) |
623 | goto error; |
624 | err = ad1983_add_spdif_mux_ctl(codec); |
625 | if (err < 0) |
626 | goto error; |
627 | |
628 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PROBE); |
629 | |
630 | return 0; |
631 | |
632 | error: |
633 | snd_hda_gen_free(codec); |
634 | return err; |
635 | } |
636 | |
637 | |
638 | /* |
639 | * AD1988 |
640 | * |
641 | * Output pins and routes |
642 | * |
643 | * Pin Mix Sel DAC (*) |
644 | * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06 |
645 | * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06 |
646 | * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a |
647 | * port-D 0x12 (mute/hp) <- 0x29 <- 04 |
648 | * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a |
649 | * port-F 0x16 (mute) <- 0x2a <- 06 |
650 | * port-G 0x24 (mute) <- 0x27 <- 05 |
651 | * port-H 0x25 (mute) <- 0x28 <- 0a |
652 | * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06 |
653 | * |
654 | * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah |
655 | * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug. |
656 | * |
657 | * Input pins and routes |
658 | * |
659 | * pin boost mix input # / adc input # |
660 | * port-A 0x11 -> 0x38 -> mix 2, ADC 0 |
661 | * port-B 0x14 -> 0x39 -> mix 0, ADC 1 |
662 | * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2 |
663 | * port-D 0x12 -> 0x3d -> mix 3, ADC 8 |
664 | * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4 |
665 | * port-F 0x16 -> 0x3b -> mix 5, ADC 3 |
666 | * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6 |
667 | * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7 |
668 | * |
669 | * |
670 | * DAC assignment |
671 | * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03 |
672 | * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03 |
673 | * |
674 | * Inputs of Analog Mix (0x20) |
675 | * 0:Port-B (front mic) |
676 | * 1:Port-C/G/H (line-in) |
677 | * 2:Port-A |
678 | * 3:Port-D (line-in/2) |
679 | * 4:Port-E/G/H (mic-in) |
680 | * 5:Port-F (mic2-in) |
681 | * 6:CD |
682 | * 7:Beep |
683 | * |
684 | * ADC selection |
685 | * 0:Port-A |
686 | * 1:Port-B (front mic-in) |
687 | * 2:Port-C (line-in) |
688 | * 3:Port-F (mic2-in) |
689 | * 4:Port-E (mic-in) |
690 | * 5:CD |
691 | * 6:Port-G |
692 | * 7:Port-H |
693 | * 8:Port-D (line-in/2) |
694 | * 9:Mix |
695 | * |
696 | * Proposed pin assignments by the datasheet |
697 | * |
698 | * 6-stack |
699 | * Port-A front headphone |
700 | * B front mic-in |
701 | * C rear line-in |
702 | * D rear front-out |
703 | * E rear mic-in |
704 | * F rear surround |
705 | * G rear CLFE |
706 | * H rear side |
707 | * |
708 | * 3-stack |
709 | * Port-A front headphone |
710 | * B front mic |
711 | * C rear line-in/surround |
712 | * D rear front-out |
713 | * E rear mic-in/CLFE |
714 | * |
715 | * laptop |
716 | * Port-A headphone |
717 | * B mic-in |
718 | * C docking station |
719 | * D internal speaker (with EAPD) |
720 | * E/F quad mic array |
721 | */ |
722 | |
723 | static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, |
724 | struct snd_ctl_elem_info *uinfo) |
725 | { |
726 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
727 | struct ad198x_spec *spec = codec->spec; |
728 | static const char * const texts[] = { |
729 | "PCM" , "ADC1" , "ADC2" , "ADC3" , |
730 | }; |
731 | int num_conns = spec->num_smux_conns; |
732 | |
733 | if (num_conns > 4) |
734 | num_conns = 4; |
735 | return snd_hda_enum_helper_info(kcontrol, uinfo, num_items: num_conns, texts); |
736 | } |
737 | |
738 | static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol, |
739 | struct snd_ctl_elem_value *ucontrol) |
740 | { |
741 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
742 | struct ad198x_spec *spec = codec->spec; |
743 | |
744 | ucontrol->value.enumerated.item[0] = spec->cur_smux; |
745 | return 0; |
746 | } |
747 | |
748 | static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, |
749 | struct snd_ctl_elem_value *ucontrol) |
750 | { |
751 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
752 | struct ad198x_spec *spec = codec->spec; |
753 | unsigned int val = ucontrol->value.enumerated.item[0]; |
754 | struct nid_path *path; |
755 | int num_conns = spec->num_smux_conns; |
756 | |
757 | if (val >= num_conns) |
758 | return -EINVAL; |
759 | if (spec->cur_smux == val) |
760 | return 0; |
761 | |
762 | mutex_lock(&codec->control_mutex); |
763 | path = snd_hda_get_path_from_idx(codec, |
764 | idx: spec->smux_paths[spec->cur_smux]); |
765 | if (path) |
766 | snd_hda_activate_path(codec, path, enable: false, add_aamix: true); |
767 | path = snd_hda_get_path_from_idx(codec, idx: spec->smux_paths[val]); |
768 | if (path) |
769 | snd_hda_activate_path(codec, path, enable: true, add_aamix: true); |
770 | spec->cur_smux = val; |
771 | mutex_unlock(lock: &codec->control_mutex); |
772 | return 1; |
773 | } |
774 | |
775 | static const struct snd_kcontrol_new ad1988_auto_smux_mixer = { |
776 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
777 | .name = "IEC958 Playback Source" , |
778 | .info = ad1988_auto_smux_enum_info, |
779 | .get = ad1988_auto_smux_enum_get, |
780 | .put = ad1988_auto_smux_enum_put, |
781 | }; |
782 | |
783 | static int ad1988_auto_init(struct hda_codec *codec) |
784 | { |
785 | struct ad198x_spec *spec = codec->spec; |
786 | int i, err; |
787 | |
788 | err = snd_hda_gen_init(codec); |
789 | if (err < 0) |
790 | return err; |
791 | if (!spec->gen.autocfg.dig_outs) |
792 | return 0; |
793 | |
794 | for (i = 0; i < 4; i++) { |
795 | struct nid_path *path; |
796 | path = snd_hda_get_path_from_idx(codec, idx: spec->smux_paths[i]); |
797 | if (path) |
798 | snd_hda_activate_path(codec, path, enable: path->active, add_aamix: false); |
799 | } |
800 | |
801 | return 0; |
802 | } |
803 | |
804 | static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) |
805 | { |
806 | struct ad198x_spec *spec = codec->spec; |
807 | int i, num_conns; |
808 | /* we create four static faked paths, since AD codecs have odd |
809 | * widget connections regarding the SPDIF out source |
810 | */ |
811 | static const struct nid_path fake_paths[4] = { |
812 | { |
813 | .depth = 3, |
814 | .path = { 0x02, 0x1d, 0x1b }, |
815 | .idx = { 0, 0, 0 }, |
816 | .multi = { 0, 0, 0 }, |
817 | }, |
818 | { |
819 | .depth = 4, |
820 | .path = { 0x08, 0x0b, 0x1d, 0x1b }, |
821 | .idx = { 0, 0, 1, 0 }, |
822 | .multi = { 0, 1, 0, 0 }, |
823 | }, |
824 | { |
825 | .depth = 4, |
826 | .path = { 0x09, 0x0b, 0x1d, 0x1b }, |
827 | .idx = { 0, 1, 1, 0 }, |
828 | .multi = { 0, 1, 0, 0 }, |
829 | }, |
830 | { |
831 | .depth = 4, |
832 | .path = { 0x0f, 0x0b, 0x1d, 0x1b }, |
833 | .idx = { 0, 2, 1, 0 }, |
834 | .multi = { 0, 1, 0, 0 }, |
835 | }, |
836 | }; |
837 | |
838 | /* SPDIF source mux appears to be present only on AD1988A */ |
839 | if (!spec->gen.autocfg.dig_outs || |
840 | get_wcaps_type(wcaps: get_wcaps(codec, nid: 0x1d)) != AC_WID_AUD_MIX) |
841 | return 0; |
842 | |
843 | num_conns = snd_hda_get_num_conns(codec, nid: 0x0b) + 1; |
844 | if (num_conns != 3 && num_conns != 4) |
845 | return 0; |
846 | spec->num_smux_conns = num_conns; |
847 | |
848 | for (i = 0; i < num_conns; i++) { |
849 | struct nid_path *path = snd_array_new(array: &spec->gen.paths); |
850 | if (!path) |
851 | return -ENOMEM; |
852 | *path = fake_paths[i]; |
853 | if (!i) |
854 | path->active = 1; |
855 | spec->smux_paths[i] = snd_hda_get_path_idx(codec, path); |
856 | } |
857 | |
858 | if (!snd_hda_gen_add_kctl(spec: &spec->gen, NULL, temp: &ad1988_auto_smux_mixer)) |
859 | return -ENOMEM; |
860 | |
861 | codec->patch_ops.init = ad1988_auto_init; |
862 | |
863 | return 0; |
864 | } |
865 | |
866 | /* |
867 | */ |
868 | |
869 | enum { |
870 | AD1988_FIXUP_6STACK_DIG, |
871 | }; |
872 | |
873 | static const struct hda_fixup ad1988_fixups[] = { |
874 | [AD1988_FIXUP_6STACK_DIG] = { |
875 | .type = HDA_FIXUP_PINS, |
876 | .v.pins = (const struct hda_pintbl[]) { |
877 | { 0x11, 0x02214130 }, /* front-hp */ |
878 | { 0x12, 0x01014010 }, /* line-out */ |
879 | { 0x14, 0x02a19122 }, /* front-mic */ |
880 | { 0x15, 0x01813021 }, /* line-in */ |
881 | { 0x16, 0x01011012 }, /* line-out */ |
882 | { 0x17, 0x01a19020 }, /* mic */ |
883 | { 0x1b, 0x0145f1f0 }, /* SPDIF */ |
884 | { 0x24, 0x01016011 }, /* line-out */ |
885 | { 0x25, 0x01012013 }, /* line-out */ |
886 | { } |
887 | } |
888 | }, |
889 | }; |
890 | |
891 | static const struct hda_model_fixup ad1988_fixup_models[] = { |
892 | { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" }, |
893 | {} |
894 | }; |
895 | |
896 | static int patch_ad1988(struct hda_codec *codec) |
897 | { |
898 | struct ad198x_spec *spec; |
899 | int err; |
900 | |
901 | err = alloc_ad_spec(codec); |
902 | if (err < 0) |
903 | return err; |
904 | spec = codec->spec; |
905 | |
906 | spec->gen.mixer_nid = 0x20; |
907 | spec->gen.mixer_merge_nid = 0x21; |
908 | spec->gen.beep_nid = 0x10; |
909 | set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); |
910 | |
911 | snd_hda_pick_fixup(codec, models: ad1988_fixup_models, NULL, fixlist: ad1988_fixups); |
912 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PRE_PROBE); |
913 | |
914 | err = ad198x_parse_auto_config(codec, indep_hp: true); |
915 | if (err < 0) |
916 | goto error; |
917 | err = ad1988_add_spdif_mux_ctl(codec); |
918 | if (err < 0) |
919 | goto error; |
920 | |
921 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PROBE); |
922 | |
923 | return 0; |
924 | |
925 | error: |
926 | snd_hda_gen_free(codec); |
927 | return err; |
928 | } |
929 | |
930 | |
931 | /* |
932 | * AD1884 / AD1984 |
933 | * |
934 | * port-B - front line/mic-in |
935 | * port-E - aux in/out |
936 | * port-F - aux in/out |
937 | * port-C - rear line/mic-in |
938 | * port-D - rear line/hp-out |
939 | * port-A - front line/hp-out |
940 | * |
941 | * AD1984 = AD1884 + two digital mic-ins |
942 | * |
943 | * AD1883 / AD1884A / AD1984A / AD1984B |
944 | * |
945 | * port-B (0x14) - front mic-in |
946 | * port-E (0x1c) - rear mic-in |
947 | * port-F (0x16) - CD / ext out |
948 | * port-C (0x15) - rear line-in |
949 | * port-D (0x12) - rear line-out |
950 | * port-A (0x11) - front hp-out |
951 | * |
952 | * AD1984A = AD1884A + digital-mic |
953 | * AD1883 = equivalent with AD1984A |
954 | * AD1984B = AD1984A + extra SPDIF-out |
955 | */ |
956 | |
957 | /* set the upper-limit for mixer amp to 0dB for avoiding the possible |
958 | * damage by overloading |
959 | */ |
960 | static void ad1884_fixup_amp_override(struct hda_codec *codec, |
961 | const struct hda_fixup *fix, int action) |
962 | { |
963 | if (action == HDA_FIXUP_ACT_PRE_PROBE) |
964 | snd_hda_override_amp_caps(codec, nid: 0x20, dir: HDA_INPUT, |
965 | caps: (0x17 << AC_AMPCAP_OFFSET_SHIFT) | |
966 | (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | |
967 | (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | |
968 | (1 << AC_AMPCAP_MUTE_SHIFT)); |
969 | } |
970 | |
971 | /* toggle GPIO1 according to the mute state */ |
972 | static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) |
973 | { |
974 | struct hda_codec *codec = private_data; |
975 | struct ad198x_spec *spec = codec->spec; |
976 | |
977 | if (spec->eapd_nid) |
978 | ad_vmaster_eapd_hook(private_data, enabled); |
979 | snd_hda_codec_write_cache(codec, nid: 0x01, flags: 0, |
980 | AC_VERB_SET_GPIO_DATA, |
981 | parm: enabled ? 0x00 : 0x02); |
982 | } |
983 | |
984 | static void ad1884_fixup_hp_eapd(struct hda_codec *codec, |
985 | const struct hda_fixup *fix, int action) |
986 | { |
987 | struct ad198x_spec *spec = codec->spec; |
988 | |
989 | switch (action) { |
990 | case HDA_FIXUP_ACT_PRE_PROBE: |
991 | spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; |
992 | spec->gen.own_eapd_ctl = 1; |
993 | snd_hda_codec_write_cache(codec, nid: 0x01, flags: 0, |
994 | AC_VERB_SET_GPIO_MASK, parm: 0x02); |
995 | snd_hda_codec_write_cache(codec, nid: 0x01, flags: 0, |
996 | AC_VERB_SET_GPIO_DIRECTION, parm: 0x02); |
997 | snd_hda_codec_write_cache(codec, nid: 0x01, flags: 0, |
998 | AC_VERB_SET_GPIO_DATA, parm: 0x02); |
999 | break; |
1000 | case HDA_FIXUP_ACT_PROBE: |
1001 | if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) |
1002 | spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; |
1003 | else |
1004 | spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; |
1005 | break; |
1006 | } |
1007 | } |
1008 | |
1009 | static void ad1884_fixup_thinkpad(struct hda_codec *codec, |
1010 | const struct hda_fixup *fix, int action) |
1011 | { |
1012 | struct ad198x_spec *spec = codec->spec; |
1013 | |
1014 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
1015 | spec->gen.keep_eapd_on = 1; |
1016 | spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; |
1017 | spec->eapd_nid = 0x12; |
1018 | /* Analog PC Beeper - allow firmware/ACPI beeps */ |
1019 | spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT); |
1020 | spec->gen.beep_nid = 0; /* no digital beep */ |
1021 | } |
1022 | } |
1023 | |
1024 | /* set magic COEFs for dmic */ |
1025 | static const struct hda_verb ad1884_dmic_init_verbs[] = { |
1026 | {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, |
1027 | {0x01, AC_VERB_SET_PROC_COEF, 0x08}, |
1028 | {} |
1029 | }; |
1030 | |
1031 | enum { |
1032 | AD1884_FIXUP_AMP_OVERRIDE, |
1033 | AD1884_FIXUP_HP_EAPD, |
1034 | AD1884_FIXUP_DMIC_COEF, |
1035 | AD1884_FIXUP_THINKPAD, |
1036 | AD1884_FIXUP_HP_TOUCHSMART, |
1037 | }; |
1038 | |
1039 | static const struct hda_fixup ad1884_fixups[] = { |
1040 | [AD1884_FIXUP_AMP_OVERRIDE] = { |
1041 | .type = HDA_FIXUP_FUNC, |
1042 | .v.func = ad1884_fixup_amp_override, |
1043 | }, |
1044 | [AD1884_FIXUP_HP_EAPD] = { |
1045 | .type = HDA_FIXUP_FUNC, |
1046 | .v.func = ad1884_fixup_hp_eapd, |
1047 | .chained = true, |
1048 | .chain_id = AD1884_FIXUP_AMP_OVERRIDE, |
1049 | }, |
1050 | [AD1884_FIXUP_DMIC_COEF] = { |
1051 | .type = HDA_FIXUP_VERBS, |
1052 | .v.verbs = ad1884_dmic_init_verbs, |
1053 | }, |
1054 | [AD1884_FIXUP_THINKPAD] = { |
1055 | .type = HDA_FIXUP_FUNC, |
1056 | .v.func = ad1884_fixup_thinkpad, |
1057 | .chained = true, |
1058 | .chain_id = AD1884_FIXUP_DMIC_COEF, |
1059 | }, |
1060 | [AD1884_FIXUP_HP_TOUCHSMART] = { |
1061 | .type = HDA_FIXUP_VERBS, |
1062 | .v.verbs = ad1884_dmic_init_verbs, |
1063 | .chained = true, |
1064 | .chain_id = AD1884_FIXUP_HP_EAPD, |
1065 | }, |
1066 | }; |
1067 | |
1068 | static const struct snd_pci_quirk ad1884_fixup_tbl[] = { |
1069 | SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart" , AD1884_FIXUP_HP_TOUCHSMART), |
1070 | SND_PCI_QUIRK_VENDOR(0x103c, "HP" , AD1884_FIXUP_HP_EAPD), |
1071 | SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad" , AD1884_FIXUP_THINKPAD), |
1072 | {} |
1073 | }; |
1074 | |
1075 | |
1076 | static int patch_ad1884(struct hda_codec *codec) |
1077 | { |
1078 | struct ad198x_spec *spec; |
1079 | int err; |
1080 | |
1081 | err = alloc_ad_spec(codec); |
1082 | if (err < 0) |
1083 | return err; |
1084 | spec = codec->spec; |
1085 | |
1086 | spec->gen.mixer_nid = 0x20; |
1087 | spec->gen.mixer_merge_nid = 0x21; |
1088 | spec->gen.beep_nid = 0x10; |
1089 | set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); |
1090 | |
1091 | snd_hda_pick_fixup(codec, NULL, quirk: ad1884_fixup_tbl, fixlist: ad1884_fixups); |
1092 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PRE_PROBE); |
1093 | |
1094 | err = ad198x_parse_auto_config(codec, indep_hp: true); |
1095 | if (err < 0) |
1096 | goto error; |
1097 | err = ad1983_add_spdif_mux_ctl(codec); |
1098 | if (err < 0) |
1099 | goto error; |
1100 | |
1101 | snd_hda_apply_fixup(codec, action: HDA_FIXUP_ACT_PROBE); |
1102 | |
1103 | return 0; |
1104 | |
1105 | error: |
1106 | snd_hda_gen_free(codec); |
1107 | return err; |
1108 | } |
1109 | |
1110 | /* |
1111 | * AD1882 / AD1882A |
1112 | * |
1113 | * port-A - front hp-out |
1114 | * port-B - front mic-in |
1115 | * port-C - rear line-in, shared surr-out (3stack) |
1116 | * port-D - rear line-out |
1117 | * port-E - rear mic-in, shared clfe-out (3stack) |
1118 | * port-F - rear surr-out (6stack) |
1119 | * port-G - rear clfe-out (6stack) |
1120 | */ |
1121 | |
1122 | static int patch_ad1882(struct hda_codec *codec) |
1123 | { |
1124 | struct ad198x_spec *spec; |
1125 | int err; |
1126 | |
1127 | err = alloc_ad_spec(codec); |
1128 | if (err < 0) |
1129 | return err; |
1130 | spec = codec->spec; |
1131 | |
1132 | spec->gen.mixer_nid = 0x20; |
1133 | spec->gen.mixer_merge_nid = 0x21; |
1134 | spec->gen.beep_nid = 0x10; |
1135 | set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); |
1136 | err = ad198x_parse_auto_config(codec, indep_hp: true); |
1137 | if (err < 0) |
1138 | goto error; |
1139 | err = ad1988_add_spdif_mux_ctl(codec); |
1140 | if (err < 0) |
1141 | goto error; |
1142 | return 0; |
1143 | |
1144 | error: |
1145 | snd_hda_gen_free(codec); |
1146 | return err; |
1147 | } |
1148 | |
1149 | |
1150 | /* |
1151 | * patch entries |
1152 | */ |
1153 | static const struct hda_device_id snd_hda_id_analog[] = { |
1154 | HDA_CODEC_ENTRY(0x11d4184a, "AD1884A" , patch_ad1884), |
1155 | HDA_CODEC_ENTRY(0x11d41882, "AD1882" , patch_ad1882), |
1156 | HDA_CODEC_ENTRY(0x11d41883, "AD1883" , patch_ad1884), |
1157 | HDA_CODEC_ENTRY(0x11d41884, "AD1884" , patch_ad1884), |
1158 | HDA_CODEC_ENTRY(0x11d4194a, "AD1984A" , patch_ad1884), |
1159 | HDA_CODEC_ENTRY(0x11d4194b, "AD1984B" , patch_ad1884), |
1160 | HDA_CODEC_ENTRY(0x11d41981, "AD1981" , patch_ad1981), |
1161 | HDA_CODEC_ENTRY(0x11d41983, "AD1983" , patch_ad1983), |
1162 | HDA_CODEC_ENTRY(0x11d41984, "AD1984" , patch_ad1884), |
1163 | HDA_CODEC_ENTRY(0x11d41986, "AD1986A" , patch_ad1986a), |
1164 | HDA_CODEC_ENTRY(0x11d41988, "AD1988" , patch_ad1988), |
1165 | HDA_CODEC_ENTRY(0x11d4198b, "AD1988B" , patch_ad1988), |
1166 | HDA_CODEC_ENTRY(0x11d4882a, "AD1882A" , patch_ad1882), |
1167 | HDA_CODEC_ENTRY(0x11d4989a, "AD1989A" , patch_ad1988), |
1168 | HDA_CODEC_ENTRY(0x11d4989b, "AD1989B" , patch_ad1988), |
1169 | {} /* terminator */ |
1170 | }; |
1171 | MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); |
1172 | |
1173 | MODULE_LICENSE("GPL" ); |
1174 | MODULE_DESCRIPTION("Analog Devices HD-audio codec" ); |
1175 | |
1176 | static struct hda_codec_driver analog_driver = { |
1177 | .id = snd_hda_id_analog, |
1178 | }; |
1179 | |
1180 | module_hda_codec_driver(analog_driver); |
1181 | |