1d0fa1179SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c577b8a1SJoseph Chan /*
3c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec
4c577b8a1SJoseph Chan *
58e86597fSLydia Wang * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
6c577b8a1SJoseph Chan *
78e86597fSLydia Wang * (C) 2006-2009 VIA Technology, Inc.
88e86597fSLydia Wang * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
9c577b8a1SJoseph Chan */
10c577b8a1SJoseph Chan
11c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
12c577b8a1SJoseph Chan /* */
13c577b8a1SJoseph Chan /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
14c577b8a1SJoseph Chan /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
15c577b8a1SJoseph Chan /* 2006-08-02 Lydia Wang Add support to VT1709 codec */
16c577b8a1SJoseph Chan /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
17f7278fd0SJosepch Chan /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
18f7278fd0SJosepch Chan /* 2007-09-17 Lydia Wang Add VT1708B codec support */
1976d9b0ddSHarald Welte /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
20fb4cb772SHarald Welte /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
21d949cac1SHarald Welte /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
2269e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
230aa62aefSHarald Welte /* 2008-04-09 Lydia Wang Add Independent HP feature */
2498aa34c0SHarald Welte /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
25d7426329SHarald Welte /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
268e86597fSLydia Wang /* 2009-02-16 Logan Li Add support for VT1718S */
278e86597fSLydia Wang /* 2009-03-13 Logan Li Add support for VT1716S */
288e86597fSLydia Wang /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
298e86597fSLydia Wang /* 2009-07-08 Lydia Wang Add support for VT2002P */
308e86597fSLydia Wang /* 2009-07-21 Lydia Wang Add support for VT1812 */
3136dd5c4aSLydia Wang /* 2009-09-19 Lydia Wang Add support for VT1818S */
32c577b8a1SJoseph Chan /* */
33c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
34c577b8a1SJoseph Chan
35c577b8a1SJoseph Chan
36c577b8a1SJoseph Chan #include <linux/init.h>
37c577b8a1SJoseph Chan #include <linux/delay.h>
38c577b8a1SJoseph Chan #include <linux/slab.h>
39da155d5bSPaul Gortmaker #include <linux/module.h>
40c577b8a1SJoseph Chan #include <sound/core.h>
410aa62aefSHarald Welte #include <sound/asoundef.h>
42be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
43c577b8a1SJoseph Chan #include "hda_local.h"
44128bc4baSTakashi Iwai #include "hda_auto_parser.h"
451835a0f9STakashi Iwai #include "hda_jack.h"
46b3f6008fSTakashi Iwai #include "hda_generic.h"
47c577b8a1SJoseph Chan
48c577b8a1SJoseph Chan /* Pin Widget NID */
4976d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20
5076d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24
51c577b8a1SJoseph Chan
52d7426329SHarald Welte enum VIA_HDA_CODEC {
53d7426329SHarald Welte UNKNOWN = -1,
54d7426329SHarald Welte VT1708,
55d7426329SHarald Welte VT1709_10CH,
56d7426329SHarald Welte VT1709_6CH,
57d7426329SHarald Welte VT1708B_8CH,
58d7426329SHarald Welte VT1708B_4CH,
59d7426329SHarald Welte VT1708S,
60518bf3baSLydia Wang VT1708BCE,
61d7426329SHarald Welte VT1702,
62eb7188caSLydia Wang VT1718S,
63f3db423dSLydia Wang VT1716S,
6425eaba2fSLydia Wang VT2002P,
65ab6734e7SLydia Wang VT1812,
6611890956SLydia Wang VT1802,
6743737e0aSLydia Wang VT1705CF,
686121b84aSLydia Wang VT1808,
69d7426329SHarald Welte CODEC_TYPES,
70d7426329SHarald Welte };
71d7426329SHarald Welte
7211890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
7311890956SLydia Wang ((spec)->codec_type == VT2002P ||\
7411890956SLydia Wang (spec)->codec_type == VT1812 ||\
7511890956SLydia Wang (spec)->codec_type == VT1802)
7611890956SLydia Wang
771f2e99feSLydia Wang struct via_spec {
78b3f6008fSTakashi Iwai struct hda_gen_spec gen;
79b3f6008fSTakashi Iwai
801f2e99feSLydia Wang /* HP mode source */
81f3db423dSLydia Wang unsigned int dmic_enabled;
821f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type;
831f2e99feSLydia Wang
84e9d010c2STakashi Iwai /* analog low-power control */
85e9d010c2STakashi Iwai bool alc_mode;
86e9d010c2STakashi Iwai
871f2e99feSLydia Wang /* work to check hp jack state */
88187d333eSTakashi Iwai int hp_work_active;
89e06e5a29STakashi Iwai int vt1708_jack_detect;
901f2e99feSLydia Wang };
911f2e99feSLydia Wang
920341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
93b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
94b3f6008fSTakashi Iwai struct hda_codec *codec,
95b3f6008fSTakashi Iwai struct snd_pcm_substream *substream,
96b3f6008fSTakashi Iwai int action);
97b3f6008fSTakashi Iwai
98225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */
99225068abSTakashi Iwai
via_new_spec(struct hda_codec * codec)1005b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1015b0cb1d8SJaroslav Kysela {
1025b0cb1d8SJaroslav Kysela struct via_spec *spec;
1035b0cb1d8SJaroslav Kysela
1045b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1055b0cb1d8SJaroslav Kysela if (spec == NULL)
1065b0cb1d8SJaroslav Kysela return NULL;
1075b0cb1d8SJaroslav Kysela
1085b0cb1d8SJaroslav Kysela codec->spec = spec;
109b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen);
1100341ccd7SLydia Wang spec->codec_type = get_codec_type(codec);
1110341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */
1120341ccd7SLydia Wang if (spec->codec_type == VT1708BCE)
1130341ccd7SLydia Wang spec->codec_type = VT1708S;
11413961170STakashi Iwai spec->gen.indep_hp = 1;
11505909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1;
11667ea698cSTakashi Iwai spec->gen.dac_min_mute = 1;
117b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook;
11874f14b36STakashi Iwai spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
119967b1307STakashi Iwai codec->power_save_node = 1;
120688b12ccSTakashi Iwai spec->gen.power_down_unused = 1;
121225068abSTakashi Iwai codec->patch_ops = via_patch_ops;
1225b0cb1d8SJaroslav Kysela return spec;
1235b0cb1d8SJaroslav Kysela }
1245b0cb1d8SJaroslav Kysela
get_codec_type(struct hda_codec * codec)125744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
126d7426329SHarald Welte {
1277639a06cSTakashi Iwai u32 vendor_id = codec->core.vendor_id;
128d7426329SHarald Welte u16 ven_id = vendor_id >> 16;
129d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff;
130d7426329SHarald Welte enum VIA_HDA_CODEC codec_type;
131d7426329SHarald Welte
132d7426329SHarald Welte /* get codec type */
133d7426329SHarald Welte if (ven_id != 0x1106)
134d7426329SHarald Welte codec_type = UNKNOWN;
135d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b)
136d7426329SHarald Welte codec_type = VT1708;
137d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713)
138d7426329SHarald Welte codec_type = VT1709_10CH;
139d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717)
140d7426329SHarald Welte codec_type = VT1709_6CH;
141518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
142d7426329SHarald Welte codec_type = VT1708B_8CH;
143518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
144518bf3baSLydia Wang codec_type = VT1708BCE;
145518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
146d7426329SHarald Welte codec_type = VT1708B_4CH;
147d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397
148d7426329SHarald Welte && (dev_id >> 12) < 8)
149d7426329SHarald Welte codec_type = VT1708S;
150d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398
151d7426329SHarald Welte && (dev_id >> 12) < 8)
152d7426329SHarald Welte codec_type = VT1702;
153eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428
154eb7188caSLydia Wang && (dev_id >> 12) < 8)
155eb7188caSLydia Wang codec_type = VT1718S;
156f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721)
157f3db423dSLydia Wang codec_type = VT1716S;
158bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441)
159bb3c6bfcSLydia Wang codec_type = VT1718S;
16025eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438)
16125eaba2fSLydia Wang codec_type = VT2002P;
162ab6734e7SLydia Wang else if (dev_id == 0x0448)
163ab6734e7SLydia Wang codec_type = VT1812;
16436dd5c4aSLydia Wang else if (dev_id == 0x0440)
16536dd5c4aSLydia Wang codec_type = VT1708S;
16611890956SLydia Wang else if ((dev_id & 0xfff) == 0x446)
16711890956SLydia Wang codec_type = VT1802;
16843737e0aSLydia Wang else if (dev_id == 0x4760)
16943737e0aSLydia Wang codec_type = VT1705CF;
1706121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762)
1716121b84aSLydia Wang codec_type = VT1808;
172d7426329SHarald Welte else
173d7426329SHarald Welte codec_type = UNKNOWN;
174d7426329SHarald Welte return codec_type;
175d7426329SHarald Welte };
176d7426329SHarald Welte
177ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
178ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1791f2e99feSLydia Wang
180187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
181187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
182187d333eSTakashi Iwai !is_aa_path_mute(codec))
1831f2e99feSLydia Wang
vt1708_stop_hp_work(struct hda_codec * codec)184b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
1851f2e99feSLydia Wang {
186b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec;
187b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
1881f2e99feSLydia Wang return;
189187d333eSTakashi Iwai if (spec->hp_work_active) {
190b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
1917eaa9161SWang Xingchao codec->jackpoll_interval = 0;
192b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work);
193b3f6008fSTakashi Iwai spec->hp_work_active = false;
194187d333eSTakashi Iwai }
195187d333eSTakashi Iwai }
196187d333eSTakashi Iwai
vt1708_update_hp_work(struct hda_codec * codec)197b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
198187d333eSTakashi Iwai {
199b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec;
200b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
201187d333eSTakashi Iwai return;
20205dc0fc9SDavid Henningsson if (spec->vt1708_jack_detect) {
203187d333eSTakashi Iwai if (!spec->hp_work_active) {
204b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100);
205b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2062f35c630STakashi Iwai schedule_delayed_work(&codec->jackpoll_work, 0);
207b3f6008fSTakashi Iwai spec->hp_work_active = true;
208187d333eSTakashi Iwai }
209b3f6008fSTakashi Iwai } else if (!hp_detect_with_aa(codec))
210b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec);
2111f2e99feSLydia Wang }
212f5271101SLydia Wang
via_pin_power_ctl_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)21324088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
21424088a58STakashi Iwai struct snd_ctl_elem_info *uinfo)
21524088a58STakashi Iwai {
216dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
21724088a58STakashi Iwai }
21824088a58STakashi Iwai
via_pin_power_ctl_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)21924088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
22024088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol)
22124088a58STakashi Iwai {
22224088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
223735c75cfSTakashi Iwai struct via_spec *spec = codec->spec;
224735c75cfSTakashi Iwai
225735c75cfSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
22624088a58STakashi Iwai return 0;
22724088a58STakashi Iwai }
22824088a58STakashi Iwai
via_pin_power_ctl_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)22924088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
23024088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol)
23124088a58STakashi Iwai {
23224088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
23324088a58STakashi Iwai struct via_spec *spec = codec->spec;
234688b12ccSTakashi Iwai bool val = !!ucontrol->value.enumerated.item[0];
23524088a58STakashi Iwai
236735c75cfSTakashi Iwai if (val == spec->gen.power_down_unused)
23724088a58STakashi Iwai return 0;
238735c75cfSTakashi Iwai /* codec->power_save_node = val; */ /* widget PM seems yet broken */
239688b12ccSTakashi Iwai spec->gen.power_down_unused = val;
240e9d010c2STakashi Iwai analog_low_current_mode(codec);
24124088a58STakashi Iwai return 1;
24224088a58STakashi Iwai }
24324088a58STakashi Iwai
2440e8f9862STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
24524088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24624088a58STakashi Iwai .name = "Dynamic Power-Control",
24724088a58STakashi Iwai .info = via_pin_power_ctl_info,
24824088a58STakashi Iwai .get = via_pin_power_ctl_get,
24924088a58STakashi Iwai .put = via_pin_power_ctl_put,
25024088a58STakashi Iwai };
25124088a58STakashi Iwai
2524738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP
2534738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */
2540e8f9862STakashi Iwai static const struct snd_kcontrol_new via_beep_mixer[] = {
2554738465cSW. Trevor King HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
2564738465cSW. Trevor King HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
2574738465cSW. Trevor King };
2584738465cSW. Trevor King
set_beep_amp(struct via_spec * spec,hda_nid_t nid,int idx,int dir)2590e8f9862STakashi Iwai static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
2600e8f9862STakashi Iwai int idx, int dir)
2614738465cSW. Trevor King {
2620e8f9862STakashi Iwai struct snd_kcontrol_new *knew;
2630e8f9862STakashi Iwai unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
2640e8f9862STakashi Iwai int i;
2654738465cSW. Trevor King
2660e8f9862STakashi Iwai spec->gen.beep_nid = nid;
2670e8f9862STakashi Iwai for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
2680e8f9862STakashi Iwai knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
2690e8f9862STakashi Iwai &via_beep_mixer[i]);
2700e8f9862STakashi Iwai if (!knew)
2714738465cSW. Trevor King return -ENOMEM;
2720e8f9862STakashi Iwai knew->private_value = beep_amp;
2734738465cSW. Trevor King }
2744738465cSW. Trevor King return 0;
2754738465cSW. Trevor King }
2764738465cSW. Trevor King
auto_parse_beep(struct hda_codec * codec)2770e8f9862STakashi Iwai static int auto_parse_beep(struct hda_codec *codec)
2784738465cSW. Trevor King {
2794738465cSW. Trevor King struct via_spec *spec = codec->spec;
2804738465cSW. Trevor King hda_nid_t nid;
2814738465cSW. Trevor King
2824738465cSW. Trevor King for_each_hda_codec_node(nid, codec)
2830e8f9862STakashi Iwai if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
2840e8f9862STakashi Iwai return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
2850e8f9862STakashi Iwai return 0;
2864738465cSW. Trevor King }
2874738465cSW. Trevor King #else
2880e8f9862STakashi Iwai #define auto_parse_beep(codec) 0
2894738465cSW. Trevor King #endif
29024088a58STakashi Iwai
291f5271101SLydia Wang /* check AA path's mute status */
is_aa_path_mute(struct hda_codec * codec)292ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
293ada509ecSTakashi Iwai {
294ada509ecSTakashi Iwai struct via_spec *spec = codec->spec;
295ada509ecSTakashi Iwai const struct hda_amp_list *p;
2960186f4f4STakashi Iwai int ch, v;
297ada509ecSTakashi Iwai
2980186f4f4STakashi Iwai p = spec->gen.loopback.amplist;
2990186f4f4STakashi Iwai if (!p)
3000186f4f4STakashi Iwai return true;
3010186f4f4STakashi Iwai for (; p->nid; p++) {
302ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) {
303ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
304ada509ecSTakashi Iwai p->idx);
305ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0)
306ada509ecSTakashi Iwai return false;
307f5271101SLydia Wang }
308f5271101SLydia Wang }
309ada509ecSTakashi Iwai return true;
310f5271101SLydia Wang }
311f5271101SLydia Wang
312f5271101SLydia Wang /* enter/exit analog low-current mode */
__analog_low_current_mode(struct hda_codec * codec,bool force)313e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
314f5271101SLydia Wang {
315f5271101SLydia Wang struct via_spec *spec = codec->spec;
316ada509ecSTakashi Iwai bool enable;
317ada509ecSTakashi Iwai unsigned int verb, parm;
318f5271101SLydia Wang
319967b1307STakashi Iwai if (!codec->power_save_node)
320e9d010c2STakashi Iwai enable = false;
321e9d010c2STakashi Iwai else
322b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
323e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force)
324e9d010c2STakashi Iwai return;
325e9d010c2STakashi Iwai spec->alc_mode = enable;
326f5271101SLydia Wang
327f5271101SLydia Wang /* decide low current mode's verb & parameter */
328f5271101SLydia Wang switch (spec->codec_type) {
329f5271101SLydia Wang case VT1708B_8CH:
330f5271101SLydia Wang case VT1708B_4CH:
331f5271101SLydia Wang verb = 0xf70;
332f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
333f5271101SLydia Wang break;
334f5271101SLydia Wang case VT1708S:
335eb7188caSLydia Wang case VT1718S:
336f3db423dSLydia Wang case VT1716S:
337f5271101SLydia Wang verb = 0xf73;
338f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
339f5271101SLydia Wang break;
340f5271101SLydia Wang case VT1702:
341f5271101SLydia Wang verb = 0xf73;
342f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
343f5271101SLydia Wang break;
34425eaba2fSLydia Wang case VT2002P:
345ab6734e7SLydia Wang case VT1812:
34611890956SLydia Wang case VT1802:
34725eaba2fSLydia Wang verb = 0xf93;
34825eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
34925eaba2fSLydia Wang break;
35043737e0aSLydia Wang case VT1705CF:
3516121b84aSLydia Wang case VT1808:
35243737e0aSLydia Wang verb = 0xf82;
35343737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
35443737e0aSLydia Wang break;
355f5271101SLydia Wang default:
356f5271101SLydia Wang return; /* other codecs are not supported */
357f5271101SLydia Wang }
358f5271101SLydia Wang /* send verb */
3597639a06cSTakashi Iwai snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
360f5271101SLydia Wang }
361f5271101SLydia Wang
analog_low_current_mode(struct hda_codec * codec)362e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
363e9d010c2STakashi Iwai {
364e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false);
365e9d010c2STakashi Iwai }
366e9d010c2STakashi Iwai
via_playback_pcm_hook(struct hda_pcm_stream * hinfo,struct hda_codec * codec,struct snd_pcm_substream * substream,int action)367b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
368b3f6008fSTakashi Iwai struct hda_codec *codec,
369b3f6008fSTakashi Iwai struct snd_pcm_substream *substream,
370b3f6008fSTakashi Iwai int action)
371c577b8a1SJoseph Chan {
372b3f6008fSTakashi Iwai analog_low_current_mode(codec);
373b3f6008fSTakashi Iwai vt1708_update_hp_work(codec);
374c577b8a1SJoseph Chan }
375c577b8a1SJoseph Chan
via_free(struct hda_codec * codec)376c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
377c577b8a1SJoseph Chan {
378b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec);
379a8dca460STakashi Iwai snd_hda_gen_free(codec);
380c577b8a1SJoseph Chan }
381c577b8a1SJoseph Chan
3822a43952aSTakashi Iwai #ifdef CONFIG_PM
via_suspend(struct hda_codec * codec)38368cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
3841f2e99feSLydia Wang {
3851f2e99feSLydia Wang struct via_spec *spec = codec->spec;
386b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec);
38794c142a1SDavid Henningsson
38894c142a1SDavid Henningsson /* Fix pop noise on headphones */
3892c38d990STakashi Iwai if (spec->codec_type == VT1802)
3902c38d990STakashi Iwai snd_hda_shutup_pins(codec);
39194c142a1SDavid Henningsson
3921f2e99feSLydia Wang return 0;
3931f2e99feSLydia Wang }
3946b6d0007STakashi Iwai
via_resume(struct hda_codec * codec)3956b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec)
3966b6d0007STakashi Iwai {
3976b6d0007STakashi Iwai /* some delay here to make jack detection working (bko#98921) */
3986b6d0007STakashi Iwai msleep(10);
3996b6d0007STakashi Iwai codec->patch_ops.init(codec);
4001a462be5STakashi Iwai snd_hda_regmap_sync(codec);
4016b6d0007STakashi Iwai return 0;
4026b6d0007STakashi Iwai }
4031f2e99feSLydia Wang #endif
4041f2e99feSLydia Wang
40583012a7cSTakashi Iwai #ifdef CONFIG_PM
via_check_power_status(struct hda_codec * codec,hda_nid_t nid)406cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
407cb53c626STakashi Iwai {
408cb53c626STakashi Iwai struct via_spec *spec = codec->spec;
409b3f6008fSTakashi Iwai analog_low_current_mode(codec);
410b3f6008fSTakashi Iwai vt1708_update_hp_work(codec);
411b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
412cb53c626STakashi Iwai }
413cb53c626STakashi Iwai #endif
414cb53c626STakashi Iwai
415c577b8a1SJoseph Chan /*
416c577b8a1SJoseph Chan */
4175d41762aSTakashi Iwai
4185d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4195d41762aSTakashi Iwai
42090dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
4210e8f9862STakashi Iwai .build_controls = snd_hda_gen_build_controls,
422b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms,
423c577b8a1SJoseph Chan .init = via_init,
424c577b8a1SJoseph Chan .free = via_free,
4254e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event,
4262a43952aSTakashi Iwai #ifdef CONFIG_PM
4271f2e99feSLydia Wang .suspend = via_suspend,
4286b6d0007STakashi Iwai .resume = via_resume,
429cb53c626STakashi Iwai .check_power_status = via_check_power_status,
430cb53c626STakashi Iwai #endif
431c577b8a1SJoseph Chan };
432c577b8a1SJoseph Chan
4334a79616dSTakashi Iwai
434b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
435b3f6008fSTakashi Iwai /* power down jack detect function */
436b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1},
437b3f6008fSTakashi Iwai { }
4384a79616dSTakashi Iwai };
vt1708_set_pinconfig_connect(struct hda_codec * codec,hda_nid_t nid)43976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
44076d9b0ddSHarald Welte {
44176d9b0ddSHarald Welte unsigned int def_conf;
44276d9b0ddSHarald Welte unsigned char seqassoc;
44376d9b0ddSHarald Welte
4442f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid);
44576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf);
44676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
44782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
44882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) {
44976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
4502f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf);
45176d9b0ddSHarald Welte }
45276d9b0ddSHarald Welte }
45376d9b0ddSHarald Welte
vt1708_jack_detect_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)454e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
4551f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol)
4561f2e99feSLydia Wang {
4571f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4581f2e99feSLydia Wang struct via_spec *spec = codec->spec;
4591f2e99feSLydia Wang
4601f2e99feSLydia Wang if (spec->codec_type != VT1708)
4611f2e99feSLydia Wang return 0;
462e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
4631f2e99feSLydia Wang return 0;
4641f2e99feSLydia Wang }
4651f2e99feSLydia Wang
vt1708_jack_detect_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)466e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
4671f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol)
4681f2e99feSLydia Wang {
4691f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4701f2e99feSLydia Wang struct via_spec *spec = codec->spec;
471187d333eSTakashi Iwai int val;
4721f2e99feSLydia Wang
4731f2e99feSLydia Wang if (spec->codec_type != VT1708)
4741f2e99feSLydia Wang return 0;
475187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0];
476187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val)
477187d333eSTakashi Iwai return 0;
478187d333eSTakashi Iwai spec->vt1708_jack_detect = val;
479b3f6008fSTakashi Iwai vt1708_update_hp_work(codec);
480187d333eSTakashi Iwai return 1;
4811f2e99feSLydia Wang }
4821f2e99feSLydia Wang
4830e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
4841f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4851f2e99feSLydia Wang .name = "Jack Detect",
4861f2e99feSLydia Wang .count = 1,
4871f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info,
488e06e5a29STakashi Iwai .get = vt1708_jack_detect_get,
489e06e5a29STakashi Iwai .put = vt1708_jack_detect_put,
4901f2e99feSLydia Wang };
4911f2e99feSLydia Wang
4924abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
4934abdbd1cSTakashi Iwai .no_primary_dac = 0x10000,
4944abdbd1cSTakashi Iwai .no_dac = 0x4000,
4954abdbd1cSTakashi Iwai .shared_primary = 0x10000,
4964abdbd1cSTakashi Iwai .shared_surr = 0x20,
4974abdbd1cSTakashi Iwai .shared_clfe = 0x20,
4984abdbd1cSTakashi Iwai .shared_surr_main = 0x20,
4994abdbd1cSTakashi Iwai };
5004abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
5014abdbd1cSTakashi Iwai .no_primary_dac = 0x4000,
5024abdbd1cSTakashi Iwai .no_dac = 0x4000,
5034abdbd1cSTakashi Iwai .shared_primary = 0x12,
5044abdbd1cSTakashi Iwai .shared_surr = 0x20,
5054abdbd1cSTakashi Iwai .shared_clfe = 0x20,
5064abdbd1cSTakashi Iwai .shared_surr_main = 0x10,
5074abdbd1cSTakashi Iwai };
5084abdbd1cSTakashi Iwai
via_parse_auto_config(struct hda_codec * codec)509b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
510b3f6008fSTakashi Iwai {
511b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec;
512b3f6008fSTakashi Iwai int err;
513b3f6008fSTakashi Iwai
5144abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness;
5154abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness;
5164abdbd1cSTakashi Iwai
517b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
518b3f6008fSTakashi Iwai if (err < 0)
519b3f6008fSTakashi Iwai return err;
520b3f6008fSTakashi Iwai
521c7807b27STakashi Iwai err = auto_parse_beep(codec);
522b3f6008fSTakashi Iwai if (err < 0)
523b3f6008fSTakashi Iwai return err;
524b3f6008fSTakashi Iwai
525c7807b27STakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
5260e8f9862STakashi Iwai if (err < 0)
5270e8f9862STakashi Iwai return err;
5280e8f9862STakashi Iwai
5290e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
5300e8f9862STakashi Iwai return -ENOMEM;
5310e8f9862STakashi Iwai
532688b12ccSTakashi Iwai /* disable widget PM at start for compatibility */
533967b1307STakashi Iwai codec->power_save_node = 0;
534688b12ccSTakashi Iwai spec->gen.power_down_unused = 0;
535b3f6008fSTakashi Iwai return 0;
5364a918ffeSTakashi Iwai }
5374a918ffeSTakashi Iwai
via_init(struct hda_codec * codec)5385d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
5395d41762aSTakashi Iwai {
540e9d010c2STakashi Iwai /* init power states */
541e9d010c2STakashi Iwai __analog_low_current_mode(codec, true);
542e9d010c2STakashi Iwai
543b3f6008fSTakashi Iwai snd_hda_gen_init(codec);
54411890956SLydia Wang
545b3f6008fSTakashi Iwai vt1708_update_hp_work(codec);
54625eaba2fSLydia Wang
547c577b8a1SJoseph Chan return 0;
548c577b8a1SJoseph Chan }
549c577b8a1SJoseph Chan
vt1708_build_controls(struct hda_codec * codec)550f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
551f672f65aSDavid Henningsson {
552f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls,
553f672f65aSDavid Henningsson temporary enable jackpoll */
554f672f65aSDavid Henningsson int err;
555f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval;
556f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100);
5570e8f9862STakashi Iwai err = snd_hda_gen_build_controls(codec);
558f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval;
559f672f65aSDavid Henningsson return err;
560f672f65aSDavid Henningsson }
561f672f65aSDavid Henningsson
vt1708_build_pcms(struct hda_codec * codec)562b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
563337b9d02STakashi Iwai {
564337b9d02STakashi Iwai struct via_spec *spec = codec->spec;
565b3f6008fSTakashi Iwai int i, err;
566337b9d02STakashi Iwai
567b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec);
5687639a06cSTakashi Iwai if (err < 0 || codec->core.vendor_id != 0x11061708)
569b3f6008fSTakashi Iwai return err;
570b3f6008fSTakashi Iwai
571b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when
572b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found,
573b3f6008fSTakashi Iwai * disable the 24bit format, so far.
574b3f6008fSTakashi Iwai */
575bbbc7e85STakashi Iwai for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
576bbbc7e85STakashi Iwai struct hda_pcm *info = spec->gen.pcm_rec[i];
577bbbc7e85STakashi Iwai if (!info)
578bbbc7e85STakashi Iwai continue;
579b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
580b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO)
581b3f6008fSTakashi Iwai continue;
582b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
583b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE;
584337b9d02STakashi Iwai }
585b3f6008fSTakashi Iwai
5861c55d521STakashi Iwai return 0;
587337b9d02STakashi Iwai }
588337b9d02STakashi Iwai
patch_vt1708(struct hda_codec * codec)589c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
590c577b8a1SJoseph Chan {
591c577b8a1SJoseph Chan struct via_spec *spec;
592c577b8a1SJoseph Chan int err;
593c577b8a1SJoseph Chan
594c577b8a1SJoseph Chan /* create a codec specific record */
5955b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
596c577b8a1SJoseph Chan if (spec == NULL)
597c577b8a1SJoseph Chan return -ENOMEM;
598c577b8a1SJoseph Chan
599225068abSTakashi Iwai /* override some patch_ops */
600225068abSTakashi Iwai codec->patch_ops.build_controls = vt1708_build_controls;
601225068abSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms;
602b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17;
603b3f6008fSTakashi Iwai
604b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */
605b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100);
606b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1;
607b3f6008fSTakashi Iwai
608b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */
609b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */
610b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1;
611eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */
612eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1;
613620e2b28STakashi Iwai
61412daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */
61512daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
61612daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
61712daef65STakashi Iwai
618f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1708_init_verbs);
619f8bfc628STakashi Iwai if (err < 0)
620f8bfc628STakashi Iwai goto error;
621f8bfc628STakashi Iwai
622c577b8a1SJoseph Chan /* automatic parse from the BIOS config */
62312daef65STakashi Iwai err = via_parse_auto_config(codec);
624fcbdcc1aSTakashi Iwai if (err < 0)
625fcbdcc1aSTakashi Iwai goto error;
626c577b8a1SJoseph Chan
62712daef65STakashi Iwai /* add jack detect on/off control */
6280e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
6290e8f9862STakashi Iwai err = -ENOMEM;
6300e8f9862STakashi Iwai goto error;
6310e8f9862STakashi Iwai }
632c577b8a1SJoseph Chan
633b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */
634b3f6008fSTakashi Iwai codec->jackpoll_interval = 0;
635b3f6008fSTakashi Iwai
636c577b8a1SJoseph Chan return 0;
637fcbdcc1aSTakashi Iwai
638fcbdcc1aSTakashi Iwai error:
639fcbdcc1aSTakashi Iwai via_free(codec);
640fcbdcc1aSTakashi Iwai return err;
641c577b8a1SJoseph Chan }
642c577b8a1SJoseph Chan
patch_vt1709(struct hda_codec * codec)643ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
644c577b8a1SJoseph Chan {
645c577b8a1SJoseph Chan struct via_spec *spec;
646c577b8a1SJoseph Chan int err;
647c577b8a1SJoseph Chan
648c577b8a1SJoseph Chan /* create a codec specific record */
6495b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
650c577b8a1SJoseph Chan if (spec == NULL)
651c577b8a1SJoseph Chan return -ENOMEM;
652c577b8a1SJoseph Chan
653b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18;
654620e2b28STakashi Iwai
65512daef65STakashi Iwai err = via_parse_auto_config(codec);
656fcbdcc1aSTakashi Iwai if (err < 0)
657fcbdcc1aSTakashi Iwai goto error;
658c577b8a1SJoseph Chan
659f7278fd0SJosepch Chan return 0;
660fcbdcc1aSTakashi Iwai
661fcbdcc1aSTakashi Iwai error:
662fcbdcc1aSTakashi Iwai via_free(codec);
663fcbdcc1aSTakashi Iwai return err;
664f7278fd0SJosepch Chan }
665f7278fd0SJosepch Chan
666518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
patch_vt1708B(struct hda_codec * codec)667ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
668f7278fd0SJosepch Chan {
669f7278fd0SJosepch Chan struct via_spec *spec;
670f7278fd0SJosepch Chan int err;
671f7278fd0SJosepch Chan
672518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE)
673518bf3baSLydia Wang return patch_vt1708S(codec);
674ddd304d8STakashi Iwai
675f7278fd0SJosepch Chan /* create a codec specific record */
6765b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
677f7278fd0SJosepch Chan if (spec == NULL)
678f7278fd0SJosepch Chan return -ENOMEM;
679f7278fd0SJosepch Chan
680b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16;
681620e2b28STakashi Iwai
682f7278fd0SJosepch Chan /* automatic parse from the BIOS config */
68312daef65STakashi Iwai err = via_parse_auto_config(codec);
684fcbdcc1aSTakashi Iwai if (err < 0)
685fcbdcc1aSTakashi Iwai goto error;
686f7278fd0SJosepch Chan
687f7278fd0SJosepch Chan return 0;
688fcbdcc1aSTakashi Iwai
689fcbdcc1aSTakashi Iwai error:
690fcbdcc1aSTakashi Iwai via_free(codec);
691fcbdcc1aSTakashi Iwai return err;
692f7278fd0SJosepch Chan }
693f7278fd0SJosepch Chan
694d949cac1SHarald Welte /* Patch for VT1708S */
695096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
696d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */
697d7426329SHarald Welte {0x1, 0xf98, 0x1},
698bc7e7e5cSLydia Wang /* don't bybass mixer */
699bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0},
700d949cac1SHarald Welte { }
701d949cac1SHarald Welte };
702d949cac1SHarald Welte
override_mic_boost(struct hda_codec * codec,hda_nid_t pin,int offset,int num_steps,int step_size)7036369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
7046369bcfcSLydia Wang int offset, int num_steps, int step_size)
7056369bcfcSLydia Wang {
706d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin,
707d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
7086369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
7096369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) |
7106369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
7116369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
7126369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT));
7136369bcfcSLydia Wang }
7146369bcfcSLydia Wang
patch_vt1708S(struct hda_codec * codec)715d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
716d949cac1SHarald Welte {
717d949cac1SHarald Welte struct via_spec *spec;
718d949cac1SHarald Welte int err;
719d949cac1SHarald Welte
720d949cac1SHarald Welte /* create a codec specific record */
7215b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
722d949cac1SHarald Welte if (spec == NULL)
723d949cac1SHarald Welte return -ENOMEM;
724d949cac1SHarald Welte
725b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16;
726d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40);
727d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40);
728620e2b28STakashi Iwai
729518bf3baSLydia Wang /* correct names for VT1708BCE */
730ded255beSTakashi Iwai if (get_codec_type(codec) == VT1708BCE)
731ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1708BCE");
732bc92df7fSLydia Wang /* correct names for VT1705 */
733ded255beSTakashi Iwai if (codec->core.vendor_id == 0x11064397)
734ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1705");
735b3f6008fSTakashi Iwai
736f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
737f8bfc628STakashi Iwai if (err < 0)
738f8bfc628STakashi Iwai goto error;
739f8bfc628STakashi Iwai
740b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */
741b3f6008fSTakashi Iwai err = via_parse_auto_config(codec);
742fcbdcc1aSTakashi Iwai if (err < 0)
743fcbdcc1aSTakashi Iwai goto error;
744b3f6008fSTakashi Iwai
745d949cac1SHarald Welte return 0;
746fcbdcc1aSTakashi Iwai
747fcbdcc1aSTakashi Iwai error:
748fcbdcc1aSTakashi Iwai via_free(codec);
749fcbdcc1aSTakashi Iwai return err;
750d949cac1SHarald Welte }
751d949cac1SHarald Welte
752d949cac1SHarald Welte /* Patch for VT1702 */
753d949cac1SHarald Welte
754096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
755bc7e7e5cSLydia Wang /* mixer enable */
756bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3},
757bc7e7e5cSLydia Wang /* GPIO 0~2 */
758bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F},
759d949cac1SHarald Welte { }
760d949cac1SHarald Welte };
761d949cac1SHarald Welte
patch_vt1702(struct hda_codec * codec)762d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
763d949cac1SHarald Welte {
764d949cac1SHarald Welte struct via_spec *spec;
765d949cac1SHarald Welte int err;
766d949cac1SHarald Welte
767d949cac1SHarald Welte /* create a codec specific record */
7685b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
769d949cac1SHarald Welte if (spec == NULL)
770d949cac1SHarald Welte return -ENOMEM;
771d949cac1SHarald Welte
772b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a;
773620e2b28STakashi Iwai
77412daef65STakashi Iwai /* limit AA path volume to 0 dB */
77512daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
77612daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
77712daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
77812daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
77912daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT));
78012daef65STakashi Iwai
781f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1702_init_verbs);
782f8bfc628STakashi Iwai if (err < 0)
783f8bfc628STakashi Iwai goto error;
784f8bfc628STakashi Iwai
785d949cac1SHarald Welte /* automatic parse from the BIOS config */
78612daef65STakashi Iwai err = via_parse_auto_config(codec);
787fcbdcc1aSTakashi Iwai if (err < 0)
788fcbdcc1aSTakashi Iwai goto error;
789d949cac1SHarald Welte
790d949cac1SHarald Welte return 0;
791fcbdcc1aSTakashi Iwai
792fcbdcc1aSTakashi Iwai error:
793fcbdcc1aSTakashi Iwai via_free(codec);
794fcbdcc1aSTakashi Iwai return err;
795d949cac1SHarald Welte }
796d949cac1SHarald Welte
797eb7188caSLydia Wang /* Patch for VT1718S */
798eb7188caSLydia Wang
799096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
8004ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */
8014ab2d53aSLydia Wang {0x1, 0xfb2, 0x10},
802eb7188caSLydia Wang /* Enable Boost Volume backdoor */
803eb7188caSLydia Wang {0x1, 0xf88, 0x8},
8045d41762aSTakashi Iwai
805eb7188caSLydia Wang { }
806eb7188caSLydia Wang };
807eb7188caSLydia Wang
80830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
80930b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection.
81030b45033STakashi Iwai */
add_secret_dac_path(struct hda_codec * codec)81130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
81230b45033STakashi Iwai {
81330b45033STakashi Iwai struct via_spec *spec = codec->spec;
81430b45033STakashi Iwai int i, nums;
81530b45033STakashi Iwai hda_nid_t conn[8];
81630b45033STakashi Iwai hda_nid_t nid;
81730b45033STakashi Iwai
818b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid)
81930b45033STakashi Iwai return 0;
820b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
82130b45033STakashi Iwai ARRAY_SIZE(conn) - 1);
822b9cee506SArtemii Karasev if (nums < 0)
823b9cee506SArtemii Karasev return nums;
824b9cee506SArtemii Karasev
82530b45033STakashi Iwai for (i = 0; i < nums; i++) {
82630b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
82730b45033STakashi Iwai return 0;
82830b45033STakashi Iwai }
82930b45033STakashi Iwai
83030b45033STakashi Iwai /* find the primary DAC and add to the connection list */
8317639a06cSTakashi Iwai for_each_hda_codec_node(nid, codec) {
83230b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid);
83330b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
83430b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) {
83530b45033STakashi Iwai conn[nums++] = nid;
83630b45033STakashi Iwai return snd_hda_override_conn_list(codec,
837b3f6008fSTakashi Iwai spec->gen.mixer_nid,
83830b45033STakashi Iwai nums, conn);
83930b45033STakashi Iwai }
84030b45033STakashi Iwai }
84130b45033STakashi Iwai return 0;
84230b45033STakashi Iwai }
84330b45033STakashi Iwai
84430b45033STakashi Iwai
patch_vt1718S(struct hda_codec * codec)845eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
846eb7188caSLydia Wang {
847eb7188caSLydia Wang struct via_spec *spec;
848eb7188caSLydia Wang int err;
849eb7188caSLydia Wang
850eb7188caSLydia Wang /* create a codec specific record */
8515b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
852eb7188caSLydia Wang if (spec == NULL)
853eb7188caSLydia Wang return -ENOMEM;
854eb7188caSLydia Wang
855b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21;
856d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40);
857d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40);
85830b45033STakashi Iwai add_secret_dac_path(codec);
859620e2b28STakashi Iwai
860f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
861f8bfc628STakashi Iwai if (err < 0)
862f8bfc628STakashi Iwai goto error;
863f8bfc628STakashi Iwai
864eb7188caSLydia Wang /* automatic parse from the BIOS config */
86512daef65STakashi Iwai err = via_parse_auto_config(codec);
866fcbdcc1aSTakashi Iwai if (err < 0)
867fcbdcc1aSTakashi Iwai goto error;
868eb7188caSLydia Wang
869eb7188caSLydia Wang return 0;
870fcbdcc1aSTakashi Iwai
871fcbdcc1aSTakashi Iwai error:
872fcbdcc1aSTakashi Iwai via_free(codec);
873fcbdcc1aSTakashi Iwai return err;
874eb7188caSLydia Wang }
875f3db423dSLydia Wang
876f3db423dSLydia Wang /* Patch for VT1716S */
877f3db423dSLydia Wang
vt1716s_dmic_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)878f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
879f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo)
880f3db423dSLydia Wang {
881f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
882f3db423dSLydia Wang uinfo->count = 1;
883f3db423dSLydia Wang uinfo->value.integer.min = 0;
884f3db423dSLydia Wang uinfo->value.integer.max = 1;
885f3db423dSLydia Wang return 0;
886f3db423dSLydia Wang }
887f3db423dSLydia Wang
vt1716s_dmic_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)888f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
889f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol)
890f3db423dSLydia Wang {
891f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
892f3db423dSLydia Wang int index = 0;
893f3db423dSLydia Wang
894f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0,
895f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0);
896f3db423dSLydia Wang if (index != -1)
897f3db423dSLydia Wang *ucontrol->value.integer.value = index;
898f3db423dSLydia Wang
899f3db423dSLydia Wang return 0;
900f3db423dSLydia Wang }
901f3db423dSLydia Wang
vt1716s_dmic_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)902f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
903f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol)
904f3db423dSLydia Wang {
905f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
906f3db423dSLydia Wang struct via_spec *spec = codec->spec;
907f3db423dSLydia Wang int index = *ucontrol->value.integer.value;
908f3db423dSLydia Wang
909f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0,
910f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index);
911f3db423dSLydia Wang spec->dmic_enabled = index;
912f3db423dSLydia Wang return 1;
913f3db423dSLydia Wang }
914f3db423dSLydia Wang
9150e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
9160e8f9862STakashi Iwai HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
9170e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
918f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
919f3db423dSLydia Wang .name = "Digital Mic Capture Switch",
9205b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
921f3db423dSLydia Wang .count = 1,
922f3db423dSLydia Wang .info = vt1716s_dmic_info,
923f3db423dSLydia Wang .get = vt1716s_dmic_get,
924f3db423dSLydia Wang .put = vt1716s_dmic_put,
925f3db423dSLydia Wang };
926f3db423dSLydia Wang
927f3db423dSLydia Wang
928f3db423dSLydia Wang /* mono-out mixer elements */
9290e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
9300e8f9862STakashi Iwai HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
931f3db423dSLydia Wang
932096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
933f3db423dSLydia Wang /* Enable Boost Volume backdoor */
934f3db423dSLydia Wang {0x1, 0xf8a, 0x80},
935f3db423dSLydia Wang /* don't bybass mixer */
936f3db423dSLydia Wang {0x1, 0xf88, 0xc0},
937f3db423dSLydia Wang /* Enable mono output */
938f3db423dSLydia Wang {0x1, 0xf90, 0x08},
939f3db423dSLydia Wang { }
940f3db423dSLydia Wang };
941f3db423dSLydia Wang
patch_vt1716S(struct hda_codec * codec)942f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
943f3db423dSLydia Wang {
944f3db423dSLydia Wang struct via_spec *spec;
945f3db423dSLydia Wang int err;
946f3db423dSLydia Wang
947f3db423dSLydia Wang /* create a codec specific record */
9485b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
949f3db423dSLydia Wang if (spec == NULL)
950f3db423dSLydia Wang return -ENOMEM;
951f3db423dSLydia Wang
952b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16;
953d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40);
954d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40);
955620e2b28STakashi Iwai
956f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
957f8bfc628STakashi Iwai if (err < 0)
958f8bfc628STakashi Iwai goto error;
959f8bfc628STakashi Iwai
960f3db423dSLydia Wang /* automatic parse from the BIOS config */
96112daef65STakashi Iwai err = via_parse_auto_config(codec);
962fcbdcc1aSTakashi Iwai if (err < 0)
963fcbdcc1aSTakashi Iwai goto error;
964f3db423dSLydia Wang
9650e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
9660e8f9862STakashi Iwai !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
9670e8f9862STakashi Iwai !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
9680e8f9862STakashi Iwai err = -ENOMEM;
9690e8f9862STakashi Iwai goto error;
9700e8f9862STakashi Iwai }
971f3db423dSLydia Wang
972f3db423dSLydia Wang return 0;
973fcbdcc1aSTakashi Iwai
974fcbdcc1aSTakashi Iwai error:
975fcbdcc1aSTakashi Iwai via_free(codec);
976fcbdcc1aSTakashi Iwai return err;
977f3db423dSLydia Wang }
97825eaba2fSLydia Wang
97925eaba2fSLydia Wang /* for vt2002P */
98025eaba2fSLydia Wang
981096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
982eadb9a80SLydia Wang /* Class-D speaker related verbs */
983eadb9a80SLydia Wang {0x1, 0xfe0, 0x4},
984eadb9a80SLydia Wang {0x1, 0xfe9, 0x80},
985eadb9a80SLydia Wang {0x1, 0xfe2, 0x22},
98625eaba2fSLydia Wang /* Enable Boost Volume backdoor */
98725eaba2fSLydia Wang {0x1, 0xfb9, 0x24},
98825eaba2fSLydia Wang /* Enable AOW0 to MW9 */
98925eaba2fSLydia Wang {0x1, 0xfb8, 0x88},
99025eaba2fSLydia Wang { }
99125eaba2fSLydia Wang };
9924a918ffeSTakashi Iwai
993096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
99411890956SLydia Wang /* Enable Boost Volume backdoor */
99511890956SLydia Wang {0x1, 0xfb9, 0x24},
99611890956SLydia Wang /* Enable AOW0 to MW9 */
99711890956SLydia Wang {0x1, 0xfb8, 0x88},
99811890956SLydia Wang { }
99911890956SLydia Wang };
100025eaba2fSLydia Wang
10014b527b65SDavid Henningsson /*
10024b527b65SDavid Henningsson * pin fix-up
10034b527b65SDavid Henningsson */
10044b527b65SDavid Henningsson enum {
10054b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST,
1006d5266125STakashi Iwai VIA_FIXUP_ASUS_G75,
10074bfd6247STakashi Iwai VIA_FIXUP_POWER_SAVE,
10084b527b65SDavid Henningsson };
10094b527b65SDavid Henningsson
via_fixup_intmic_boost(struct hda_codec * codec,const struct hda_fixup * fix,int action)10104b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
10114b527b65SDavid Henningsson const struct hda_fixup *fix, int action)
10124b527b65SDavid Henningsson {
10134b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE)
10144b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40);
10154b527b65SDavid Henningsson }
10164b527b65SDavid Henningsson
via_fixup_power_save(struct hda_codec * codec,const struct hda_fixup * fix,int action)10174bfd6247STakashi Iwai static void via_fixup_power_save(struct hda_codec *codec,
10184bfd6247STakashi Iwai const struct hda_fixup *fix, int action)
10194bfd6247STakashi Iwai {
10204bfd6247STakashi Iwai if (action == HDA_FIXUP_ACT_PRE_PROBE)
10214bfd6247STakashi Iwai codec->power_save_node = 0;
10224bfd6247STakashi Iwai }
10234bfd6247STakashi Iwai
10244b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
10254b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = {
10264b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC,
10274b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost,
10284b527b65SDavid Henningsson },
1029d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = {
1030d5266125STakashi Iwai .type = HDA_FIXUP_PINS,
1031d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) {
1032d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */
1033d5266125STakashi Iwai { 0x24, 0x991301f0 },
1034d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */
1035d5266125STakashi Iwai { }
1036d5266125STakashi Iwai }
1037d5266125STakashi Iwai },
10384bfd6247STakashi Iwai [VIA_FIXUP_POWER_SAVE] = {
10394bfd6247STakashi Iwai .type = HDA_FIXUP_FUNC,
10404bfd6247STakashi Iwai .v.func = via_fixup_power_save,
10414bfd6247STakashi Iwai },
10424b527b65SDavid Henningsson };
10434b527b65SDavid Henningsson
1044*f4306ef1STakashi Iwai static const struct hda_quirk vt2002p_fixups[] = {
10454bf61ad5STakashi Iwai SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE),
1046d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10474b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
10484961167bSTakashi Iwai SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
10494b527b65SDavid Henningsson {}
10504b527b65SDavid Henningsson };
10514b527b65SDavid Henningsson
1052ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1053ef4da458STakashi Iwai * Replace this with mixer NID 0x1c
1054ef4da458STakashi Iwai */
fix_vt1802_connections(struct hda_codec * codec)1055ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1056ef4da458STakashi Iwai {
1057caf3c043SMichał Mirosław static const hda_nid_t conn_24[] = { 0x14, 0x1c };
1058caf3c043SMichał Mirosław static const hda_nid_t conn_33[] = { 0x1c };
1059ef4da458STakashi Iwai
1060ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1061ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1062ef4da458STakashi Iwai }
1063ef4da458STakashi Iwai
106425eaba2fSLydia Wang /* patch for vt2002P */
patch_vt2002P(struct hda_codec * codec)106525eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
106625eaba2fSLydia Wang {
106725eaba2fSLydia Wang struct via_spec *spec;
106825eaba2fSLydia Wang int err;
106925eaba2fSLydia Wang
107025eaba2fSLydia Wang /* create a codec specific record */
10715b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
107225eaba2fSLydia Wang if (spec == NULL)
107325eaba2fSLydia Wang return -ENOMEM;
107425eaba2fSLydia Wang
1075b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21;
1076d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40);
1077d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40);
1078ef4da458STakashi Iwai if (spec->codec_type == VT1802)
1079ef4da458STakashi Iwai fix_vt1802_connections(codec);
108030b45033STakashi Iwai add_secret_dac_path(codec);
1081620e2b28STakashi Iwai
10824b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
10834b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
10844b527b65SDavid Henningsson
1085f8bfc628STakashi Iwai if (spec->codec_type == VT1802)
1086f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1802_init_verbs);
1087f8bfc628STakashi Iwai else
1088f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
1089f8bfc628STakashi Iwai if (err < 0)
1090f8bfc628STakashi Iwai goto error;
1091f8bfc628STakashi Iwai
109225eaba2fSLydia Wang /* automatic parse from the BIOS config */
109312daef65STakashi Iwai err = via_parse_auto_config(codec);
1094fcbdcc1aSTakashi Iwai if (err < 0)
1095fcbdcc1aSTakashi Iwai goto error;
109625eaba2fSLydia Wang
109725eaba2fSLydia Wang return 0;
1098fcbdcc1aSTakashi Iwai
1099fcbdcc1aSTakashi Iwai error:
1100fcbdcc1aSTakashi Iwai via_free(codec);
1101fcbdcc1aSTakashi Iwai return err;
110225eaba2fSLydia Wang }
1103ab6734e7SLydia Wang
1104ab6734e7SLydia Wang /* for vt1812 */
1105ab6734e7SLydia Wang
1106096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1107ab6734e7SLydia Wang /* Enable Boost Volume backdoor */
1108ab6734e7SLydia Wang {0x1, 0xfb9, 0x24},
1109ab6734e7SLydia Wang /* Enable AOW0 to MW9 */
1110ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8},
1111ab6734e7SLydia Wang { }
1112ab6734e7SLydia Wang };
1113ab6734e7SLydia Wang
1114ab6734e7SLydia Wang /* patch for vt1812 */
patch_vt1812(struct hda_codec * codec)1115ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1116ab6734e7SLydia Wang {
1117ab6734e7SLydia Wang struct via_spec *spec;
1118ab6734e7SLydia Wang int err;
1119ab6734e7SLydia Wang
1120ab6734e7SLydia Wang /* create a codec specific record */
11215b0cb1d8SJaroslav Kysela spec = via_new_spec(codec);
1122ab6734e7SLydia Wang if (spec == NULL)
1123ab6734e7SLydia Wang return -ENOMEM;
1124ab6734e7SLydia Wang
1125b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21;
1126d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40);
1127d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40);
112830b45033STakashi Iwai add_secret_dac_path(codec);
1129620e2b28STakashi Iwai
1130f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1812_init_verbs);
1131f8bfc628STakashi Iwai if (err < 0)
1132f8bfc628STakashi Iwai goto error;
1133f8bfc628STakashi Iwai
1134ab6734e7SLydia Wang /* automatic parse from the BIOS config */
113512daef65STakashi Iwai err = via_parse_auto_config(codec);
1136fcbdcc1aSTakashi Iwai if (err < 0)
1137fcbdcc1aSTakashi Iwai goto error;
1138ab6734e7SLydia Wang
1139ab6734e7SLydia Wang return 0;
1140fcbdcc1aSTakashi Iwai
1141fcbdcc1aSTakashi Iwai error:
1142fcbdcc1aSTakashi Iwai via_free(codec);
1143fcbdcc1aSTakashi Iwai return err;
1144ab6734e7SLydia Wang }
1145ab6734e7SLydia Wang
114643737e0aSLydia Wang /* patch for vt3476 */
114743737e0aSLydia Wang
114843737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
114943737e0aSLydia Wang /* Enable DMic 8/16/32K */
115043737e0aSLydia Wang {0x1, 0xF7B, 0x30},
115143737e0aSLydia Wang /* Enable Boost Volume backdoor */
115243737e0aSLydia Wang {0x1, 0xFB9, 0x20},
115343737e0aSLydia Wang /* Enable AOW-MW9 path */
115443737e0aSLydia Wang {0x1, 0xFB8, 0x10},
115543737e0aSLydia Wang { }
115643737e0aSLydia Wang };
115743737e0aSLydia Wang
patch_vt3476(struct hda_codec * codec)115843737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
115943737e0aSLydia Wang {
116043737e0aSLydia Wang struct via_spec *spec;
116143737e0aSLydia Wang int err;
116243737e0aSLydia Wang
116343737e0aSLydia Wang /* create a codec specific record */
116443737e0aSLydia Wang spec = via_new_spec(codec);
116543737e0aSLydia Wang if (spec == NULL)
116643737e0aSLydia Wang return -ENOMEM;
116743737e0aSLydia Wang
1168b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f;
116943737e0aSLydia Wang add_secret_dac_path(codec);
117043737e0aSLydia Wang
1171f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt3476_init_verbs);
1172f8bfc628STakashi Iwai if (err < 0)
1173f8bfc628STakashi Iwai goto error;
1174f8bfc628STakashi Iwai
117543737e0aSLydia Wang /* automatic parse from the BIOS config */
117643737e0aSLydia Wang err = via_parse_auto_config(codec);
1177fcbdcc1aSTakashi Iwai if (err < 0)
1178fcbdcc1aSTakashi Iwai goto error;
117943737e0aSLydia Wang
118043737e0aSLydia Wang return 0;
1181fcbdcc1aSTakashi Iwai
1182fcbdcc1aSTakashi Iwai error:
1183fcbdcc1aSTakashi Iwai via_free(codec);
1184fcbdcc1aSTakashi Iwai return err;
118543737e0aSLydia Wang }
118643737e0aSLydia Wang
1187c577b8a1SJoseph Chan /*
1188c577b8a1SJoseph Chan * patch entries
1189c577b8a1SJoseph Chan */
1190b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = {
1191b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
1192b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
1193b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
1194b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
1195b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
1196b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
1197b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
1198b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
1199b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
1200b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
1201b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
1202b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
1203b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
1204b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
1205b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
1206b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
1207b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
1208b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
1209b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
1210b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
1211b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
1212b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
1213b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
1214b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
1215b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
1216b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
1217b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
1218b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
1219b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
1220b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
1221b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
1222b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
1223b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
1224b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
1225b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
1226b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
1227b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
1228b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
1229b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
1230b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
1231b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
1232b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
1233b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
1234b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
1235b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
1236b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
1237b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
1238b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
1239b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
1240b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
1241b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
1242c577b8a1SJoseph Chan {} /* terminator */
1243c577b8a1SJoseph Chan };
1244b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
12451289e9e8STakashi Iwai
1246d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
1247b9a94a9cSTakashi Iwai .id = snd_hda_id_via,
12481289e9e8STakashi Iwai };
12491289e9e8STakashi Iwai
12501289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12511289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12521289e9e8STakashi Iwai
1253d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1254