Commit | Line | Data |
---|---|---|
c577b8a1 JC |
1 | /* |
2 | * Universal Interface for Intel High Definition Audio Codec | |
3 | * | |
8e86597f | 4 | * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec |
c577b8a1 | 5 | * |
8e86597f LW |
6 | * (C) 2006-2009 VIA Technology, Inc. |
7 | * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> | |
c577b8a1 JC |
8 | * |
9 | * This driver is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This driver is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ | |
377ff31a | 25 | /* */ |
c577b8a1 | 26 | /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ |
377ff31a LW |
27 | /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ |
28 | /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ | |
c577b8a1 | 29 | /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ |
377ff31a LW |
30 | /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ |
31 | /* 2007-09-17 Lydia Wang Add VT1708B codec support */ | |
76d9b0dd | 32 | /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ |
fb4cb772 | 33 | /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ |
377ff31a LW |
34 | /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ |
35 | /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ | |
36 | /* 2008-04-09 Lydia Wang Add Independent HP feature */ | |
98aa34c0 | 37 | /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ |
377ff31a | 38 | /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ |
8e86597f LW |
39 | /* 2009-02-16 Logan Li Add support for VT1718S */ |
40 | /* 2009-03-13 Logan Li Add support for VT1716S */ | |
41 | /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ | |
42 | /* 2009-07-08 Lydia Wang Add support for VT2002P */ | |
43 | /* 2009-07-21 Lydia Wang Add support for VT1812 */ | |
36dd5c4a | 44 | /* 2009-09-19 Lydia Wang Add support for VT1818S */ |
377ff31a | 45 | /* */ |
c577b8a1 JC |
46 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
47 | ||
48 | ||
c577b8a1 JC |
49 | #include <linux/init.h> |
50 | #include <linux/delay.h> | |
51 | #include <linux/slab.h> | |
da155d5b | 52 | #include <linux/module.h> |
c577b8a1 | 53 | #include <sound/core.h> |
0aa62aef | 54 | #include <sound/asoundef.h> |
be57bfff | 55 | #include <sound/hda_codec.h> |
c577b8a1 | 56 | #include "hda_local.h" |
128bc4ba | 57 | #include "hda_auto_parser.h" |
1835a0f9 | 58 | #include "hda_jack.h" |
b3f6008f | 59 | #include "hda_generic.h" |
c577b8a1 | 60 | |
c577b8a1 | 61 | /* Pin Widget NID */ |
d949cac1 HW |
62 | #define VT1708_HP_PIN_NID 0x20 |
63 | #define VT1708_CD_PIN_NID 0x24 | |
c577b8a1 | 64 | |
d7426329 HW |
65 | enum VIA_HDA_CODEC { |
66 | UNKNOWN = -1, | |
67 | VT1708, | |
68 | VT1709_10CH, | |
69 | VT1709_6CH, | |
70 | VT1708B_8CH, | |
71 | VT1708B_4CH, | |
72 | VT1708S, | |
518bf3ba | 73 | VT1708BCE, |
d7426329 | 74 | VT1702, |
eb7188ca | 75 | VT1718S, |
f3db423d | 76 | VT1716S, |
25eaba2f | 77 | VT2002P, |
ab6734e7 | 78 | VT1812, |
11890956 | 79 | VT1802, |
43737e0a | 80 | VT1705CF, |
6121b84a | 81 | VT1808, |
d7426329 HW |
82 | CODEC_TYPES, |
83 | }; | |
84 | ||
11890956 LW |
85 | #define VT2002P_COMPATIBLE(spec) \ |
86 | ((spec)->codec_type == VT2002P ||\ | |
87 | (spec)->codec_type == VT1812 ||\ | |
88 | (spec)->codec_type == VT1802) | |
89 | ||
1f2e99fe | 90 | struct via_spec { |
b3f6008f TI |
91 | struct hda_gen_spec gen; |
92 | ||
1f2e99fe | 93 | /* HP mode source */ |
f3db423d | 94 | unsigned int dmic_enabled; |
1f2e99fe LW |
95 | enum VIA_HDA_CODEC codec_type; |
96 | ||
e9d010c2 TI |
97 | /* analog low-power control */ |
98 | bool alc_mode; | |
99 | ||
1f2e99fe | 100 | /* work to check hp jack state */ |
187d333e | 101 | int hp_work_active; |
e06e5a29 | 102 | int vt1708_jack_detect; |
1f2e99fe LW |
103 | }; |
104 | ||
0341ccd7 | 105 | static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); |
b3f6008f TI |
106 | static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, |
107 | struct hda_codec *codec, | |
108 | struct snd_pcm_substream *substream, | |
109 | int action); | |
b3f6008f | 110 | |
225068ab TI |
111 | static const struct hda_codec_ops via_patch_ops; /* defined below */ |
112 | ||
b3f6008f | 113 | static struct via_spec *via_new_spec(struct hda_codec *codec) |
5b0cb1d8 JK |
114 | { |
115 | struct via_spec *spec; | |
116 | ||
117 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
118 | if (spec == NULL) | |
119 | return NULL; | |
120 | ||
121 | codec->spec = spec; | |
b3f6008f | 122 | snd_hda_gen_spec_init(&spec->gen); |
0341ccd7 LW |
123 | spec->codec_type = get_codec_type(codec); |
124 | /* VT1708BCE & VT1708S are almost same */ | |
125 | if (spec->codec_type == VT1708BCE) | |
126 | spec->codec_type = VT1708S; | |
13961170 | 127 | spec->gen.indep_hp = 1; |
05909d5c | 128 | spec->gen.keep_eapd_on = 1; |
b3f6008f | 129 | spec->gen.pcm_playback_hook = via_playback_pcm_hook; |
74f14b36 | 130 | spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; |
967b1307 | 131 | codec->power_save_node = 1; |
688b12cc | 132 | spec->gen.power_down_unused = 1; |
225068ab | 133 | codec->patch_ops = via_patch_ops; |
5b0cb1d8 JK |
134 | return spec; |
135 | } | |
136 | ||
744ff5f4 | 137 | static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) |
d7426329 | 138 | { |
7639a06c | 139 | u32 vendor_id = codec->core.vendor_id; |
d7426329 HW |
140 | u16 ven_id = vendor_id >> 16; |
141 | u16 dev_id = vendor_id & 0xffff; | |
142 | enum VIA_HDA_CODEC codec_type; | |
143 | ||
144 | /* get codec type */ | |
145 | if (ven_id != 0x1106) | |
146 | codec_type = UNKNOWN; | |
147 | else if (dev_id >= 0x1708 && dev_id <= 0x170b) | |
148 | codec_type = VT1708; | |
149 | else if (dev_id >= 0xe710 && dev_id <= 0xe713) | |
150 | codec_type = VT1709_10CH; | |
151 | else if (dev_id >= 0xe714 && dev_id <= 0xe717) | |
152 | codec_type = VT1709_6CH; | |
518bf3ba | 153 | else if (dev_id >= 0xe720 && dev_id <= 0xe723) { |
d7426329 | 154 | codec_type = VT1708B_8CH; |
518bf3ba LW |
155 | if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) |
156 | codec_type = VT1708BCE; | |
157 | } else if (dev_id >= 0xe724 && dev_id <= 0xe727) | |
d7426329 HW |
158 | codec_type = VT1708B_4CH; |
159 | else if ((dev_id & 0xfff) == 0x397 | |
160 | && (dev_id >> 12) < 8) | |
161 | codec_type = VT1708S; | |
162 | else if ((dev_id & 0xfff) == 0x398 | |
163 | && (dev_id >> 12) < 8) | |
164 | codec_type = VT1702; | |
eb7188ca LW |
165 | else if ((dev_id & 0xfff) == 0x428 |
166 | && (dev_id >> 12) < 8) | |
167 | codec_type = VT1718S; | |
f3db423d LW |
168 | else if (dev_id == 0x0433 || dev_id == 0xa721) |
169 | codec_type = VT1716S; | |
bb3c6bfc LW |
170 | else if (dev_id == 0x0441 || dev_id == 0x4441) |
171 | codec_type = VT1718S; | |
25eaba2f LW |
172 | else if (dev_id == 0x0438 || dev_id == 0x4438) |
173 | codec_type = VT2002P; | |
ab6734e7 LW |
174 | else if (dev_id == 0x0448) |
175 | codec_type = VT1812; | |
36dd5c4a LW |
176 | else if (dev_id == 0x0440) |
177 | codec_type = VT1708S; | |
11890956 LW |
178 | else if ((dev_id & 0xfff) == 0x446) |
179 | codec_type = VT1802; | |
43737e0a LW |
180 | else if (dev_id == 0x4760) |
181 | codec_type = VT1705CF; | |
6121b84a LW |
182 | else if (dev_id == 0x4761 || dev_id == 0x4762) |
183 | codec_type = VT1808; | |
d7426329 HW |
184 | else |
185 | codec_type = UNKNOWN; | |
186 | return codec_type; | |
187 | }; | |
188 | ||
ada509ec TI |
189 | static void analog_low_current_mode(struct hda_codec *codec); |
190 | static bool is_aa_path_mute(struct hda_codec *codec); | |
1f2e99fe | 191 | |
187d333e TI |
192 | #define hp_detect_with_aa(codec) \ |
193 | (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ | |
194 | !is_aa_path_mute(codec)) | |
195 | ||
b3f6008f | 196 | static void vt1708_stop_hp_work(struct hda_codec *codec) |
1f2e99fe | 197 | { |
b3f6008f TI |
198 | struct via_spec *spec = codec->spec; |
199 | if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) | |
1f2e99fe | 200 | return; |
187d333e | 201 | if (spec->hp_work_active) { |
b3f6008f | 202 | snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); |
7eaa9161 | 203 | codec->jackpoll_interval = 0; |
b3f6008f TI |
204 | cancel_delayed_work_sync(&codec->jackpoll_work); |
205 | spec->hp_work_active = false; | |
187d333e | 206 | } |
1f2e99fe LW |
207 | } |
208 | ||
b3f6008f | 209 | static void vt1708_update_hp_work(struct hda_codec *codec) |
1f2e99fe | 210 | { |
b3f6008f TI |
211 | struct via_spec *spec = codec->spec; |
212 | if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) | |
1f2e99fe | 213 | return; |
05dc0fc9 | 214 | if (spec->vt1708_jack_detect) { |
187d333e | 215 | if (!spec->hp_work_active) { |
b3f6008f TI |
216 | codec->jackpoll_interval = msecs_to_jiffies(100); |
217 | snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); | |
2f35c630 | 218 | schedule_delayed_work(&codec->jackpoll_work, 0); |
b3f6008f | 219 | spec->hp_work_active = true; |
187d333e | 220 | } |
b3f6008f TI |
221 | } else if (!hp_detect_with_aa(codec)) |
222 | vt1708_stop_hp_work(codec); | |
1f2e99fe | 223 | } |
f5271101 | 224 | |
24088a58 TI |
225 | static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, |
226 | struct snd_ctl_elem_info *uinfo) | |
227 | { | |
dda415d4 | 228 | return snd_hda_enum_bool_helper_info(kcontrol, uinfo); |
24088a58 TI |
229 | } |
230 | ||
231 | static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, | |
232 | struct snd_ctl_elem_value *ucontrol) | |
233 | { | |
234 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
735c75cf TI |
235 | struct via_spec *spec = codec->spec; |
236 | ||
237 | ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused; | |
24088a58 TI |
238 | return 0; |
239 | } | |
240 | ||
241 | static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, | |
242 | struct snd_ctl_elem_value *ucontrol) | |
243 | { | |
244 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
245 | struct via_spec *spec = codec->spec; | |
688b12cc | 246 | bool val = !!ucontrol->value.enumerated.item[0]; |
24088a58 | 247 | |
735c75cf | 248 | if (val == spec->gen.power_down_unused) |
24088a58 | 249 | return 0; |
735c75cf | 250 | /* codec->power_save_node = val; */ /* widget PM seems yet broken */ |
688b12cc | 251 | spec->gen.power_down_unused = val; |
e9d010c2 | 252 | analog_low_current_mode(codec); |
24088a58 TI |
253 | return 1; |
254 | } | |
255 | ||
0e8f9862 | 256 | static const struct snd_kcontrol_new via_pin_power_ctl_enum = { |
24088a58 TI |
257 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
258 | .name = "Dynamic Power-Control", | |
259 | .info = via_pin_power_ctl_info, | |
260 | .get = via_pin_power_ctl_get, | |
261 | .put = via_pin_power_ctl_put, | |
262 | }; | |
263 | ||
4738465c | 264 | #ifdef CONFIG_SND_HDA_INPUT_BEEP |
4738465c | 265 | /* additional beep mixers; the actual parameters are overwritten at build */ |
0e8f9862 | 266 | static const struct snd_kcontrol_new via_beep_mixer[] = { |
4738465c TK |
267 | HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), |
268 | HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), | |
4738465c TK |
269 | }; |
270 | ||
0e8f9862 TI |
271 | static int set_beep_amp(struct via_spec *spec, hda_nid_t nid, |
272 | int idx, int dir) | |
4738465c | 273 | { |
0e8f9862 TI |
274 | struct snd_kcontrol_new *knew; |
275 | unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); | |
276 | int i; | |
4738465c | 277 | |
0e8f9862 TI |
278 | spec->gen.beep_nid = nid; |
279 | for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) { | |
280 | knew = snd_hda_gen_add_kctl(&spec->gen, NULL, | |
281 | &via_beep_mixer[i]); | |
282 | if (!knew) | |
283 | return -ENOMEM; | |
284 | knew->private_value = beep_amp; | |
4738465c TK |
285 | } |
286 | return 0; | |
287 | } | |
288 | ||
0e8f9862 | 289 | static int auto_parse_beep(struct hda_codec *codec) |
4738465c TK |
290 | { |
291 | struct via_spec *spec = codec->spec; | |
292 | hda_nid_t nid; | |
293 | ||
294 | for_each_hda_codec_node(nid, codec) | |
0e8f9862 TI |
295 | if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) |
296 | return set_beep_amp(spec, nid, 0, HDA_OUTPUT); | |
297 | return 0; | |
4738465c TK |
298 | } |
299 | #else | |
0e8f9862 | 300 | #define auto_parse_beep(codec) 0 |
4738465c | 301 | #endif |
24088a58 | 302 | |
b3f6008f TI |
303 | /* check AA path's mute status */ |
304 | static bool is_aa_path_mute(struct hda_codec *codec) | |
0aa62aef | 305 | { |
cdc1784d | 306 | struct via_spec *spec = codec->spec; |
b3f6008f | 307 | const struct hda_amp_list *p; |
0186f4f4 | 308 | int ch, v; |
cdc1784d | 309 | |
0186f4f4 TI |
310 | p = spec->gen.loopback.amplist; |
311 | if (!p) | |
312 | return true; | |
313 | for (; p->nid; p++) { | |
b3f6008f TI |
314 | for (ch = 0; ch < 2; ch++) { |
315 | v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, | |
316 | p->idx); | |
317 | if (!(v & HDA_AMP_MUTE) && v > 0) | |
318 | return false; | |
319 | } | |
3b607e3d | 320 | } |
b3f6008f | 321 | return true; |
3b607e3d TI |
322 | } |
323 | ||
b3f6008f TI |
324 | /* enter/exit analog low-current mode */ |
325 | static void __analog_low_current_mode(struct hda_codec *codec, bool force) | |
3b607e3d TI |
326 | { |
327 | struct via_spec *spec = codec->spec; | |
b3f6008f TI |
328 | bool enable; |
329 | unsigned int verb, parm; | |
3b607e3d | 330 | |
967b1307 | 331 | if (!codec->power_save_node) |
b3f6008f TI |
332 | enable = false; |
333 | else | |
334 | enable = is_aa_path_mute(codec) && !spec->gen.active_streams; | |
335 | if (enable == spec->alc_mode && !force) | |
3b607e3d | 336 | return; |
b3f6008f | 337 | spec->alc_mode = enable; |
3b607e3d | 338 | |
b3f6008f TI |
339 | /* decide low current mode's verb & parameter */ |
340 | switch (spec->codec_type) { | |
341 | case VT1708B_8CH: | |
342 | case VT1708B_4CH: | |
343 | verb = 0xf70; | |
344 | parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ | |
345 | break; | |
346 | case VT1708S: | |
347 | case VT1718S: | |
348 | case VT1716S: | |
349 | verb = 0xf73; | |
350 | parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ | |
351 | break; | |
352 | case VT1702: | |
353 | verb = 0xf73; | |
354 | parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ | |
355 | break; | |
356 | case VT2002P: | |
357 | case VT1812: | |
358 | case VT1802: | |
359 | verb = 0xf93; | |
360 | parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ | |
361 | break; | |
362 | case VT1705CF: | |
363 | case VT1808: | |
364 | verb = 0xf82; | |
365 | parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ | |
366 | break; | |
367 | default: | |
368 | return; /* other codecs are not supported */ | |
f5271101 LW |
369 | } |
370 | /* send verb */ | |
7639a06c | 371 | snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); |
a86a88ea TI |
372 | } |
373 | ||
b3f6008f TI |
374 | static void analog_low_current_mode(struct hda_codec *codec) |
375 | { | |
376 | return __analog_low_current_mode(codec, false); | |
377 | } | |
a86a88ea | 378 | |
b3f6008f TI |
379 | static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, |
380 | struct hda_codec *codec, | |
381 | struct snd_pcm_substream *substream, | |
382 | int action) | |
a86a88ea | 383 | { |
b3f6008f TI |
384 | analog_low_current_mode(codec); |
385 | vt1708_update_hp_work(codec); | |
a86a88ea TI |
386 | } |
387 | ||
b3f6008f | 388 | static void via_free(struct hda_codec *codec) |
a86a88ea | 389 | { |
b3f6008f | 390 | vt1708_stop_hp_work(codec); |
a8dca460 | 391 | snd_hda_gen_free(codec); |
a86a88ea TI |
392 | } |
393 | ||
b3f6008f TI |
394 | #ifdef CONFIG_PM |
395 | static int via_suspend(struct hda_codec *codec) | |
a86a88ea TI |
396 | { |
397 | struct via_spec *spec = codec->spec; | |
b3f6008f | 398 | vt1708_stop_hp_work(codec); |
d7a99cce | 399 | |
2c38d990 TI |
400 | /* Fix pop noise on headphones */ |
401 | if (spec->codec_type == VT1802) | |
402 | snd_hda_shutup_pins(codec); | |
d7a99cce | 403 | |
a86a88ea TI |
404 | return 0; |
405 | } | |
6b6d0007 TI |
406 | |
407 | static int via_resume(struct hda_codec *codec) | |
408 | { | |
409 | /* some delay here to make jack detection working (bko#98921) */ | |
410 | msleep(10); | |
411 | codec->patch_ops.init(codec); | |
412 | regcache_sync(codec->core.regmap); | |
413 | return 0; | |
414 | } | |
b3f6008f | 415 | #endif |
a86a88ea | 416 | |
b3f6008f TI |
417 | #ifdef CONFIG_PM |
418 | static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) | |
a86a88ea TI |
419 | { |
420 | struct via_spec *spec = codec->spec; | |
b3f6008f TI |
421 | analog_low_current_mode(codec); |
422 | vt1708_update_hp_work(codec); | |
423 | return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); | |
424 | } | |
425 | #endif | |
a86a88ea | 426 | |
b3f6008f TI |
427 | /* |
428 | */ | |
a86a88ea | 429 | |
b3f6008f | 430 | static int via_init(struct hda_codec *codec); |
a86a88ea | 431 | |
b3f6008f | 432 | static const struct hda_codec_ops via_patch_ops = { |
0e8f9862 | 433 | .build_controls = snd_hda_gen_build_controls, |
b3f6008f TI |
434 | .build_pcms = snd_hda_gen_build_pcms, |
435 | .init = via_init, | |
436 | .free = via_free, | |
437 | .unsol_event = snd_hda_jack_unsol_event, | |
438 | #ifdef CONFIG_PM | |
439 | .suspend = via_suspend, | |
6b6d0007 | 440 | .resume = via_resume, |
b3f6008f TI |
441 | .check_power_status = via_check_power_status, |
442 | #endif | |
443 | }; | |
a86a88ea | 444 | |
c577b8a1 | 445 | |
b3f6008f TI |
446 | static const struct hda_verb vt1708_init_verbs[] = { |
447 | /* power down jack detect function */ | |
448 | {0x1, 0xf81, 0x1}, | |
449 | { } | |
450 | }; | |
76d9b0dd HW |
451 | static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) |
452 | { | |
453 | unsigned int def_conf; | |
454 | unsigned char seqassoc; | |
455 | ||
2f334f92 | 456 | def_conf = snd_hda_codec_get_pincfg(codec, nid); |
76d9b0dd HW |
457 | seqassoc = (unsigned char) get_defcfg_association(def_conf); |
458 | seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); | |
82ef9e45 LW |
459 | if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE |
460 | && (seqassoc == 0xf0 || seqassoc == 0xff)) { | |
461 | def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); | |
462 | snd_hda_codec_set_pincfg(codec, nid, def_conf); | |
76d9b0dd HW |
463 | } |
464 | ||
465 | return; | |
466 | } | |
467 | ||
e06e5a29 | 468 | static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, |
1f2e99fe LW |
469 | struct snd_ctl_elem_value *ucontrol) |
470 | { | |
471 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
472 | struct via_spec *spec = codec->spec; | |
473 | ||
474 | if (spec->codec_type != VT1708) | |
475 | return 0; | |
e06e5a29 | 476 | ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; |
1f2e99fe LW |
477 | return 0; |
478 | } | |
479 | ||
e06e5a29 | 480 | static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, |
1f2e99fe LW |
481 | struct snd_ctl_elem_value *ucontrol) |
482 | { | |
483 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
484 | struct via_spec *spec = codec->spec; | |
187d333e | 485 | int val; |
1f2e99fe LW |
486 | |
487 | if (spec->codec_type != VT1708) | |
488 | return 0; | |
187d333e TI |
489 | val = !!ucontrol->value.integer.value[0]; |
490 | if (spec->vt1708_jack_detect == val) | |
491 | return 0; | |
492 | spec->vt1708_jack_detect = val; | |
b3f6008f | 493 | vt1708_update_hp_work(codec); |
187d333e | 494 | return 1; |
1f2e99fe LW |
495 | } |
496 | ||
0e8f9862 | 497 | static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { |
e06e5a29 TI |
498 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
499 | .name = "Jack Detect", | |
500 | .count = 1, | |
501 | .info = snd_ctl_boolean_mono_info, | |
502 | .get = vt1708_jack_detect_get, | |
503 | .put = vt1708_jack_detect_put, | |
1f2e99fe LW |
504 | }; |
505 | ||
4abdbd1c TI |
506 | static const struct badness_table via_main_out_badness = { |
507 | .no_primary_dac = 0x10000, | |
508 | .no_dac = 0x4000, | |
509 | .shared_primary = 0x10000, | |
510 | .shared_surr = 0x20, | |
511 | .shared_clfe = 0x20, | |
512 | .shared_surr_main = 0x20, | |
513 | }; | |
514 | static const struct badness_table via_extra_out_badness = { | |
515 | .no_primary_dac = 0x4000, | |
516 | .no_dac = 0x4000, | |
517 | .shared_primary = 0x12, | |
518 | .shared_surr = 0x20, | |
519 | .shared_clfe = 0x20, | |
520 | .shared_surr_main = 0x10, | |
521 | }; | |
522 | ||
b3f6008f TI |
523 | static int via_parse_auto_config(struct hda_codec *codec) |
524 | { | |
525 | struct via_spec *spec = codec->spec; | |
526 | int err; | |
527 | ||
4abdbd1c TI |
528 | spec->gen.main_out_badness = &via_main_out_badness; |
529 | spec->gen.extra_out_badness = &via_extra_out_badness; | |
530 | ||
b3f6008f TI |
531 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); |
532 | if (err < 0) | |
533 | return err; | |
534 | ||
535 | err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); | |
536 | if (err < 0) | |
537 | return err; | |
538 | ||
0e8f9862 TI |
539 | err = auto_parse_beep(codec); |
540 | if (err < 0) | |
541 | return err; | |
542 | ||
543 | if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum)) | |
544 | return -ENOMEM; | |
545 | ||
688b12cc | 546 | /* disable widget PM at start for compatibility */ |
967b1307 | 547 | codec->power_save_node = 0; |
688b12cc | 548 | spec->gen.power_down_unused = 0; |
b3f6008f TI |
549 | return 0; |
550 | } | |
551 | ||
5d41762a TI |
552 | static int via_init(struct hda_codec *codec) |
553 | { | |
e9d010c2 | 554 | /* init power states */ |
e9d010c2 TI |
555 | __analog_low_current_mode(codec, true); |
556 | ||
b3f6008f | 557 | snd_hda_gen_init(codec); |
4a918ffe | 558 | |
b3f6008f | 559 | vt1708_update_hp_work(codec); |
25eaba2f | 560 | |
c577b8a1 JC |
561 | return 0; |
562 | } | |
563 | ||
f672f65a DH |
564 | static int vt1708_build_controls(struct hda_codec *codec) |
565 | { | |
566 | /* In order not to create "Phantom Jack" controls, | |
567 | temporary enable jackpoll */ | |
568 | int err; | |
569 | int old_interval = codec->jackpoll_interval; | |
570 | codec->jackpoll_interval = msecs_to_jiffies(100); | |
0e8f9862 | 571 | err = snd_hda_gen_build_controls(codec); |
f672f65a DH |
572 | codec->jackpoll_interval = old_interval; |
573 | return err; | |
574 | } | |
575 | ||
b3f6008f | 576 | static int vt1708_build_pcms(struct hda_codec *codec) |
337b9d02 TI |
577 | { |
578 | struct via_spec *spec = codec->spec; | |
b3f6008f TI |
579 | int i, err; |
580 | ||
581 | err = snd_hda_gen_build_pcms(codec); | |
7639a06c | 582 | if (err < 0 || codec->core.vendor_id != 0x11061708) |
b3f6008f TI |
583 | return err; |
584 | ||
585 | /* We got noisy outputs on the right channel on VT1708 when | |
586 | * 24bit samples are used. Until any workaround is found, | |
587 | * disable the 24bit format, so far. | |
588 | */ | |
bbbc7e85 TI |
589 | for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { |
590 | struct hda_pcm *info = spec->gen.pcm_rec[i]; | |
591 | if (!info) | |
592 | continue; | |
b3f6008f TI |
593 | if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || |
594 | info->pcm_type != HDA_PCM_TYPE_AUDIO) | |
595 | continue; | |
596 | info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = | |
597 | SNDRV_PCM_FMTBIT_S16_LE; | |
337b9d02 | 598 | } |
b3f6008f | 599 | |
1c55d521 | 600 | return 0; |
337b9d02 TI |
601 | } |
602 | ||
c577b8a1 JC |
603 | static int patch_vt1708(struct hda_codec *codec) |
604 | { | |
605 | struct via_spec *spec; | |
606 | int err; | |
607 | ||
608 | /* create a codec specific record */ | |
5b0cb1d8 | 609 | spec = via_new_spec(codec); |
c577b8a1 JC |
610 | if (spec == NULL) |
611 | return -ENOMEM; | |
612 | ||
225068ab TI |
613 | /* override some patch_ops */ |
614 | codec->patch_ops.build_controls = vt1708_build_controls; | |
615 | codec->patch_ops.build_pcms = vt1708_build_pcms; | |
b3f6008f TI |
616 | spec->gen.mixer_nid = 0x17; |
617 | ||
618 | /* set jackpoll_interval while parsing the codec */ | |
619 | codec->jackpoll_interval = msecs_to_jiffies(100); | |
620 | spec->vt1708_jack_detect = 1; | |
621 | ||
622 | /* don't support the input jack switching due to lack of unsol event */ | |
623 | /* (it may work with polling, though, but it needs testing) */ | |
624 | spec->gen.suppress_auto_mic = 1; | |
eb33ccf7 TI |
625 | /* Some machines show the broken speaker mute */ |
626 | spec->gen.auto_mute_via_amp = 1; | |
620e2b28 | 627 | |
12daef65 TI |
628 | /* Add HP and CD pin config connect bit re-config action */ |
629 | vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); | |
630 | vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); | |
631 | ||
f8bfc628 TI |
632 | err = snd_hda_add_verbs(codec, vt1708_init_verbs); |
633 | if (err < 0) | |
634 | goto error; | |
635 | ||
c577b8a1 | 636 | /* automatic parse from the BIOS config */ |
12daef65 | 637 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
638 | if (err < 0) |
639 | goto error; | |
c577b8a1 | 640 | |
12daef65 | 641 | /* add jack detect on/off control */ |
0e8f9862 TI |
642 | if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { |
643 | err = -ENOMEM; | |
644 | goto error; | |
645 | } | |
c577b8a1 | 646 | |
b3f6008f TI |
647 | /* clear jackpoll_interval again; it's set dynamically */ |
648 | codec->jackpoll_interval = 0; | |
c577b8a1 | 649 | |
c577b8a1 | 650 | return 0; |
fcbdcc1a TI |
651 | |
652 | error: | |
653 | via_free(codec); | |
654 | return err; | |
c577b8a1 JC |
655 | } |
656 | ||
ddd304d8 | 657 | static int patch_vt1709(struct hda_codec *codec) |
c577b8a1 JC |
658 | { |
659 | struct via_spec *spec; | |
660 | int err; | |
661 | ||
662 | /* create a codec specific record */ | |
5b0cb1d8 | 663 | spec = via_new_spec(codec); |
c577b8a1 JC |
664 | if (spec == NULL) |
665 | return -ENOMEM; | |
666 | ||
b3f6008f | 667 | spec->gen.mixer_nid = 0x18; |
620e2b28 | 668 | |
12daef65 | 669 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
670 | if (err < 0) |
671 | goto error; | |
c577b8a1 | 672 | |
f7278fd0 | 673 | return 0; |
fcbdcc1a TI |
674 | |
675 | error: | |
676 | via_free(codec); | |
677 | return err; | |
f7278fd0 JC |
678 | } |
679 | ||
518bf3ba | 680 | static int patch_vt1708S(struct hda_codec *codec); |
ddd304d8 | 681 | static int patch_vt1708B(struct hda_codec *codec) |
f7278fd0 JC |
682 | { |
683 | struct via_spec *spec; | |
684 | int err; | |
685 | ||
518bf3ba LW |
686 | if (get_codec_type(codec) == VT1708BCE) |
687 | return patch_vt1708S(codec); | |
ddd304d8 | 688 | |
f7278fd0 | 689 | /* create a codec specific record */ |
5b0cb1d8 | 690 | spec = via_new_spec(codec); |
f7278fd0 JC |
691 | if (spec == NULL) |
692 | return -ENOMEM; | |
693 | ||
b3f6008f | 694 | spec->gen.mixer_nid = 0x16; |
620e2b28 | 695 | |
f7278fd0 | 696 | /* automatic parse from the BIOS config */ |
12daef65 | 697 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
698 | if (err < 0) |
699 | goto error; | |
f7278fd0 | 700 | |
f7278fd0 | 701 | return 0; |
fcbdcc1a TI |
702 | |
703 | error: | |
704 | via_free(codec); | |
705 | return err; | |
f7278fd0 JC |
706 | } |
707 | ||
d949cac1 | 708 | /* Patch for VT1708S */ |
096a8854 | 709 | static const struct hda_verb vt1708S_init_verbs[] = { |
d7426329 HW |
710 | /* Enable Mic Boost Volume backdoor */ |
711 | {0x1, 0xf98, 0x1}, | |
bc7e7e5c LW |
712 | /* don't bybass mixer */ |
713 | {0x1, 0xf88, 0xc0}, | |
d949cac1 HW |
714 | { } |
715 | }; | |
716 | ||
6369bcfc LW |
717 | static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, |
718 | int offset, int num_steps, int step_size) | |
719 | { | |
d045c5dc TI |
720 | snd_hda_override_wcaps(codec, pin, |
721 | get_wcaps(codec, pin) | AC_WCAP_IN_AMP); | |
6369bcfc LW |
722 | snd_hda_override_amp_caps(codec, pin, HDA_INPUT, |
723 | (offset << AC_AMPCAP_OFFSET_SHIFT) | | |
724 | (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | | |
725 | (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | | |
726 | (0 << AC_AMPCAP_MUTE_SHIFT)); | |
727 | } | |
728 | ||
d949cac1 HW |
729 | static int patch_vt1708S(struct hda_codec *codec) |
730 | { | |
731 | struct via_spec *spec; | |
732 | int err; | |
733 | ||
734 | /* create a codec specific record */ | |
5b0cb1d8 | 735 | spec = via_new_spec(codec); |
d949cac1 HW |
736 | if (spec == NULL) |
737 | return -ENOMEM; | |
738 | ||
b3f6008f | 739 | spec->gen.mixer_nid = 0x16; |
d7a99cce TI |
740 | override_mic_boost(codec, 0x1a, 0, 3, 40); |
741 | override_mic_boost(codec, 0x1e, 0, 3, 40); | |
620e2b28 | 742 | |
518bf3ba | 743 | /* correct names for VT1708BCE */ |
ded255be TI |
744 | if (get_codec_type(codec) == VT1708BCE) |
745 | snd_hda_codec_set_name(codec, "VT1708BCE"); | |
bc92df7f | 746 | /* correct names for VT1705 */ |
ded255be TI |
747 | if (codec->core.vendor_id == 0x11064397) |
748 | snd_hda_codec_set_name(codec, "VT1705"); | |
b3f6008f | 749 | |
f8bfc628 TI |
750 | err = snd_hda_add_verbs(codec, vt1708S_init_verbs); |
751 | if (err < 0) | |
752 | goto error; | |
753 | ||
b3f6008f TI |
754 | /* automatic parse from the BIOS config */ |
755 | err = via_parse_auto_config(codec); | |
fcbdcc1a TI |
756 | if (err < 0) |
757 | goto error; | |
b3f6008f | 758 | |
d949cac1 | 759 | return 0; |
fcbdcc1a TI |
760 | |
761 | error: | |
762 | via_free(codec); | |
763 | return err; | |
d949cac1 HW |
764 | } |
765 | ||
766 | /* Patch for VT1702 */ | |
767 | ||
096a8854 | 768 | static const struct hda_verb vt1702_init_verbs[] = { |
bc7e7e5c LW |
769 | /* mixer enable */ |
770 | {0x1, 0xF88, 0x3}, | |
771 | /* GPIO 0~2 */ | |
772 | {0x1, 0xF82, 0x3F}, | |
d949cac1 HW |
773 | { } |
774 | }; | |
775 | ||
d949cac1 HW |
776 | static int patch_vt1702(struct hda_codec *codec) |
777 | { | |
778 | struct via_spec *spec; | |
779 | int err; | |
d949cac1 HW |
780 | |
781 | /* create a codec specific record */ | |
5b0cb1d8 | 782 | spec = via_new_spec(codec); |
d949cac1 HW |
783 | if (spec == NULL) |
784 | return -ENOMEM; | |
785 | ||
b3f6008f | 786 | spec->gen.mixer_nid = 0x1a; |
620e2b28 | 787 | |
12daef65 TI |
788 | /* limit AA path volume to 0 dB */ |
789 | snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, | |
790 | (0x17 << AC_AMPCAP_OFFSET_SHIFT) | | |
791 | (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | | |
792 | (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | | |
793 | (1 << AC_AMPCAP_MUTE_SHIFT)); | |
794 | ||
f8bfc628 TI |
795 | err = snd_hda_add_verbs(codec, vt1702_init_verbs); |
796 | if (err < 0) | |
797 | goto error; | |
798 | ||
d949cac1 | 799 | /* automatic parse from the BIOS config */ |
12daef65 | 800 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
801 | if (err < 0) |
802 | goto error; | |
d949cac1 | 803 | |
d949cac1 | 804 | return 0; |
fcbdcc1a TI |
805 | |
806 | error: | |
807 | via_free(codec); | |
808 | return err; | |
d949cac1 HW |
809 | } |
810 | ||
eb7188ca LW |
811 | /* Patch for VT1718S */ |
812 | ||
096a8854 | 813 | static const struct hda_verb vt1718S_init_verbs[] = { |
4ab2d53a LW |
814 | /* Enable MW0 adjust Gain 5 */ |
815 | {0x1, 0xfb2, 0x10}, | |
eb7188ca LW |
816 | /* Enable Boost Volume backdoor */ |
817 | {0x1, 0xf88, 0x8}, | |
5d41762a | 818 | |
eb7188ca LW |
819 | { } |
820 | }; | |
821 | ||
30b45033 TI |
822 | /* Add a connection to the primary DAC from AA-mixer for some codecs |
823 | * This isn't listed from the raw info, but the chip has a secret connection. | |
824 | */ | |
825 | static int add_secret_dac_path(struct hda_codec *codec) | |
826 | { | |
827 | struct via_spec *spec = codec->spec; | |
828 | int i, nums; | |
829 | hda_nid_t conn[8]; | |
830 | hda_nid_t nid; | |
831 | ||
b3f6008f | 832 | if (!spec->gen.mixer_nid) |
30b45033 | 833 | return 0; |
b3f6008f | 834 | nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, |
30b45033 TI |
835 | ARRAY_SIZE(conn) - 1); |
836 | for (i = 0; i < nums; i++) { | |
837 | if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) | |
838 | return 0; | |
839 | } | |
840 | ||
841 | /* find the primary DAC and add to the connection list */ | |
7639a06c | 842 | for_each_hda_codec_node(nid, codec) { |
30b45033 TI |
843 | unsigned int caps = get_wcaps(codec, nid); |
844 | if (get_wcaps_type(caps) == AC_WID_AUD_OUT && | |
845 | !(caps & AC_WCAP_DIGITAL)) { | |
846 | conn[nums++] = nid; | |
847 | return snd_hda_override_conn_list(codec, | |
b3f6008f | 848 | spec->gen.mixer_nid, |
30b45033 TI |
849 | nums, conn); |
850 | } | |
851 | } | |
852 | return 0; | |
853 | } | |
854 | ||
855 | ||
eb7188ca LW |
856 | static int patch_vt1718S(struct hda_codec *codec) |
857 | { | |
858 | struct via_spec *spec; | |
859 | int err; | |
860 | ||
861 | /* create a codec specific record */ | |
5b0cb1d8 | 862 | spec = via_new_spec(codec); |
eb7188ca LW |
863 | if (spec == NULL) |
864 | return -ENOMEM; | |
865 | ||
b3f6008f | 866 | spec->gen.mixer_nid = 0x21; |
d7a99cce TI |
867 | override_mic_boost(codec, 0x2b, 0, 3, 40); |
868 | override_mic_boost(codec, 0x29, 0, 3, 40); | |
30b45033 | 869 | add_secret_dac_path(codec); |
620e2b28 | 870 | |
f8bfc628 TI |
871 | err = snd_hda_add_verbs(codec, vt1718S_init_verbs); |
872 | if (err < 0) | |
873 | goto error; | |
874 | ||
eb7188ca | 875 | /* automatic parse from the BIOS config */ |
12daef65 | 876 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
877 | if (err < 0) |
878 | goto error; | |
eb7188ca | 879 | |
eb7188ca | 880 | return 0; |
fcbdcc1a TI |
881 | |
882 | error: | |
883 | via_free(codec); | |
884 | return err; | |
eb7188ca | 885 | } |
f3db423d LW |
886 | |
887 | /* Patch for VT1716S */ | |
888 | ||
889 | static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, | |
890 | struct snd_ctl_elem_info *uinfo) | |
891 | { | |
892 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
893 | uinfo->count = 1; | |
894 | uinfo->value.integer.min = 0; | |
895 | uinfo->value.integer.max = 1; | |
896 | return 0; | |
897 | } | |
898 | ||
899 | static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, | |
900 | struct snd_ctl_elem_value *ucontrol) | |
901 | { | |
902 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
903 | int index = 0; | |
904 | ||
905 | index = snd_hda_codec_read(codec, 0x26, 0, | |
906 | AC_VERB_GET_CONNECT_SEL, 0); | |
907 | if (index != -1) | |
908 | *ucontrol->value.integer.value = index; | |
909 | ||
910 | return 0; | |
911 | } | |
912 | ||
913 | static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, | |
914 | struct snd_ctl_elem_value *ucontrol) | |
915 | { | |
916 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
917 | struct via_spec *spec = codec->spec; | |
918 | int index = *ucontrol->value.integer.value; | |
919 | ||
920 | snd_hda_codec_write(codec, 0x26, 0, | |
921 | AC_VERB_SET_CONNECT_SEL, index); | |
922 | spec->dmic_enabled = index; | |
f3db423d LW |
923 | return 1; |
924 | } | |
925 | ||
0e8f9862 TI |
926 | static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol = |
927 | HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT); | |
928 | static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = { | |
f3db423d LW |
929 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
930 | .name = "Digital Mic Capture Switch", | |
5b0cb1d8 | 931 | .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, |
f3db423d LW |
932 | .count = 1, |
933 | .info = vt1716s_dmic_info, | |
934 | .get = vt1716s_dmic_get, | |
935 | .put = vt1716s_dmic_put, | |
f3db423d LW |
936 | }; |
937 | ||
938 | ||
939 | /* mono-out mixer elements */ | |
0e8f9862 TI |
940 | static const struct snd_kcontrol_new vt1716S_mono_out_mixer = |
941 | HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT); | |
f3db423d | 942 | |
096a8854 | 943 | static const struct hda_verb vt1716S_init_verbs[] = { |
f3db423d LW |
944 | /* Enable Boost Volume backdoor */ |
945 | {0x1, 0xf8a, 0x80}, | |
946 | /* don't bybass mixer */ | |
947 | {0x1, 0xf88, 0xc0}, | |
948 | /* Enable mono output */ | |
949 | {0x1, 0xf90, 0x08}, | |
950 | { } | |
951 | }; | |
952 | ||
f3db423d LW |
953 | static int patch_vt1716S(struct hda_codec *codec) |
954 | { | |
955 | struct via_spec *spec; | |
956 | int err; | |
957 | ||
958 | /* create a codec specific record */ | |
5b0cb1d8 | 959 | spec = via_new_spec(codec); |
f3db423d LW |
960 | if (spec == NULL) |
961 | return -ENOMEM; | |
962 | ||
b3f6008f | 963 | spec->gen.mixer_nid = 0x16; |
d7a99cce TI |
964 | override_mic_boost(codec, 0x1a, 0, 3, 40); |
965 | override_mic_boost(codec, 0x1e, 0, 3, 40); | |
620e2b28 | 966 | |
f8bfc628 TI |
967 | err = snd_hda_add_verbs(codec, vt1716S_init_verbs); |
968 | if (err < 0) | |
969 | goto error; | |
970 | ||
f3db423d | 971 | /* automatic parse from the BIOS config */ |
12daef65 | 972 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
973 | if (err < 0) |
974 | goto error; | |
f3db423d | 975 | |
0e8f9862 TI |
976 | if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || |
977 | !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || | |
978 | !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { | |
979 | err = -ENOMEM; | |
980 | goto error; | |
981 | } | |
f3db423d | 982 | |
f3db423d | 983 | return 0; |
fcbdcc1a TI |
984 | |
985 | error: | |
986 | via_free(codec); | |
987 | return err; | |
f3db423d | 988 | } |
25eaba2f LW |
989 | |
990 | /* for vt2002P */ | |
991 | ||
096a8854 | 992 | static const struct hda_verb vt2002P_init_verbs[] = { |
eadb9a80 LW |
993 | /* Class-D speaker related verbs */ |
994 | {0x1, 0xfe0, 0x4}, | |
995 | {0x1, 0xfe9, 0x80}, | |
996 | {0x1, 0xfe2, 0x22}, | |
25eaba2f LW |
997 | /* Enable Boost Volume backdoor */ |
998 | {0x1, 0xfb9, 0x24}, | |
25eaba2f LW |
999 | /* Enable AOW0 to MW9 */ |
1000 | {0x1, 0xfb8, 0x88}, | |
1001 | { } | |
1002 | }; | |
4a918ffe | 1003 | |
096a8854 | 1004 | static const struct hda_verb vt1802_init_verbs[] = { |
11890956 LW |
1005 | /* Enable Boost Volume backdoor */ |
1006 | {0x1, 0xfb9, 0x24}, | |
11890956 LW |
1007 | /* Enable AOW0 to MW9 */ |
1008 | {0x1, 0xfb8, 0x88}, | |
1009 | { } | |
1010 | }; | |
25eaba2f | 1011 | |
4b527b65 DH |
1012 | /* |
1013 | * pin fix-up | |
1014 | */ | |
1015 | enum { | |
1016 | VIA_FIXUP_INTMIC_BOOST, | |
d5266125 | 1017 | VIA_FIXUP_ASUS_G75, |
4b527b65 DH |
1018 | }; |
1019 | ||
1020 | static void via_fixup_intmic_boost(struct hda_codec *codec, | |
1021 | const struct hda_fixup *fix, int action) | |
1022 | { | |
1023 | if (action == HDA_FIXUP_ACT_PRE_PROBE) | |
1024 | override_mic_boost(codec, 0x30, 0, 2, 40); | |
1025 | } | |
1026 | ||
1027 | static const struct hda_fixup via_fixups[] = { | |
1028 | [VIA_FIXUP_INTMIC_BOOST] = { | |
1029 | .type = HDA_FIXUP_FUNC, | |
1030 | .v.func = via_fixup_intmic_boost, | |
1031 | }, | |
d5266125 TI |
1032 | [VIA_FIXUP_ASUS_G75] = { |
1033 | .type = HDA_FIXUP_PINS, | |
1034 | .v.pins = (const struct hda_pintbl[]) { | |
1035 | /* set 0x24 and 0x33 as speakers */ | |
1036 | { 0x24, 0x991301f0 }, | |
1037 | { 0x33, 0x991301f1 }, /* subwoofer */ | |
1038 | { } | |
1039 | } | |
1040 | }, | |
4b527b65 DH |
1041 | }; |
1042 | ||
1043 | static const struct snd_pci_quirk vt2002p_fixups[] = { | |
d5266125 | 1044 | SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), |
4b527b65 DH |
1045 | SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), |
1046 | {} | |
1047 | }; | |
1048 | ||
ef4da458 TI |
1049 | /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e |
1050 | * Replace this with mixer NID 0x1c | |
1051 | */ | |
1052 | static void fix_vt1802_connections(struct hda_codec *codec) | |
1053 | { | |
1054 | static hda_nid_t conn_24[] = { 0x14, 0x1c }; | |
1055 | static hda_nid_t conn_33[] = { 0x1c }; | |
1056 | ||
1057 | snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); | |
1058 | snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); | |
1059 | } | |
1060 | ||
25eaba2f LW |
1061 | /* patch for vt2002P */ |
1062 | static int patch_vt2002P(struct hda_codec *codec) | |
1063 | { | |
1064 | struct via_spec *spec; | |
1065 | int err; | |
1066 | ||
1067 | /* create a codec specific record */ | |
5b0cb1d8 | 1068 | spec = via_new_spec(codec); |
25eaba2f LW |
1069 | if (spec == NULL) |
1070 | return -ENOMEM; | |
1071 | ||
b3f6008f | 1072 | spec->gen.mixer_nid = 0x21; |
d7a99cce TI |
1073 | override_mic_boost(codec, 0x2b, 0, 3, 40); |
1074 | override_mic_boost(codec, 0x29, 0, 3, 40); | |
ef4da458 TI |
1075 | if (spec->codec_type == VT1802) |
1076 | fix_vt1802_connections(codec); | |
30b45033 | 1077 | add_secret_dac_path(codec); |
620e2b28 | 1078 | |
4b527b65 DH |
1079 | snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); |
1080 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | |
1081 | ||
f8bfc628 TI |
1082 | if (spec->codec_type == VT1802) |
1083 | err = snd_hda_add_verbs(codec, vt1802_init_verbs); | |
1084 | else | |
1085 | err = snd_hda_add_verbs(codec, vt2002P_init_verbs); | |
1086 | if (err < 0) | |
1087 | goto error; | |
1088 | ||
25eaba2f | 1089 | /* automatic parse from the BIOS config */ |
12daef65 | 1090 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
1091 | if (err < 0) |
1092 | goto error; | |
25eaba2f | 1093 | |
25eaba2f | 1094 | return 0; |
fcbdcc1a TI |
1095 | |
1096 | error: | |
1097 | via_free(codec); | |
1098 | return err; | |
25eaba2f | 1099 | } |
ab6734e7 LW |
1100 | |
1101 | /* for vt1812 */ | |
1102 | ||
096a8854 | 1103 | static const struct hda_verb vt1812_init_verbs[] = { |
ab6734e7 LW |
1104 | /* Enable Boost Volume backdoor */ |
1105 | {0x1, 0xfb9, 0x24}, | |
ab6734e7 LW |
1106 | /* Enable AOW0 to MW9 */ |
1107 | {0x1, 0xfb8, 0xa8}, | |
1108 | { } | |
1109 | }; | |
1110 | ||
ab6734e7 LW |
1111 | /* patch for vt1812 */ |
1112 | static int patch_vt1812(struct hda_codec *codec) | |
1113 | { | |
1114 | struct via_spec *spec; | |
1115 | int err; | |
1116 | ||
1117 | /* create a codec specific record */ | |
5b0cb1d8 | 1118 | spec = via_new_spec(codec); |
ab6734e7 LW |
1119 | if (spec == NULL) |
1120 | return -ENOMEM; | |
1121 | ||
b3f6008f | 1122 | spec->gen.mixer_nid = 0x21; |
d7a99cce TI |
1123 | override_mic_boost(codec, 0x2b, 0, 3, 40); |
1124 | override_mic_boost(codec, 0x29, 0, 3, 40); | |
30b45033 | 1125 | add_secret_dac_path(codec); |
620e2b28 | 1126 | |
f8bfc628 TI |
1127 | err = snd_hda_add_verbs(codec, vt1812_init_verbs); |
1128 | if (err < 0) | |
1129 | goto error; | |
1130 | ||
ab6734e7 | 1131 | /* automatic parse from the BIOS config */ |
12daef65 | 1132 | err = via_parse_auto_config(codec); |
fcbdcc1a TI |
1133 | if (err < 0) |
1134 | goto error; | |
ab6734e7 | 1135 | |
ab6734e7 | 1136 | return 0; |
fcbdcc1a TI |
1137 | |
1138 | error: | |
1139 | via_free(codec); | |
1140 | return err; | |
ab6734e7 LW |
1141 | } |
1142 | ||
43737e0a LW |
1143 | /* patch for vt3476 */ |
1144 | ||
1145 | static const struct hda_verb vt3476_init_verbs[] = { | |
1146 | /* Enable DMic 8/16/32K */ | |
1147 | {0x1, 0xF7B, 0x30}, | |
1148 | /* Enable Boost Volume backdoor */ | |
1149 | {0x1, 0xFB9, 0x20}, | |
1150 | /* Enable AOW-MW9 path */ | |
1151 | {0x1, 0xFB8, 0x10}, | |
1152 | { } | |
1153 | }; | |
1154 | ||
43737e0a LW |
1155 | static int patch_vt3476(struct hda_codec *codec) |
1156 | { | |
1157 | struct via_spec *spec; | |
1158 | int err; | |
1159 | ||
1160 | /* create a codec specific record */ | |
1161 | spec = via_new_spec(codec); | |
1162 | if (spec == NULL) | |
1163 | return -ENOMEM; | |
1164 | ||
b3f6008f | 1165 | spec->gen.mixer_nid = 0x3f; |
43737e0a LW |
1166 | add_secret_dac_path(codec); |
1167 | ||
f8bfc628 TI |
1168 | err = snd_hda_add_verbs(codec, vt3476_init_verbs); |
1169 | if (err < 0) | |
1170 | goto error; | |
1171 | ||
43737e0a LW |
1172 | /* automatic parse from the BIOS config */ |
1173 | err = via_parse_auto_config(codec); | |
fcbdcc1a TI |
1174 | if (err < 0) |
1175 | goto error; | |
43737e0a | 1176 | |
43737e0a | 1177 | return 0; |
fcbdcc1a TI |
1178 | |
1179 | error: | |
1180 | via_free(codec); | |
1181 | return err; | |
43737e0a LW |
1182 | } |
1183 | ||
c577b8a1 JC |
1184 | /* |
1185 | * patch entries | |
1186 | */ | |
b9a94a9c TI |
1187 | static const struct hda_device_id snd_hda_id_via[] = { |
1188 | HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), | |
1189 | HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), | |
1190 | HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), | |
1191 | HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), | |
1192 | HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), | |
1193 | HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), | |
1194 | HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), | |
1195 | HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), | |
1196 | HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), | |
1197 | HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), | |
1198 | HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), | |
1199 | HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), | |
1200 | HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), | |
1201 | HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), | |
1202 | HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), | |
1203 | HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), | |
1204 | HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), | |
1205 | HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), | |
1206 | HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), | |
1207 | HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), | |
1208 | HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), | |
1209 | HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), | |
1210 | HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), | |
1211 | HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), | |
1212 | HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), | |
1213 | HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), | |
1214 | HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), | |
1215 | HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), | |
1216 | HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), | |
1217 | HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), | |
1218 | HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), | |
1219 | HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), | |
1220 | HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), | |
1221 | HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), | |
1222 | HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), | |
1223 | HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), | |
1224 | HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), | |
1225 | HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), | |
1226 | HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), | |
1227 | HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), | |
1228 | HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), | |
1229 | HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), | |
1230 | HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), | |
1231 | HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), | |
1232 | HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), | |
1233 | HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), | |
1234 | HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), | |
1235 | HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), | |
1236 | HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), | |
1237 | HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), | |
1238 | HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), | |
c577b8a1 JC |
1239 | {} /* terminator */ |
1240 | }; | |
b9a94a9c | 1241 | MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); |
1289e9e8 | 1242 | |
d8a766a1 | 1243 | static struct hda_codec_driver via_driver = { |
b9a94a9c | 1244 | .id = snd_hda_id_via, |
1289e9e8 TI |
1245 | }; |
1246 | ||
1247 | MODULE_LICENSE("GPL"); | |
1248 | MODULE_DESCRIPTION("VIA HD-audio codec"); | |
1249 | ||
d8a766a1 | 1250 | module_hda_codec_driver(via_driver); |