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