diff options
Diffstat (limited to 'sound/pci/hda/patch_via.c')
| -rw-r--r-- | sound/pci/hda/patch_via.c | 6221 |
1 files changed, 1023 insertions, 5198 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 1371b57c11e..778166259b3 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -49,41 +49,19 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/asoundef.h> #include "hda_codec.h" #include "hda_local.h" - -#define NID_MAPPING (-1) - -/* amp values */ -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" /* Pin Widget NID */ -#define VT1708_HP_NID 0x13 -#define VT1708_DIGOUT_NID 0x14 -#define VT1708_DIGIN_NID 0x16 -#define VT1708_DIGIN_PIN 0x26 #define VT1708_HP_PIN_NID 0x20 #define VT1708_CD_PIN_NID 0x24 -#define VT1709_HP_DAC_NID 0x28 -#define VT1709_DIGOUT_NID 0x13 -#define VT1709_DIGIN_NID 0x17 -#define VT1709_DIGIN_PIN 0x25 - -#define VT1708B_HP_NID 0x25 -#define VT1708B_DIGOUT_NID 0x12 -#define VT1708B_DIGIN_NID 0x15 -#define VT1708B_DIGIN_PIN 0x21 - -#define VT1708S_HP_NID 0x25 -#define VT1708S_DIGOUT_NID 0x12 - -#define VT1702_HP_NID 0x17 -#define VT1702_DIGOUT_NID 0x11 - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, @@ -98,69 +76,51 @@ enum VIA_HDA_CODEC { VT1716S, VT2002P, VT1812, + VT1802, + VT1705CF, + VT1808, CODEC_TYPES, }; +#define VT2002P_COMPATIBLE(spec) \ + ((spec)->codec_type == VT2002P ||\ + (spec)->codec_type == VT1812 ||\ + (spec)->codec_type == VT1802) + struct via_spec { + struct hda_gen_spec gen; + /* codec parameterization */ - struct snd_kcontrol_new *mixers[6]; + const struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; - struct hda_verb *init_verbs[5]; + const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; - char *stream_name_analog; - struct hda_pcm_stream *stream_analog_playback; - struct hda_pcm_stream *stream_analog_capture; - - char *stream_name_digital; - struct hda_pcm_stream *stream_digital_playback; - struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; - hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; - - /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux[2]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - /* HP mode source */ - const struct hda_input_mux *hp_mux; - unsigned int hp_independent_mode; - unsigned int hp_independent_mode_index; - unsigned int smart51_enabled; unsigned int dmic_enabled; + unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; + /* analog low-power control */ + bool alc_mode; + /* work to check hp jack state */ - struct hda_codec *codec; - struct delayed_work vt1708_hp_work; - int vt1708_jack_detectect; - int vt1708_hp_present; -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; -#endif + int hp_work_active; + int vt1708_jack_detect; + + void (*set_widgets_power_state)(struct hda_codec *codec); + unsigned int dac_stream_tag[4]; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static struct via_spec * via_new_spec(struct hda_codec *codec) +static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); +static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); + +static struct via_spec *via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -169,11 +129,16 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) return NULL; codec->spec = spec; - spec->codec = codec; + snd_hda_gen_spec_init(&spec->gen); spec->codec_type = get_codec_type(codec); /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; + spec->no_pin_power_ctl = 1; + spec->gen.indep_hp = 1; + spec->gen.keep_eapd_on = 1; + spec->gen.pcm_playback_hook = via_playback_pcm_hook; + spec->gen.add_stereo_mix_input = 1; return spec; } @@ -218,367 +183,111 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1812; else if (dev_id == 0x0440) codec_type = VT1708S; + else if ((dev_id & 0xfff) == 0x446) + codec_type = VT1802; + else if (dev_id == 0x4760) + codec_type = VT1705CF; + else if (dev_id == 0x4761 || dev_id == 0x4762) + codec_type = VT1808; else codec_type = UNKNOWN; return codec_type; }; -#define VIA_HP_EVENT 0x01 -#define VIA_GPIO_EVENT 0x02 -#define VIA_JACK_EVENT 0x04 -#define VIA_MONO_EVENT 0x08 -#define VIA_SPEAKER_EVENT 0x10 -#define VIA_BIND_HP_EVENT 0x20 +static void analog_low_current_mode(struct hda_codec *codec); +static bool is_aa_path_mute(struct hda_codec *codec); -enum { - VIA_CTL_WIDGET_VOL, - VIA_CTL_WIDGET_MUTE, - VIA_CTL_WIDGET_ANALOG_MUTE, - VIA_CTL_WIDGET_BIND_PIN_MUTE, -}; +#define hp_detect_with_aa(codec) \ + (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ + !is_aa_path_mute(codec)) -enum { - AUTO_SEQ_FRONT = 0, - AUTO_SEQ_SURROUND, - AUTO_SEQ_CENLFE, - AUTO_SEQ_SIDE -}; - -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); -static void set_jack_power_state(struct hda_codec *codec); -static int is_aa_path_mute(struct hda_codec *codec); - -static void vt1708_start_hp_work(struct via_spec *spec) +static void vt1708_stop_hp_work(struct hda_codec *codec) { - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) - return; - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); - if (!delayed_work_pending(&spec->vt1708_hp_work)) - schedule_delayed_work(&spec->vt1708_hp_work, - msecs_to_jiffies(100)); -} - -static void vt1708_stop_hp_work(struct via_spec *spec) -{ - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) - return; - if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 - && !is_aa_path_mute(spec->codec)) + struct via_spec *spec = codec->spec; + if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) return; - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); - cancel_delayed_work_sync(&spec->vt1708_hp_work); -} - - -static int analog_input_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - set_jack_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); - if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { - if (is_aa_path_mute(codec)) - vt1708_start_hp_work(codec->spec); - else - vt1708_stop_hp_work(codec->spec); + if (spec->hp_work_active) { + snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); + codec->jackpoll_interval = 0; + cancel_delayed_work_sync(&codec->jackpoll_work); + spec->hp_work_active = false; } - return change; } -/* modify .put = snd_hda_mixer_amp_switch_put */ -#define ANALOG_INPUT_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = analog_input_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - -static void via_hp_bind_automute(struct hda_codec *codec); - -static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void vt1708_update_hp_work(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - int i; - int change = 0; - - long *valp = ucontrol->value.integer.value; - int lmute, rmute; - if (strstr(kcontrol->id.name, "Switch") == NULL) { - snd_printd("Invalid control!\n"); - return change; - } - change = snd_hda_mixer_amp_switch_put(kcontrol, - ucontrol); - /* Get mute value */ - lmute = *valp ? 0 : HDA_AMP_MUTE; - valp++; - rmute = *valp ? 0 : HDA_AMP_MUTE; - - /* Set hp pins */ - if (!spec->hp_independent_mode) { - for (i = 0; i < spec->autocfg.hp_outs; i++) { - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - snd_hda_codec_amp_update( - codec, spec->autocfg.hp_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - } - } - - if (!lmute && !rmute) { - /* Line Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - /* unmute */ - via_hp_bind_automute(codec); - - } else { - if (lmute) { - /* Mute all left channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, - lmute); - } - if (rmute) { - /* mute all right channels */ - for (i = 1; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.line_out_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_update( - codec, - spec->autocfg.speaker_pins[i], - 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, - rmute); + if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) + return; + if (spec->vt1708_jack_detect) { + if (!spec->hp_work_active) { + codec->jackpoll_interval = msecs_to_jiffies(100); + snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); + queue_delayed_work(codec->bus->workq, + &codec->jackpoll_work, 0); + spec->hp_work_active = true; } - } - return change; + } else if (!hp_detect_with_aa(codec)) + vt1708_stop_hp_work(codec); } -#define BIND_PIN_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = bind_pin_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - -static struct snd_kcontrol_new via_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - ANALOG_INPUT_MUTE, - BIND_PIN_MUTE, -}; - -static hda_nid_t vt1708_adc_nids[2] = { - /* ADC1-2 */ - 0x15, 0x27 -}; - -static hda_nid_t vt1709_adc_nids[3] = { - /* ADC1-2 */ - 0x14, 0x15, 0x16 -}; - -static hda_nid_t vt1708B_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static hda_nid_t vt1708S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static hda_nid_t vt1702_adc_nids[3] = { - /* ADC1-2 */ - 0x12, 0x20, 0x1F -}; - -static hda_nid_t vt1718S_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static hda_nid_t vt1716S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static hda_nid_t vt2002P_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static hda_nid_t vt1812_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - - -/* add dynamic controls */ -static int __via_add_control(struct via_spec *spec, int type, const char *name, - int idx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return -ENOMEM; - *knew = via_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return -ENOMEM; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -#define via_add_control(spec, type, name, val) \ - __via_add_control(spec, type, name, 0, val) - -static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, - struct snd_kcontrol_new *tmpl) -{ - struct snd_kcontrol_new *knew; - - snd_array_init(&spec->kctls, sizeof(*knew), 32); - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *tmpl; - knew->name = kstrdup(tmpl->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} - -static void via_free_kctls(struct hda_codec *codec) +static void set_widgets_power_state(struct hda_codec *codec) { +#if 0 /* FIXME: the assumed connections don't match always with the + * actual routes by the generic parser, so better to disable + * the control for safety. + */ struct via_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -/* create input playback/capture controls for the given pin */ -static int via_new_analog_input(struct via_spec *spec, const char *ctlname, - int type_idx, int idx, int mix_nid) -{ - char name[32]; - int err; - - sprintf(name, "%s Playback Volume", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - return 0; + if (spec->set_widgets_power_state) + spec->set_widgets_power_state(codec); +#endif } -static void via_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +static void update_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int parm) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); + if (snd_hda_check_power_state(codec, nid, parm)) + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } - -static void via_auto_init_multi_out(struct hda_codec *codec) +static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int parm, unsigned int index) { struct via_spec *spec = codec->spec; - int i; + unsigned int format; + + if (snd_hda_check_power_state(codec, nid, parm)) + return; + format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + if (format && (spec->dac_stream_tag[index] != format)) + spec->dac_stream_tag[index] = format; - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); + if (parm == AC_PWRST_D0) { + format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + if (!format && (spec->dac_stream_tag[index] != format)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + spec->dac_stream_tag[index]); } } -static void via_auto_init_hp_out(struct hda_codec *codec) +static bool smart51_enabled(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - hda_nid_t pin; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - pin = spec->autocfg.hp_pins[i]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - } + return spec->gen.ext_channel_count > 2; } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); - -static void via_auto_init_analog_input(struct hda_codec *codec) +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int ctl; int i; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (spec->smart51_enabled && is_smart51_pins(spec, nid)) - ctl = PIN_OUT; - else if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl = PIN_VREF50; - else - ctl = PIN_IN; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); - } + for (i = 0; i < spec->gen.multi_ios; i++) + if (spec->gen.multi_io[i].pin == pin) + return true; + return false; } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, @@ -589,9 +298,13 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned no_presence = (def_conf & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - unsigned present = snd_hda_jack_detect(codec, nid); struct via_spec *spec = codec->spec; - if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + unsigned present = 0; + + no_presence |= spec->no_pin_power_ctl; + if (!no_presence) + present = snd_hda_jack_detect(codec, nid); + if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -599,990 +312,86 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, } else parm = AC_PWRST_D3; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, nid, parm); } -static void set_jack_power_state(struct hda_codec *codec) +static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - - if (spec->codec_type == VT1702) { - imux_is_smixer = snd_hda_codec_read( - codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - /* inputs */ - /* PW 1/2/5 (14h/15h/18h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x14, &parm); - set_pin_power_state(codec, 0x15, &parm); - set_pin_power_state(codec, 0x18, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ - /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* outputs */ - /* PW 3/4 (16h/17h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x16, &parm); - set_pin_power_state(codec, 0x17, &parm); - /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, - parm); - } else if (spec->codec_type == VT1708B_8CH - || spec->codec_type == VT1708B_4CH - || spec->codec_type == VT1708S) { - /* SW0 (17h) = stereo mixer */ - int is_8ch = spec->codec_type != VT1708B_4CH; - imux_is_smixer = snd_hda_codec_read( - codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) - == ((spec->codec_type == VT1708S) ? 5 : 0); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW 0/1 (13h/14h) */ - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - if (spec->smart51_enabled) - parm = AC_PWRST_D0; - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* PW6 (22h), SW2 (26h), AOW2 (24h) */ - if (is_8ch) { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x22, &parm); - if (spec->smart51_enabled) - parm = AC_PWRST_D0; - snd_hda_codec_write(codec, 0x26, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x24, 0, - AC_VERB_SET_POWER_STATE, parm); - } - - /* PW 3/4/7 (1ch/1dh/23h) */ - parm = AC_PWRST_D3; - /* force to D0 for internal Speaker */ - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - if (is_8ch) - set_pin_power_state(codec, 0x23, &parm); - /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, - parm); - if (is_8ch) { - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x27, 0, - AC_VERB_SET_POWER_STATE, parm); - } - } else if (spec->codec_type == VT1718S) { - /* MUX6 (1eh) = stereo mixer */ - imux_is_smixer = snd_hda_codec_read( - codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* outputs */ - /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* PW2 (26h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* PW0/1 (24h/25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - set_pin_power_state(codec, 0x25, &parm); - if (!spec->hp_independent_mode) /* check for redirected HP */ - set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, - parm); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - if (spec->hp_independent_mode) { - /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0xc, 0, - AC_VERB_SET_POWER_STATE, parm); - } - } else if (spec->codec_type == VT1716S) { - unsigned int mono_out, present; - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = snd_hda_codec_read( - codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW0(13h) */ - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, - parm); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1e, &parm); - /* PW11 (22h) */ - if (spec->dmic_enabled) - set_pin_power_state(codec, 0x22, &parm); - else - snd_hda_codec_write( - codec, 0x22, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - /* SW2(26h), AIW1(14h) */ - snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - /* Smart 5.1 PW2(1bh) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1b, &parm); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* PW7 (23h), SW3 (27h), AOW3 (25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - /* Smart 5.1 PW1(1ah) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1a, &parm); - snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* Smart 5.1 PW5(1eh) */ - if (spec->smart51_enabled) - set_pin_power_state(codec, 0x1e, &parm); - snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* Mono out */ - /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_jack_detect(codec, 0x1c); - if (present) - mono_out = 0; - else { - present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->hp_independent_mode && present) - mono_out = 0; - else - mono_out = 1; - } - parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, - parm); - snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, - parm); - - /* PW 3/4 (1ch/1dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - /* HP Independent Mode, power on AOW3 */ - if (spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); - - /* force to D0 for internal Speaker */ - /* MW0 (16h), AOW0 (10h) */ - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, - mono_out ? AC_PWRST_D0 : parm); - } else if (spec->codec_type == VT2002P) { - unsigned int present; - /* MUX9 (1eh) = stereo mixer */ - imux_is_smixer = snd_hda_codec_read( - codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_POWER_STATE, parm); - - /* outputs */ - /* AOW0 (8h)*/ - snd_hda_codec_write(codec, 0x8, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - /* PW4 (26h), MW4 (1ch), MUX4(37h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x37, - 0, AC_VERB_SET_POWER_STATE, parm); - - /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_POWER_STATE, parm); - if (spec->hp_independent_mode) { - snd_hda_codec_write(codec, 0x9, 0, - AC_VERB_SET_POWER_STATE, parm); - } - - /* Class-D */ - /* PW0 (24h), MW0(18h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (present) { - snd_hda_codec_write( - codec, 0x18, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write( - codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - } else { - snd_hda_codec_write( - codec, 0x18, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - snd_hda_codec_write( - codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - } - - /* Mono Out */ - /* PW15 (31h), MW8(17h), MUX8(3bh) */ - present = snd_hda_jack_detect(codec, 0x26); - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x31, &parm); - if (present) { - snd_hda_codec_write( - codec, 0x17, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write( - codec, 0x3b, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - } else { - snd_hda_codec_write( - codec, 0x17, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - snd_hda_codec_write( - codec, 0x3b, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - } - - /* MW9 (21h) */ - if (imux_is_smixer || !is_aa_path_mute(codec)) - snd_hda_codec_write( - codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - else - snd_hda_codec_write( - codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - } else if (spec->codec_type == VT1812) { - unsigned int present; - /* MUX10 (1eh) = stereo mixer */ - imux_is_smixer = snd_hda_codec_read( - codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_POWER_STATE, parm); - - /* outputs */ - /* AOW0 (8h)*/ - snd_hda_codec_write(codec, 0x8, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x18, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x38, 0, - AC_VERB_SET_POWER_STATE, parm); - - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - snd_hda_codec_write(codec, 0x15, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_POWER_STATE, parm); - if (spec->hp_independent_mode) { - snd_hda_codec_write(codec, 0x9, 0, - AC_VERB_SET_POWER_STATE, parm); - } - - /* Internal Speaker */ - /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (present) { - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - } else { - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - } - /* Mono Out */ - /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_jack_detect(codec, 0x28); - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x31, &parm); - if (present) { - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - snd_hda_codec_write(codec, 0x3c, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - snd_hda_codec_write(codec, 0x3e, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - } else { - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - snd_hda_codec_write(codec, 0x3c, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - snd_hda_codec_write(codec, 0x3e, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - } - - /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x33, &parm); - snd_hda_codec_write(codec, 0x1d, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x3d, 0, - AC_VERB_SET_POWER_STATE, parm); - - /* MW9 (21h) */ - if (imux_is_smixer || !is_aa_path_mute(codec)) - snd_hda_codec_write( - codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - else - snd_hda_codec_write( - codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - } + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); } -/* - * input MUX handling - */ -static int via_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; return 0; } -static int via_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - int ret; - - if (!spec->mux_nids[adc_idx]) - return -EINVAL; - /* switch to D0 beofre change index */ - if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) - snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->mux_nids[adc_idx], - &spec->cur_mux[adc_idx]); - /* update jack power state */ - set_jack_power_state(codec); - - return ret; -} + unsigned int val = !ucontrol->value.enumerated.item[0]; -static int via_independent_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->hp_mux, uinfo); -} - -static int via_independent_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int pinsel; - - /* use !! to translate conn sel 2 for VT1718S */ - pinsel = !!snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); - ucontrol->value.enumerated.item[0] = pinsel; - - return 0; -} - -static void activate_ctl(struct hda_codec *codec, const char *name, int active) -{ - struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); - if (ctl) { - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access |= active - ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); - } -} - -static hda_nid_t side_mute_channel(struct via_spec *spec) -{ - switch (spec->codec_type) { - case VT1708: return 0x1b; - case VT1709_10CH: return 0x29; - case VT1708B_8CH: /* fall thru */ - case VT1708S: return 0x27; - default: return 0; - } -} - -static int update_side_mute_status(struct hda_codec *codec) -{ - /* mute side channel */ - struct via_spec *spec = codec->spec; - unsigned int parm = spec->hp_independent_mode - ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; - hda_nid_t sw3 = side_mute_channel(spec); - - if (sw3) - snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm); - return 0; -} - -static int via_independent_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - unsigned int pinsel = ucontrol->value.enumerated.item[0]; - /* Get Independent Mode index of headphone pin widget */ - spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel - ? 1 : 0; - if (spec->codec_type == VT1718S) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); - else - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); - - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); - if (spec->multiout.hp_nid && spec->multiout.hp_nid - != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, - 0, 0, 0); - - update_side_mute_status(codec); - /* update HP volume/swtich active state */ - if (spec->codec_type == VT1708S - || spec->codec_type == VT1702 - || spec->codec_type == VT1718S - || spec->codec_type == VT1716S - || spec->codec_type == VT2002P - || spec->codec_type == VT1812) { - activate_ctl(codec, "Headphone Playback Volume", - spec->hp_independent_mode); - activate_ctl(codec, "Headphone Playback Switch", - spec->hp_independent_mode); - } - /* update jack power state */ - set_jack_power_state(codec); - return 0; -} - -static struct snd_kcontrol_new via_hp_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = via_independent_hp_info, - .get = via_independent_hp_get, - .put = via_independent_hp_put, - }, - { - .iface = NID_MAPPING, - .name = "Independent HP", - }, -}; - -static int via_hp_build(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - hda_nid_t nid; - int nums; - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - break; - case VT2002P: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } - - if (spec->codec_type != VT1708) { - nums = snd_hda_get_connections(codec, nid, - conn, HDA_MAX_CONNECTIONS); - if (nums <= 1) - return 0; - } - - knew = via_clone_control(spec, &via_hp_mixer[0]); - if (knew == NULL) - return -ENOMEM; - - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - knew->private_value = nid; - - knew = via_clone_control(spec, &via_hp_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = side_mute_channel(spec); - - return 0; -} - -static void notify_aa_path_ctls(struct hda_codec *codec) -{ - int i; - struct snd_ctl_elem_id id; - const char *labels[] = {"Mic", "Front Mic", "Line"}; - - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - for (i = 0; i < ARRAY_SIZE(labels); i++) { - sprintf(id.name, "%s Playback Volume", labels[i]); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); - } -} - -static void mute_aa_path(struct hda_codec *codec, int mute) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid_mixer; - int start_idx; - int end_idx; - int i; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708: - nid_mixer = 0x17; - start_idx = 2; - end_idx = 4; - break; - case VT1709_10CH: - case VT1709_6CH: - nid_mixer = 0x18; - start_idx = 2; - end_idx = 4; - break; - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - nid_mixer = 0x16; - start_idx = 2; - end_idx = 4; - break; - case VT1718S: - nid_mixer = 0x21; - start_idx = 1; - end_idx = 3; - break; - default: - return; - } - /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, - HDA_AMP_MUTE, val); - } -} -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) -{ - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - if (pin == cfg->inputs[i].pin) - return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; - } - return 0; -} - -static int via_smart51_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int via_smart51_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int on = 1; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* ignore FMic for independent HP */ - if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) - on = 0; - } - *ucontrol->value.integer.value = on; - return 0; -} - -static int via_smart51_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int out_in = *ucontrol->value.integer.value - ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int parm; - - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - continue; - if (cfg->inputs[i].type == AUTO_PIN_MIC && - spec->hp_independent_mode && spec->codec_type != VT1718S) - continue; /* don't retask FMic for independent HP */ - - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - parm |= out_in; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - parm); - if (out_in == AC_PINCTL_OUT_EN) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - if (spec->codec_type == VT1718S) { - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_UNMUTE); - } - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - if (spec->codec_type == VT1708S - || spec->codec_type == VT1716S) { - /* input = index 1 (AOW3) */ - snd_hda_codec_write( - codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, 1); - snd_hda_codec_amp_stereo( - codec, nid, HDA_OUTPUT, - 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); - } - } - } - spec->smart51_enabled = *ucontrol->value.integer.value; - set_jack_power_state(codec); + if (val == spec->no_pin_power_ctl) + return 0; + spec->no_pin_power_ctl = val; + set_widgets_power_state(codec); + analog_low_current_mode(codec); return 1; } -static struct snd_kcontrol_new via_smart51_mixer[2] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Smart 5.1", - .count = 1, - .info = via_smart51_info, - .get = via_smart51_get, - .put = via_smart51_put, - }, - { - .iface = NID_MAPPING, - .name = "Smart 5.1", - } -}; - -static int via_smart51_build(struct via_spec *spec) -{ - struct snd_kcontrol_new *knew; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; - - knew = via_clone_control(spec, &via_smart51_mixer[0]); - if (knew == NULL) - return -ENOMEM; - - for (i = 0; i < cfg->num_inputs; i++) { - nid = cfg->inputs[i].pin; - if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { - knew = via_clone_control(spec, &via_smart51_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = nid; - break; - } - } - - return 0; -} - -/* capture mixer elements */ -static struct snd_kcontrol_new vt1708_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), +static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Dynamic Power-Control", + .info = via_pin_power_ctl_info, + .get = via_pin_power_ctl_get, + .put = via_pin_power_ctl_put, }, - { } /* end */ + {} /* terminator */ }; -/* check AA path's mute statue */ -static int is_aa_path_mute(struct hda_codec *codec) + +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) { - int mute = 1; - hda_nid_t nid_mixer; - int start_idx; - int end_idx; - int i; struct via_spec *spec = codec->spec; - /* get nid of MW0 and start & end index */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - case VT1708S: - case VT1716S: - nid_mixer = 0x16; - start_idx = 2; - end_idx = 4; - break; - case VT1702: - nid_mixer = 0x1a; - start_idx = 1; - end_idx = 3; - break; - case VT1718S: - nid_mixer = 0x21; - start_idx = 1; - end_idx = 3; - break; - case VT2002P: - case VT1812: - nid_mixer = 0x21; - start_idx = 0; - end_idx = 2; - break; - default: - return 0; - } - /* check AA path's mute status */ - for (i = start_idx; i <= end_idx; i++) { - unsigned int con_list = snd_hda_codec_read( - codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); - int shift = 8 * (i % 4); - hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; - unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); - if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { - /* check mute status while the pin is connected */ - int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, - HDA_INPUT, i) >> 7; - int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, - HDA_INPUT, i) >> 7; - if (!mute_l || !mute_r) { - mute = 0; - break; - } + const struct hda_amp_list *p; + int ch, v; + + p = spec->gen.loopback.amplist; + if (!p) + return true; + for (; p->nid; p++) { + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; } } - return mute; + return true; } /* enter/exit analog low-current mode */ -static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +static void __analog_low_current_mode(struct hda_codec *codec, bool force) { struct via_spec *spec = codec->spec; - static int saved_stream_idle = 1; /* saved stream idle status */ - int enable = is_aa_path_mute(codec); - unsigned int verb = 0; - unsigned int parm = 0; + bool enable; + unsigned int verb, parm; - if (stream_idle == -1) /* stream status did not change */ - enable = enable && saved_stream_idle; - else { - enable = enable && stream_idle; - saved_stream_idle = stream_idle; - } + if (spec->no_pin_power_ctl) + enable = false; + else + enable = is_aa_path_mute(codec) && !spec->gen.active_streams; + if (enable == spec->alc_mode && !force) + return; + spec->alc_mode = enable; /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -1603,9 +412,15 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) break; case VT2002P: case VT1812: + case VT1802: verb = 0xf93; parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ break; + case VT1705CF: + case VT1808: + verb = 0xf82; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; default: return; /* other codecs are not supported */ } @@ -1613,887 +428,95 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb vt1708_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers (0x19 - 0x1b) - */ - /* set vol=0 to output mixers */ - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input MW0 to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } -}; - -static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - analog_low_current_mode(codec, idle); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static void playback_multi_pcm_prep_0(struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - hda_nid_t *nids = mout->dac_nids; - int chs = substream->runtime->channels; - int i; - - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { - if (chs == 2 && - snd_hda_is_supported_format(codec, mout->dig_out_nid, - format) && - !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { - mout->dig_out_used = HDA_DIG_ANALOG_DUP; - /* turn off SPDIF once; otherwise the IEC958 bits won't - * be updated */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & - ~AC_DIG1_ENABLE & 0xff); - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - stream_tag, 0, format); - /* turn on again (if needed) */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, mout->dig_out_nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); - } else { - mout->dig_out_used = 0; - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - } - } - mutex_unlock(&codec->spdif_mutex); - - /* front */ - snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, - 0, format); - - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] - && !spec->hp_independent_mode) - /* headphone out will just decode front left/right (stereo) */ - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, - 0, format); - - /* extra outputs copied from front */ - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); - - /* surrounds */ - for (i = 1; i < mout->num_dacs; i++) { - if (chs >= (i + 1) * 2) /* independent out */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - i * 2, format); - else /* copy front */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - 0, format); - } -} - -static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static void analog_low_current_mode(struct hda_codec *codec) { - struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - hda_nid_t *nids = mout->dac_nids; - - if (substream->number == 0) - playback_multi_pcm_prep_0(codec, stream_tag, format, - substream); - else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - stream_tag, 0, format); - } - vt1708_start_hp_work(spec); - return 0; + return __analog_low_current_mode(codec, false); } -static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - struct hda_multi_out *mout = &spec->multiout; - hda_nid_t *nids = mout->dac_nids; - int i; - - if (substream->number == 0) { - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); - - if (mout->hp_nid && !spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); - - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && - mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); - } else { - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, mout->hp_nid, - 0, 0, 0); - } - vt1708_stop_hp_work(spec); - return 0; -} - -/* - * Digital out - */ -static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); - return 0; -} - -/* - * Analog capture - */ -static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -static struct hda_pcm_stream vt1708_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x15, /* NID to query formats and rates */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new *knew; int err, i; - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } + if (spec->set_widgets_power_state) + spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) return err; } - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - err = snd_hda_add_nid(codec, kctl, 0, - knew->subdevice); - } - } - - /* init power states */ - set_jack_power_state(codec); - analog_low_current_mode(codec, 1); - - via_free_kctls(codec); /* no longer needed */ return 0; } -static int via_build_pcms(struct hda_codec *codec) +static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { - struct via_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = spec->stream_name_analog; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_analog_playback); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - codec->num_pcms++; - info++; - info->name = spec->stream_name_digital; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *(spec->stream_digital_playback); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *(spec->stream_digital_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->dig_in_nid; - } - } - - return 0; + analog_low_current_mode(codec); + vt1708_update_hp_work(codec); } static void via_free(struct hda_codec *codec) { - struct via_spec *spec = codec->spec; - - if (!spec) - return; - - via_free_kctls(codec); - vt1708_stop_hp_work(spec); - kfree(codec->spec); -} - -/* mute internal speaker if HP is plugged */ -static void via_hp_automute(struct hda_codec *codec) -{ - unsigned int present = 0; - struct via_spec *spec = codec->spec; - - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; - /* auto mute */ - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Front Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); - } + vt1708_stop_hp_work(codec); + snd_hda_gen_free(codec); } -/* mute mono out if HP or Line out is plugged */ -static void via_mono_automute(struct hda_codec *codec) +#ifdef CONFIG_PM +static int via_suspend(struct hda_codec *codec) { - unsigned int hp_present, lineout_present; struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(codec); - if (spec->codec_type != VT1716S) - return; - - lineout_present = snd_hda_jack_detect(codec, - spec->autocfg.line_out_pins[0]); - - /* Mute Mono Out if Line Out is plugged */ - if (lineout_present) { - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); - return; - } - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) - snd_hda_codec_amp_stereo( - codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, - hp_present ? HDA_AMP_MUTE : 0); -} - -static void via_gpio_control(struct hda_codec *codec) -{ - unsigned int gpio_data; - unsigned int vol_counter; - unsigned int vol; - unsigned int master_vol; - - struct via_spec *spec = codec->spec; - - gpio_data = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & 0x03; - - vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, - 0xF84, 0) & 0x3F0000) >> 16; - - vol = vol_counter & 0x1F; - master_vol = snd_hda_codec_read(codec, 0x1A, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - - if (gpio_data == 0x02) { - /* unmute line out */ - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - - if (vol_counter & 0x20) { - /* decrease volume */ - if (vol > master_vol) - vol = master_vol; - snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, - 0, HDA_AMP_VOLMASK, - master_vol-vol); - } else { - /* increase volume */ - snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, - HDA_AMP_VOLMASK, - ((master_vol+vol) > 0x2A) ? 0x2A : - (master_vol+vol)); - } - } else if (!(gpio_data & 0x02)) { - /* mute line out */ - snd_hda_codec_amp_stereo(codec, - spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - HDA_AMP_MUTE); - } -} - -/* mute Internal-Speaker if HP is plugged */ -static void via_speaker_automute(struct hda_codec *codec) -{ - unsigned int hp_present; - struct via_spec *spec = codec->spec; - - if (spec->codec_type != VT2002P && spec->codec_type != VT1812) - return; - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (!spec->hp_independent_mode) { - struct snd_ctl_elem_id id; - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); - /* notify change */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Speaker Playback Switch"); - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, - &id); - } -} - -/* mute line-out and internal speaker if HP is plugged */ -static void via_hp_bind_automute(struct hda_codec *codec) -{ - /* use long instead of int below just to avoid an internal compiler - * error with gcc 4.0.x - */ - unsigned long hp_present, present = 0; - struct via_spec *spec = codec->spec; - int i; - - if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) - return; - - hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); - - if (!spec->hp_independent_mode) { - /* Mute Line-Outs */ - for (i = 0; i < spec->autocfg.line_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.line_out_pins[i], - HDA_OUTPUT, 0, - HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); - if (hp_present) - present = hp_present; - } - /* Speakers */ - for (i = 0; i < spec->autocfg.speaker_outs; i++) - snd_hda_codec_amp_stereo( - codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - - -/* unsolicited event for jack sensing */ -static void via_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - if (res & VIA_HP_EVENT) - via_hp_automute(codec); - if (res & VIA_GPIO_EVENT) - via_gpio_control(codec); - if (res & VIA_JACK_EVENT) - set_jack_power_state(codec); - if (res & VIA_MONO_EVENT) - via_mono_automute(codec); - if (res & VIA_SPEAKER_EVENT) - via_speaker_automute(codec); - if (res & VIA_BIND_HP_EVENT) - via_hp_bind_automute(codec); -} - -static int via_init(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - for (i = 0; i < spec->num_iverbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - - /* Lydia Add for EAPD enable */ - if (!spec->dig_in_nid) { /* No Digital In connection */ - if (spec->dig_in_pin) { - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); - snd_hda_codec_write(codec, spec->dig_in_pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); - } - } else /* enable SPDIF-input pin */ - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); - - /* assign slave outs */ - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; + /* Fix pop noise on headphones */ + if (spec->codec_type == VT1802) + snd_hda_shutup_pins(codec); return 0; } - -#ifdef SND_HDA_NEEDS_RESUME -static int via_suspend(struct hda_codec *codec, pm_message_t state) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); - return 0; -} #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#ifdef CONFIG_PM static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); + set_widgets_power_state(codec); + analog_low_current_mode(codec); + vt1708_update_hp_work(codec); + return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); } #endif /* */ -static struct hda_codec_ops via_patch_ops = { + +static int via_init(struct hda_codec *codec); + +static const struct hda_codec_ops via_patch_ops = { .build_controls = via_build_controls, - .build_pcms = via_build_pcms, + .build_pcms = snd_hda_gen_build_pcms, .init = via_init, .free = via_free, -#ifdef SND_HDA_NEEDS_RESUME + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM .suspend = via_suspend, -#endif -#ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = via_check_power_status, #endif }; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->multiout.dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - spec->multiout.dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->multiout.dac_nids[i] = 0x13; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static void create_hp_imux(struct via_spec *spec) -{ - int i; - struct hda_input_mux *imux = &spec->private_imux[1]; - static const char * const texts[] = { "OFF", "ON", NULL}; - - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); - - spec->hp_mux = &spec->private_imux[1]; -} - -static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - hda_nid_t cap_nid, - hda_nid_t pin_idxs[], int num_idxs) -{ - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, type, type_idx = 0; - - /* for internal loopback recording select */ - for (idx = 0; idx < num_idxs; idx++) { - if (pin_idxs[idx] == 0xff) { - snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); - break; - } - } - - for (i = 0; i < cfg->num_inputs; i++) { - const char *label; - type = cfg->inputs[i].type; - for (idx = 0; idx < num_idxs; idx++) - if (pin_idxs[idx] == cfg->inputs[i].pin) - break; - if (idx >= num_idxs) - continue; - if (i > 0 && type == cfg->inputs[i - 1].type) - type_idx++; - else - type_idx = 0; - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->codec_type == VT1708S || - spec->codec_type == VT1702 || - spec->codec_type == VT1716S) - err = via_new_analog_input(spec, label, type_idx, - idx+1, cap_nid); - else - err = via_new_analog_input(spec, label, type_idx, - idx, cap_nid); - if (err < 0) - return err; - snd_hda_add_imux_item(imux, label, idx, NULL); - } - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1708_loopbacks[] = { - { 0x17, HDA_INPUT, 1 }, - { 0x17, HDA_INPUT, 2 }, - { 0x17, HDA_INPUT, 3 }, - { 0x17, HDA_INPUT, 4 }, - { } /* end */ +static const struct hda_verb vt1708_init_verbs[] = { + /* power down jack detect function */ + {0x1, 0xf81, 0x1}, + { } }; -#endif - static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { unsigned int def_conf; @@ -2511,7 +534,7 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) return; } -static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2519,599 +542,182 @@ static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = - !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); - ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; return 0; } -static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, +static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - int change; + int val; if (spec->codec_type != VT1708) return 0; - spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; - change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) - == !spec->vt1708_jack_detectect; - if (spec->vt1708_jack_detectect) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - return change; + val = !!ucontrol->value.integer.value[0]; + if (spec->vt1708_jack_detect == val) + return 0; + spec->vt1708_jack_detect = val; + vt1708_update_hp_work(codec); + return 1; } -static struct snd_kcontrol_new vt1708_jack_detectect[] = { +static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Jack Detect", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = vt1708_jack_detectect_get, - .put = vt1708_jack_detectect_put, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detect_get, + .put = vt1708_jack_detect_put, }, - {} /* end */ + {} /* terminator */ }; -static int vt1708_parse_auto_config(struct hda_codec *codec) +static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - struct via_spec *spec = codec->spec; - int err; - - /* Add HP and CD pin config connect bit re-config action */ - vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); - vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - /* add jack detect on/off control */ - err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; - spec->dig_in_pin = VT1708_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - return 1; + set_widgets_power_state(codec); + snd_hda_gen_hp_automute(codec, tbl); } -/* init callback for auto-configuration model -- overriding the default init */ -static int via_auto_init(struct hda_codec *codec) +static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - struct via_spec *spec = codec->spec; - - via_init(codec); - via_auto_init_multi_out(codec); - via_auto_init_hp_out(codec); - via_auto_init_analog_input(codec); - if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { - via_hp_bind_automute(codec); - } else { - via_hp_automute(codec); - via_speaker_automute(codec); - } - - return 0; + set_widgets_power_state(codec); + snd_hda_gen_line_automute(codec, tbl); } -static void vt1708_update_hp_jack_state(struct work_struct *work) +static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - struct via_spec *spec = container_of(work, struct via_spec, - vt1708_hp_work.work); - if (spec->codec_type != VT1708) - return; - /* if jack state toggled */ - if (spec->vt1708_hp_present - != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { - spec->vt1708_hp_present ^= 1; - via_hp_automute(spec->codec); - } - vt1708_start_hp_work(spec); + set_widgets_power_state(codec); } -static int get_mux_nids(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid, conn[8]; - unsigned int type; - int i, n; - - for (i = 0; i < spec->num_adc_nids; i++) { - nid = spec->adc_nids[i]; - while (nid) { - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN) - break; - n = snd_hda_get_connections(codec, nid, conn, - ARRAY_SIZE(conn)); - if (n <= 0) - break; - if (n > 1) { - spec->mux_nids[i] = nid; - break; - } - nid = conn[0]; - } - } - return 0; -} +#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) -static int patch_vt1708(struct hda_codec *codec) +static void via_set_jack_unsol_events(struct hda_codec *codec) { - struct via_spec *spec; - int err; + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t pin; + int i; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + spec->gen.hp_automute_hook = via_hp_automute; + if (cfg->speaker_pins[0]) + spec->gen.line_automute_hook = via_line_automute; - /* automatic parse from the BIOS config */ - err = vt1708_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); + for (i = 0; i < cfg->line_outs; i++) { + pin = cfg->line_out_pins[i]; + if (pin && !snd_hda_jack_tbl_get(codec, pin) && + is_jack_detectable(codec, pin)) + snd_hda_jack_detect_enable_callback(codec, pin, + VIA_JACK_EVENT, + via_jack_powerstate_event); } - - spec->stream_name_analog = "VT1708 Analog"; - spec->stream_analog_playback = &vt1708_pcm_analog_playback; - /* disable 32bit format on VT1708 */ - if (codec->vendor_id == 0x11061708) - spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; - spec->stream_analog_capture = &vt1708_pcm_analog_capture; - - spec->stream_name_digital = "VT1708 Digital"; - spec->stream_digital_playback = &vt1708_pcm_digital_playback; - spec->stream_digital_capture = &vt1708_pcm_digital_capture; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708_capture_mixer; - spec->num_mixers++; + for (i = 0; i < cfg->num_inputs; i++) { + pin = cfg->line_out_pins[i]; + if (pin && !snd_hda_jack_tbl_get(codec, pin) && + is_jack_detectable(codec, pin)) + snd_hda_jack_detect_enable_callback(codec, pin, + VIA_JACK_EVENT, + via_jack_powerstate_event); } - - codec->patch_ops = via_patch_ops; - - codec->patch_ops.init = via_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708_loopbacks; -#endif - INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); - return 0; } -/* capture mixer elements */ -static struct snd_kcontrol_new vt1709_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - { } -}; - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb vt1709_10ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } +static const struct badness_table via_main_out_badness = { + .no_primary_dac = 0x10000, + .no_dac = 0x4000, + .shared_primary = 0x10000, + .shared_surr = 0x20, + .shared_clfe = 0x20, + .shared_surr_main = 0x20, }; - -static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 10, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, +static const struct badness_table via_extra_out_badness = { + .no_primary_dac = 0x4000, + .no_dac = 0x4000, + .shared_primary = 0x12, + .shared_surr = 0x20, + .shared_clfe = 0x20, + .shared_surr_main = 0x10, }; -static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - }, -}; - -static struct hda_pcm_stream vt1709_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x14, /* NID to query formats and rates */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1709_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close - }, -}; - -static struct hda_pcm_stream vt1709_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int vt1709_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - if (cfg->line_outs == 4) /* 10 channels */ - spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ - else if (cfg->line_outs == 3) /* 6 channels */ - spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ - - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_outs == 4) { /* 10 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->multiout.dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW3 */ - spec->multiout.dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - /* AOW1 */ - spec->multiout.dac_nids[i] = 0x27; - break; - default: - break; - } - } - } - spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ - - } else if (cfg->line_outs == 3) { /* 6 channels */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - /* AOW0 */ - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - /* AOW2 */ - spec->multiout.dac_nids[i] = 0x12; - break; - case AUTO_SEQ_SURROUND: - /* AOW1 */ - spec->multiout.dac_nids[i] = 0x11; - break; - default: - break; - } - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static int via_parse_auto_config(struct hda_codec *codec) { - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; - int i, err; + struct via_spec *spec = codec->spec; + int err; - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; + spec->gen.main_out_badness = &via_main_out_badness; + spec->gen.extra_out_badness = &via_extra_out_badness; - if (!nid) - continue; + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* ADD control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SURROUND) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_SIDE) { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + via_set_jack_unsol_events(codec); return 0; } -static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +static int via_init(struct hda_codec *codec) { - int err; + struct via_spec *spec = codec->spec; + int i; - if (!pin) - return 0; + for (i = 0; i < spec->num_iverbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); - if (spec->multiout.num_dacs == 5) /* 10 channels */ - spec->multiout.hp_nid = VT1709_HP_DAC_NID; - else if (spec->multiout.num_dacs == 3) /* 6 channels */ - spec->multiout.hp_nid = 0; - spec->hp_independent_mode_index = 1; + /* init power states */ + set_widgets_power_state(codec); + __analog_low_current_mode(codec, true); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; + snd_hda_gen_init(codec); + + vt1708_update_hp_work(codec); return 0; } -/* create playback/capture controls for input pins */ -static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +static int vt1708_build_controls(struct hda_codec *codec) { - static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, - ARRAY_SIZE(pin_idxs)); + /* In order not to create "Phantom Jack" controls, + temporary enable jackpoll */ + int err; + int old_interval = codec->jackpoll_interval; + codec->jackpoll_interval = msecs_to_jiffies(100); + err = via_build_controls(codec); + codec->jackpoll_interval = old_interval; + return err; } -static int vt1709_parse_auto_config(struct hda_codec *codec) +static int vt1708_build_pcms(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + int i, err; - err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) + err = snd_hda_gen_build_pcms(codec); + if (err < 0 || codec->vendor_id != 0x11061708) return err; - err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; - spec->dig_in_pin = VT1709_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1709_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); + /* We got noisy outputs on the right channel on VT1708 when + * 24bit samples are used. Until any workaround is found, + * disable the 24bit format, so far. + */ + for (i = 0; i < codec->num_pcms; i++) { + struct hda_pcm *info = &spec->gen.pcm_rec[i]; + if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || + info->pcm_type != HDA_PCM_TYPE_AUDIO) + continue; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = + SNDRV_PCM_FMTBIT_S16_LE; + } - via_smart51_build(spec); - return 1; + return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1709_loopbacks[] = { - { 0x18, HDA_INPUT, 1 }, - { 0x18, HDA_INPUT, 2 }, - { 0x18, HDA_INPUT, 3 }, - { 0x18, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - -static int patch_vt1709_10ch(struct hda_codec *codec) +static int patch_vt1708(struct hda_codec *codec) { struct via_spec *spec; int err; @@ -3121,89 +727,45 @@ static int patch_vt1709_10ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - err = vt1709_parse_auto_config(codec); + spec->gen.mixer_nid = 0x17; + + /* set jackpoll_interval while parsing the codec */ + codec->jackpoll_interval = msecs_to_jiffies(100); + spec->vt1708_jack_detect = 1; + + /* don't support the input jack switching due to lack of unsol event */ + /* (it may work with polling, though, but it needs testing) */ + spec->gen.suppress_auto_mic = 1; + /* Some machines show the broken speaker mute */ + spec->gen.auto_mute_via_amp = 1; + + /* Add HP and CD pin config connect bit re-config action */ + vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); + vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); + + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - - spec->stream_name_analog = "VT1709 Analog"; - spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_name_digital = "VT1709 Digital"; - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; + /* add jack detect on/off control */ + spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; + codec->patch_ops.build_controls = vt1708_build_controls; + codec->patch_ops.build_pcms = vt1708_build_pcms; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif + /* clear jackpoll_interval again; it's set dynamically */ + codec->jackpoll_interval = 0; return 0; } -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb vt1709_6ch_volume_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output selector (0x1a, 0x1b, 0x29) - */ - /* set vol=0 to output mixers */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* - * Unmute PW3 and PW4 - */ - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Set input of PW4 as MW0 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - { } -}; - -static int patch_vt1709_6ch(struct hda_codec *codec) +static int patch_vt1709(struct hda_codec *codec) { struct via_spec *spec; int err; @@ -3213,583 +775,123 @@ static int patch_vt1709_6ch(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - err = vt1709_parse_auto_config(codec); + spec->gen.mixer_nid = 0x18; + + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration. " - "Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; - - spec->stream_name_analog = "VT1709 Analog"; - spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1709_pcm_analog_capture; - - spec->stream_name_digital = "VT1709 Digital"; - spec->stream_digital_playback = &vt1709_pcm_digital_playback; - spec->stream_digital_capture = &vt1709_pcm_digital_capture; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1709_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1709_capture_mixer; - spec->num_mixers++; } codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1709_loopbacks; -#endif return 0; } -/* capture mixer elements */ -static struct snd_kcontrol_new vt1708B_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - -static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* - * Set up output mixers - */ - /* set vol=0 to output mixers */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* PW10 Input enable */ - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - { } -}; - -static struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static int via_pcm_open_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - int idle = substream->pstr->substream_opened == 1 - && substream->ref_count == 0; - - analog_low_current_mode(codec, idle); - return 0; -} - -static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 4, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708B_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1708B_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1708B_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->multiout.dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->multiout.dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->multiout.dac_nids[i] = 0x25; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; - hda_nid_t nid, nid_vol = 0; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* add control to PW3 */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1708B_parse_auto_config(struct hda_codec *codec) +static void set_widgets_power_state_vt1708B(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; - spec->dig_in_pin = VT1708B_DIGIN_PIN; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = VT1708B_DIGIN_NID; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; + int imux_is_smixer; + unsigned int parm; + int is_8ch = 0; + if ((spec->codec_type != VT1708B_4CH) && + (codec->vendor_id != 0x11064397)) + is_8ch = 1; + + /* SW0 (17h) = stereo mixer */ + imux_is_smixer = + (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) + == ((spec->codec_type == VT1708S) ? 5 : 0)); + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW 0/1 (13h/14h) */ + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x13, parm); + update_power_state(codec, 0x14, parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1b, &parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x11, parm); - spec->input_mux = &spec->private_imux[0]; + /* PW6 (22h), SW2 (26h), AOW2 (24h) */ + if (is_8ch) { + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x22, &parm); + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1a, &parm); + update_power_state(codec, 0x26, parm); + update_power_state(codec, 0x24, parm); + } else if (codec->vendor_id == 0x11064397) { + /* PW7(23h), SW2(27h), AOW2(25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x23, &parm); + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1a, &parm); + update_power_state(codec, 0x27, parm); + update_power_state(codec, 0x25, parm); + } - if (spec->hp_mux) - via_hp_build(codec); + /* PW 3/4/7 (1ch/1dh/23h) */ + parm = AC_PWRST_D3; + /* force to D0 for internal Speaker */ + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + if (is_8ch) + set_pin_power_state(codec, 0x23, &parm); - via_smart51_build(spec); - return 1; + /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ + update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, parm); + if (is_8ch) { + update_power_state(codec, 0x25, parm); + update_power_state(codec, 0x27, parm); + } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) + update_power_state(codec, 0x25, parm); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1708B_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B_8ch(struct hda_codec *codec) +static int patch_vt1708B(struct hda_codec *codec) { struct via_spec *spec; int err; if (get_codec_type(codec) == VT1708BCE) return patch_vt1708S(codec); - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - - spec->stream_name_analog = "VT1708B Analog"; - spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_name_digital = "VT1708B Digital"; - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; - } - - codec->patch_ops = via_patch_ops; - - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif - - return 0; -} - -static int patch_vt1708B_4ch(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; /* create a codec specific record */ spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; + spec->gen.mixer_nid = 0x16; + /* automatic parse from the BIOS config */ - err = vt1708B_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; - - spec->stream_name_analog = "VT1708B Analog"; - spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; - spec->stream_analog_capture = &vt1708B_pcm_analog_capture; - - spec->stream_name_digital = "VT1708B Digital"; - spec->stream_digital_playback = &vt1708B_pcm_digital_playback; - spec->stream_digital_capture = &vt1708B_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708B_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; - spec->num_mixers++; } codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708B_loopbacks; -#endif + spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } /* Patch for VT1708S */ - -/* capture mixer elements */ -static struct snd_kcontrol_new vt1708S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct hda_verb vt1708S_volume_init_verbs[] = { - /* Unmute ADC0-1 and set the default input to mic-in */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the - * analog-loopback mixer widget */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* PW9, PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, /* don't bybass mixer */ @@ -3797,314 +899,11 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { { } }; -static struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static struct hda_pcm_stream vt1708S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 8, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1708S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1708S_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->multiout.dac_nids[i] = 0x24; - break; - case AUTO_SEQ_SURROUND: - spec->multiout.dac_nids[i] = 0x11; - break; - case AUTO_SEQ_SIDE: - spec->multiout.dac_nids[i] = 0x25; - break; - } - } - } - - /* for Smart 5.1, line/mic inputs double as output pins */ - if (cfg->line_outs == 1) { - spec->multiout.num_dacs = 3; - spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11; - spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24; - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; - hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; - - /* for Smart 5.1, there are always at least six channels */ - if (!nid && i > AUTO_SEQ_CENLFE) - continue; - - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, - 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, - 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - - return 0; -} - -static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -/* fill out digital output widgets; one for master and one for slave outputs */ -static void fill_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t nid; - int conn; - - nid = spec->autocfg.dig_out_pins[i]; - if (!nid) - continue; - conn = snd_hda_get_connections(codec, nid, &nid, 1); - if (conn < 1) - continue; - if (!spec->multiout.dig_out_nid) - spec->multiout.dig_out_nid = nid; - else { - spec->slave_dig_outs[0] = nid; - break; /* at most two dig outs */ - } - } -} - -static int vt1708S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1708S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { + snd_hda_override_wcaps(codec, pin, + get_wcaps(codec, pin) | AC_WCAP_IN_AMP); snd_hda_override_amp_caps(codec, pin, HDA_INPUT, (offset << AC_AMPCAP_OFFSET_SHIFT) | (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | @@ -4122,49 +921,9 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - /* automatic parse from the BIOS config */ - err = vt1708S_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); - } - - spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - - if (codec->vendor_id == 0x11060440) - spec->stream_name_analog = "VT1818S Analog"; - else - spec->stream_name_analog = "VT1708S Analog"; - spec->stream_analog_playback = &vt1708S_pcm_analog_playback; - spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - - if (codec->vendor_id == 0x11060440) - spec->stream_name_digital = "VT1818S Digital"; - else - spec->stream_name_digital = "VT1708S Digital"; - spec->stream_digital_playback = &vt1708S_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1708S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; - spec->num_mixers++; - } - - codec->patch_ops = via_patch_ops; - - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1708S_loopbacks; -#endif + spec->gen.mixer_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { @@ -4173,68 +932,34 @@ static int patch_vt1708S(struct hda_codec *codec) snprintf(codec->bus->card->mixername, sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); - spec->stream_name_analog = "VT1708BCE Analog"; - spec->stream_name_digital = "VT1708BCE Digital"; } - /* correct names for VT1818S */ - if (codec->vendor_id == 0x11060440) { - spec->stream_name_analog = "VT1818S Analog"; - spec->stream_name_digital = "VT1818S Digital"; + /* correct names for VT1705 */ + if (codec->vendor_id == 0x11064397) { + kfree(codec->chip_name); + codec->chip_name = kstrdup("VT1705", GFP_KERNEL); + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); } - return 0; -} -/* Patch for VT1702 */ + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } -/* capture mixer elements */ -static struct snd_kcontrol_new vt1702_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; + spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; -static struct hda_verb vt1702_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + codec->patch_ops = via_patch_ops; + + spec->set_widgets_power_state = set_widgets_power_state_vt1708B; + return 0; +} +/* Patch for VT1702 */ - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* Setup default input of PW4 to MW0 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* PW6 PW7 Output enable */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1702_init_verbs[] = { /* mixer enable */ {0x1, 0xF88, 0x3}, /* GPIO 0~2 */ @@ -4242,588 +967,180 @@ static struct hda_verb vt1702_volume_init_verbs[] = { { } }; -static struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static struct hda_pcm_stream vt1702_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1702_pcm_analog_capture = { - .substreams = 3, - .channels_min = 2, - .channels_max = 2, - .nid = 0x12, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close - }, -}; - -static struct hda_pcm_stream vt1702_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1702_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static void set_widgets_power_state_vt1702(struct hda_codec *codec) { - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - - if (cfg->line_out_pins[0]) { - /* config dac list */ - spec->multiout.dac_nids[0] = 0x10; - } - - return 0; + int imux_is_smixer = + snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + unsigned int parm; + /* inputs */ + /* PW 1/2/5 (14h/15h/18h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x14, &parm); + set_pin_power_state(codec, 0x15, &parm); + set_pin_power_state(codec, 0x18, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ + /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ + update_power_state(codec, 0x13, parm); + update_power_state(codec, 0x12, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x20, parm); + + /* outputs */ + /* PW 3/4 (16h/17h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x17, &parm); + set_pin_power_state(codec, 0x16, &parm); + /* MW0 (1ah), AOW 0/1 (10h/1dh) */ + update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x1d, parm); } -/* add playback controls from the parsed DAC table */ -static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static int patch_vt1702(struct hda_codec *codec) { + struct via_spec *spec; int err; - if (!cfg->line_out_pins[0]) - return -1; - - /* add control to mixer index 0 */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - /* Front */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - return 0; -} - -static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err, i; - struct hda_input_mux *imux; - static const char * const texts[] = { "ON", "OFF", NULL}; - if (!pin) - return 0; - spec->multiout.hp_nid = 0x1D; - spec->hp_independent_mode_index = 0; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - imux = &spec->private_imux[1]; - - /* for hp mode select */ - for (i = 0; texts[i]; i++) - snd_hda_add_imux_item(imux, texts[i], i, NULL); - - spec->hp_mux = &spec->private_imux[1]; - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1702_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; + /* create a codec specific record */ + spec = via_new_spec(codec); + if (spec == NULL) + return -ENOMEM; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ + spec->gen.mixer_nid = 0x1a; - err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; /* limit AA path volume to 0 dB */ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, (0x17 << AC_AMPCAP_OFFSET_SHIFT) | (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | (1 << AC_AMPCAP_MUTE_SHIFT)); - err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1702_loopbacks[] = { - { 0x1A, HDA_INPUT, 1 }, - { 0x1A, HDA_INPUT, 2 }, - { 0x1A, HDA_INPUT, 3 }, - { 0x1A, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - -static int patch_vt1702(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; /* automatic parse from the BIOS config */ - err = vt1702_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; - - spec->stream_name_analog = "VT1702 Analog"; - spec->stream_analog_playback = &vt1702_pcm_analog_playback; - spec->stream_analog_capture = &vt1702_pcm_analog_capture; - - spec->stream_name_digital = "VT1702 Digital"; - spec->stream_digital_playback = &vt1702_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1702_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); - get_mux_nids(codec); - spec->mixers[spec->num_mixers] = vt1702_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1702_loopbacks; -#endif - + spec->set_widgets_power_state = set_widgets_power_state_vt1702; return 0; } /* Patch for VT1718S */ -/* capture mixer elements */ -static struct snd_kcontrol_new vt1718S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct hda_verb vt1718S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - - /* Setup default input of Front HP to MW9 */ - {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* PW9 PW10 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - /* PW11 Input enable */ - {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, +static const struct hda_verb vt1718S_init_verbs[] = { + /* Enable MW0 adjust Gain 5 */ + {0x1, 0xfb2, 0x10}, /* Enable Boost Volume backdoor */ {0x1, 0xf88, 0x8}, - /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Unmute MW4's index 0 */ - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -static struct hda_verb vt1718S_uniwill_init_verbs[] = { - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; -static struct hda_pcm_stream vt1718S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 10, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1718S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1718S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream vt1718S_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static void set_widgets_power_state_vt1718S(struct hda_codec *codec) { - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 4; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->multiout.dac_nids[i] = 0x8; - break; - case AUTO_SEQ_CENLFE: - spec->multiout.dac_nids[i] = 0xa; - break; - case AUTO_SEQ_SURROUND: - spec->multiout.dac_nids[i] = 0x9; - break; - case AUTO_SEQ_SIDE: - spec->multiout.dac_nids[i] = 0xb; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", "C/LFE", "Side" - }; - hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; - hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; - hda_nid_t nid, nid_vol, nid_mute = 0; - int i, err; + struct via_spec *spec = codec->spec; + int imux_is_smixer; + unsigned int parm, parm2; + /* MUX6 (1eh) = stereo mixer */ + imux_is_smixer = + snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); + + /* outputs */ + /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x27, &parm); + update_power_state(codec, 0x1a, parm); + parm2 = parm; /* for pin 0x0b */ + + /* PW2 (26h), AOW2 (ah) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x2b, &parm); + update_power_state(codec, 0xa, parm); - for (i = 0; i <= AUTO_SEQ_SIDE; i++) { - nid = cfg->line_out_pins[i]; + /* PW0 (24h), AOW0 (8h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ + set_pin_power_state(codec, 0x28, &parm); + update_power_state(codec, 0x8, parm); + if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + parm = parm2; + update_power_state(codec, 0xb, parm); + /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ + update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); + + /* PW1 (25h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x2a, &parm); + update_power_state(codec, 0x9, parm); - if (!nid) - continue; - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - /* Center/LFE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - /* Front */ - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } + if (spec->gen.indep_hp_enabled) { + /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + update_power_state(codec, 0x1b, parm); + update_power_state(codec, 0x34, parm); + update_power_state(codec, 0xc, parm); } - return 0; } -static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +/* Add a connection to the primary DAC from AA-mixer for some codecs + * This isn't listed from the raw info, but the chip has a secret connection. + */ +static int add_secret_dac_path(struct hda_codec *codec) { - int err; + struct via_spec *spec = codec->spec; + int i, nums; + hda_nid_t conn[8]; + hda_nid_t nid; - if (!pin) + if (!spec->gen.mixer_nid) return 0; + nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, + ARRAY_SIZE(conn) - 1); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) + return 0; + } - spec->multiout.hp_nid = 0xc; /* AOW4 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); + /* find the primary DAC and add to the connection list */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int caps = get_wcaps(codec, nid); + if (get_wcaps_type(caps) == AC_WID_AUD_OUT && + !(caps & AC_WCAP_DIGITAL)) { + conn[nums++] = nid; + return snd_hda_override_conn_list(codec, + spec->gen.mixer_nid, + nums, conn); + } + } return 0; } -/* create playback/capture controls for input pins */ -static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1718S_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - - if (err < 0) - return err; - err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) - spec->dig_in_nid = 0x13; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux[0]; - - if (spec->hp_mux) - via_hp_build(codec); - - via_smart51_build(spec); - - return 1; -} - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1718S_loopbacks[] = { - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { 0x21, HDA_INPUT, 3 }, - { 0x21, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif static int patch_vt1718S(struct hda_codec *codec) { @@ -4835,56 +1152,23 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->gen.mixer_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); + /* automatic parse from the BIOS config */ - err = vt1718S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - - if (codec->vendor_id == 0x11060441) - spec->stream_name_analog = "VT2020 Analog"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_analog = "VT1828S Analog"; - else - spec->stream_name_analog = "VT1718S Analog"; - spec->stream_analog_playback = &vt1718S_pcm_analog_playback; - spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - - if (codec->vendor_id == 0x11060441) - spec->stream_name_digital = "VT2020 Digital"; - else if (codec->vendor_id == 0x11064441) - spec->stream_name_digital = "VT1828S Digital"; - else - spec->stream_name_digital = "VT1718S Digital"; - spec->stream_digital_playback = &vt1718S_pcm_digital_playback; - if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) - spec->stream_digital_capture = &vt1718S_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1718S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1718S_loopbacks; -#endif + spec->set_widgets_power_state = set_widgets_power_state_vt1718S; return 0; } @@ -4925,32 +1209,11 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_CONNECT_SEL, index); spec->dmic_enabled = index; - set_jack_power_state(codec); - + set_widgets_power_state(codec); return 1; } -/* capture mixer elements */ -static struct snd_kcontrol_new vt1716S_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .count = 1, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { +static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -4966,50 +1229,12 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { /* mono-out mixer elements */ -static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { +static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), { } /* end */ }; -static struct hda_verb vt1716S_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Stereo Mixer = 5 */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, - - /* Setup default input of PW4 to MW0 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, - - /* Setup default input of SW1 as MW0 */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* Setup default input of SW4 as AOW0 */ - {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, - - /* PW9 PW10 Output enable */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - - /* Unmute SW1, PW12 */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* PW12 Output enable */ - {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +static const struct hda_verb vt1716S_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xf8a, 0x80}, /* don't bybass mixer */ @@ -5019,272 +1244,95 @@ static struct hda_verb vt1716S_volume_init_verbs[] = { { } }; - -static struct hda_verb vt1716S_uniwill_init_verbs[] = { - {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, - {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static struct hda_pcm_stream vt1716S_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 6, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1716S_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x13, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1716S_pcm_digital_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ int i; - hda_nid_t nid; - - spec->multiout.num_dacs = cfg->line_outs; - - spec->multiout.dac_nids = spec->private_dac_nids; - - for (i = 0; i < 3; i++) { - nid = cfg->line_out_pins[i]; - if (nid) { - /* config dac list */ - switch (i) { - case AUTO_SEQ_FRONT: - spec->multiout.dac_nids[i] = 0x10; - break; - case AUTO_SEQ_CENLFE: - spec->multiout.dac_nids[i] = 0x25; - break; - case AUTO_SEQ_SURROUND: - spec->multiout.dac_nids[i] = 0x11; - break; - } - } - } - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[3] = { - "Front", "Surround", "C/LFE" - }; - hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; - hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; - hda_nid_t nid, nid_vol, nid_mute; - int i, err; - - for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { - nid = cfg->line_out_pins[i]; - - if (!nid) - continue; - - nid_vol = nid_vols[i]; - nid_mute = nid_mutes[i]; - - if (i == AUTO_SEQ_CENLFE) { - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (i == AUTO_SEQ_FRONT) { - - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = via_add_control( - spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x25; /* AOW3 */ - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} - -/* create playback/capture controls for input pins */ -static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; - return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, - ARRAY_SIZE(pin_idxs)); -} - -static int vt1716S_parse_auto_config(struct hda_codec *codec) +static void set_widgets_power_state_vt1716S(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); + int imux_is_smixer; + unsigned int parm; + unsigned int mono_out, present; + /* SW0 (17h) = stereo mixer */ + imux_is_smixer = + (snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_CONNECT_SEL, 0x00) == 5); + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW0(13h) */ + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x13, parm); + + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1e, &parm); + /* PW11 (22h) */ + if (spec->dmic_enabled) + set_pin_power_state(codec, 0x22, &parm); + else + update_power_state(codec, 0x22, AC_PWRST_D3); + + /* SW2(26h), AIW1(14h) */ + update_power_state(codec, 0x26, parm); + update_power_state(codec, 0x14, parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + /* Smart 5.1 PW2(1bh) */ + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1b, &parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x11, parm); + + /* PW7 (23h), SW3 (27h), AOW3 (25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x23, &parm); + /* Smart 5.1 PW1(1ah) */ + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1a, &parm); + update_power_state(codec, 0x27, parm); - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; + /* Smart 5.1 PW5(1eh) */ + if (smart51_enabled(codec)) + set_pin_power_state(codec, 0x1e, &parm); + update_power_state(codec, 0x25, parm); - spec->input_mux = &spec->private_imux[0]; + /* Mono out */ + /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ + present = snd_hda_jack_detect(codec, 0x1c); - if (spec->hp_mux) - via_hp_build(codec); + if (present) + mono_out = 0; + else { + present = snd_hda_jack_detect(codec, 0x1d); + if (!spec->gen.indep_hp_enabled && present) + mono_out = 0; + else + mono_out = 1; + } + parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; + update_power_state(codec, 0x28, parm); + update_power_state(codec, 0x29, parm); + update_power_state(codec, 0x2a, parm); - via_smart51_build(spec); + /* PW 3/4 (1ch/1dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + /* HP Independent Mode, power on AOW3 */ + if (spec->gen.indep_hp_enabled) + update_power_state(codec, 0x25, parm); - return 1; + /* force to D0 for internal Speaker */ + /* MW0 (16h), AOW0 (10h) */ + update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1716S_loopbacks[] = { - { 0x16, HDA_INPUT, 1 }, - { 0x16, HDA_INPUT, 2 }, - { 0x16, HDA_INPUT, 3 }, - { 0x16, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - static int patch_vt1716S(struct hda_codec *codec) { struct via_spec *spec; @@ -5295,321 +1343,191 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->gen.mixer_nid = 0x16; + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + /* automatic parse from the BIOS config */ - err = vt1716S_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; - - spec->stream_name_analog = "VT1716S Analog"; - spec->stream_analog_playback = &vt1716S_pcm_analog_playback; - spec->stream_analog_capture = &vt1716S_pcm_analog_capture; - - spec->stream_name_digital = "VT1716S Digital"; - spec->stream_digital_playback = &vt1716S_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1716S_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; - spec->num_mixers++; - } - - spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; - spec->num_mixers++; + spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; + spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1716S_loopbacks; -#endif - + spec->set_widgets_power_state = set_widgets_power_state_vt1716S; return 0; } /* for vt2002P */ -/* capture mixer elements */ -static struct snd_kcontrol_new vt2002P_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct hda_verb vt2002P_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - +static const struct hda_verb vt2002P_init_verbs[] = { + /* Class-D speaker related verbs */ + {0x1, 0xfe0, 0x4}, + {0x1, 0xfe9, 0x80}, + {0x1, 0xfe2, 0x22}, /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/8 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x37, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, - - /* set PW0 index=0 (MW0) */ - {0x24, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0x88}, { } }; - -static struct hda_verb vt2002P_uniwill_init_verbs[] = { - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, +static const struct hda_verb vt1802_init_verbs[] = { + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0x88}, { } }; -static struct hda_pcm_stream vt2002P_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt2002P_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt2002P_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->multiout.dac_nids[0] = 0x8; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static void set_widgets_power_state_vt2002P(struct hda_codec *codec) { - int err; + struct via_spec *spec = codec->spec; + int imux_is_smixer; + unsigned int parm; + unsigned int present; + /* MUX9 (1eh) = stereo mixer */ + imux_is_smixer = + snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + parm = AC_PWRST_D0; + /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); + + /* outputs */ + /* AOW0 (8h)*/ + update_power_state(codec, 0x8, parm); + + if (spec->codec_type == VT1802) { + /* PW4 (28h), MW4 (18h), MUX4(38h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x38, parm); + } else { + /* PW4 (26h), MW4 (1ch), MUX4(37h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + update_power_state(codec, 0x1c, parm); + update_power_state(codec, 0x37, parm); + } - if (!cfg->line_out_pins[0]) - return -1; + if (spec->codec_type == VT1802) { + /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + update_power_state(codec, 0x15, parm); + update_power_state(codec, 0x35, parm); + } else { + /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + update_power_state(codec, 0x19, parm); + update_power_state(codec, 0x35, parm); + } + if (spec->gen.indep_hp_enabled) + update_power_state(codec, 0x9, AC_PWRST_D0); - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; + /* Class-D */ + /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ + present = snd_hda_jack_detect(codec, 0x25); - return 0; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + parm = present ? AC_PWRST_D3 : AC_PWRST_D0; + if (spec->codec_type == VT1802) + update_power_state(codec, 0x14, parm); + else + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x34, parm); + + /* Mono Out */ + present = snd_hda_jack_detect(codec, 0x26); + + parm = present ? AC_PWRST_D3 : AC_PWRST_D0; + if (spec->codec_type == VT1802) { + /* PW15 (33h), MW8(1ch), MUX8(3ch) */ + update_power_state(codec, 0x33, parm); + update_power_state(codec, 0x1c, parm); + update_power_state(codec, 0x3c, parm); + } else { + /* PW15 (31h), MW8(17h), MUX8(3bh) */ + update_power_state(codec, 0x31, parm); + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x3b, parm); + } + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + update_power_state(codec, 0x21, AC_PWRST_D0); + else + update_power_state(codec, 0x21, AC_PWRST_D3); } -static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) -{ - int err; - - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; - - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - - create_hp_imux(spec); - return 0; -} +/* + * pin fix-up + */ +enum { + VIA_FIXUP_INTMIC_BOOST, + VIA_FIXUP_ASUS_G75, +}; -/* create playback/capture controls for input pins */ -static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) +static void via_fixup_intmic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); - if (err < 0) - return err; - - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); - - return 0; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + override_mic_boost(codec, 0x30, 0, 2, 40); } -static int vt2002P_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return 0; /* can't find valid BIOS pin config */ - - err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; +static const struct hda_fixup via_fixups[] = { + [VIA_FIXUP_INTMIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = via_fixup_intmic_boost, + }, + [VIA_FIXUP_ASUS_G75] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set 0x24 and 0x33 as speakers */ + { 0x24, 0x991301f0 }, + { 0x33, 0x991301f1 }, /* subwoofer */ + { } + } + }, +}; - spec->input_mux = &spec->private_imux[0]; +static const struct snd_pci_quirk vt2002p_fixups[] = { + SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), + SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), + {} +}; - if (spec->hp_mux) - via_hp_build(codec); +/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e + * Replace this with mixer NID 0x1c + */ +static void fix_vt1802_connections(struct hda_codec *codec) +{ + static hda_nid_t conn_24[] = { 0x14, 0x1c }; + static hda_nid_t conn_33[] = { 0x1c }; - return 1; + snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); + snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt2002P_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - - /* patch for vt2002P */ static int patch_vt2002P(struct hda_codec *codec) { @@ -5621,319 +1539,241 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->gen.mixer_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + if (spec->codec_type == VT1802) + fix_vt1802_connections(codec); + add_secret_dac_path(codec); + + snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* automatic parse from the BIOS config */ - err = vt2002P_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; - - spec->stream_name_analog = "VT2002P Analog"; - spec->stream_analog_playback = &vt2002P_pcm_analog_playback; - spec->stream_analog_capture = &vt2002P_pcm_analog_capture; - - spec->stream_name_digital = "VT2002P Digital"; - spec->stream_digital_playback = &vt2002P_pcm_digital_playback; - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt2002P_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; - spec->num_mixers++; - } + if (spec->codec_type == VT1802) + spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; + else + spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt2002P_loopbacks; -#endif - + spec->set_widgets_power_state = set_widgets_power_state_vt2002P; return 0; } /* for vt1812 */ -/* capture mixer elements */ -static struct snd_kcontrol_new vt1812_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, - HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - .name = "Input Source", - .count = 2, - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, - }, - { } /* end */ -}; - -static struct hda_verb vt1812_volume_init_verbs[] = { - /* - * Unmute ADC0-1 and set the default input to mic-in - */ - {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - */ - /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* MUX Indices: Mic = 0 */ - {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, - - /* PW9 Output enable */ - {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, - +static const struct hda_verb vt1812_init_verbs[] = { /* Enable Boost Volume backdoor */ {0x1, 0xfb9, 0x24}, - - /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* set MUX0/1/4/13/15 = 0 (AOW0) */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0}, - {0x35, AC_VERB_SET_CONNECT_SEL, 0}, - {0x38, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, - {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, - /* Enable AOW0 to MW9 */ {0x1, 0xfb8, 0xa8}, { } }; - -static struct hda_verb vt1812_uniwill_init_verbs[] = { - {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, - {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, - {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, - { } -}; - -static struct hda_pcm_stream vt1812_pcm_analog_playback = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x8, /* NID to query formats and rates */ - .ops = { - .open = via_playback_pcm_open, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1812_pcm_analog_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x10, /* NID to query formats and rates */ - .ops = { - .open = via_pcm_open_close, - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup, - .close = via_pcm_open_close, - }, -}; - -static struct hda_pcm_stream vt1812_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; -/* fill in the dac_nids table from the parsed pin configuration */ -static int vt1812_auto_fill_dac_nids(struct via_spec *spec, - const struct auto_pin_cfg *cfg) +static void set_widgets_power_state_vt1812(struct hda_codec *codec) { - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = spec->private_dac_nids; - if (cfg->line_out_pins[0]) - spec->multiout.dac_nids[0] = 0x8; - return 0; -} + struct via_spec *spec = codec->spec; + unsigned int parm; + unsigned int present; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + parm = AC_PWRST_D0; + /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); + + /* outputs */ + /* AOW0 (8h)*/ + update_power_state(codec, 0x8, AC_PWRST_D0); + + /* PW4 (28h), MW4 (18h), MUX4(38h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x38, parm); + + /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + update_power_state(codec, 0x15, parm); + update_power_state(codec, 0x35, parm); + if (spec->gen.indep_hp_enabled) + update_power_state(codec, 0x9, AC_PWRST_D0); + + /* Internal Speaker */ + /* PW0 (24h), MW0(14h), MUX0(34h) */ + present = snd_hda_jack_detect(codec, 0x25); + + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + update_power_state(codec, 0x14, AC_PWRST_D3); + update_power_state(codec, 0x34, AC_PWRST_D3); + } else { + update_power_state(codec, 0x14, AC_PWRST_D0); + update_power_state(codec, 0x34, AC_PWRST_D0); + } -/* add playback controls from the parsed DAC table */ -static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; + /* Mono Out */ + /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ + present = snd_hda_jack_detect(codec, 0x28); - if (!cfg->line_out_pins[0]) - return -1; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + update_power_state(codec, 0x1c, AC_PWRST_D3); + update_power_state(codec, 0x3c, AC_PWRST_D3); + update_power_state(codec, 0x3e, AC_PWRST_D3); + } else { + update_power_state(codec, 0x1c, AC_PWRST_D0); + update_power_state(codec, 0x3c, AC_PWRST_D0); + update_power_state(codec, 0x3e, AC_PWRST_D0); + } - /* Line-Out: PortE */ - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; + /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x33, &parm); + update_power_state(codec, 0x1d, parm); + update_power_state(codec, 0x3d, parm); - return 0; } -static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +/* patch for vt1812 */ +static int patch_vt1812(struct hda_codec *codec) { + struct via_spec *spec; int err; - if (!pin) - return 0; - - spec->multiout.hp_nid = 0x9; - spec->hp_independent_mode_index = 1; + /* create a codec specific record */ + spec = via_new_spec(codec); + if (spec == NULL) + return -ENOMEM; + spec->gen.mixer_nid = 0x21; + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + add_secret_dac_path(codec); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL( - spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); - if (err < 0) + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); + if (err < 0) { + via_free(codec); return err; + } - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Headphone Playback Switch", - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; + spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; - create_hp_imux(spec); + codec->patch_ops = via_patch_ops; + + spec->set_widgets_power_state = set_widgets_power_state_vt1812; return 0; } -/* create playback/capture controls for input pins */ -static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct via_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; - int err; - - err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, - ARRAY_SIZE(pin_idxs)); - if (err < 0) - return err; - - /* build volume/mute control of loopback */ - err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); - if (err < 0) - return err; +/* patch for vt3476 */ - /* for digital mic select */ - snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); - - return 0; -} +static const struct hda_verb vt3476_init_verbs[] = { + /* Enable DMic 8/16/32K */ + {0x1, 0xF7B, 0x30}, + /* Enable Boost Volume backdoor */ + {0x1, 0xFB9, 0x20}, + /* Enable AOW-MW9 path */ + {0x1, 0xFB8, 0x10}, + { } +}; -static int vt1812_parse_auto_config(struct hda_codec *codec) +static void set_widgets_power_state_vt3476(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - fill_dig_outs(codec); - err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); - if (err < 0) - return err; - - if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) - return 0; /* can't find valid BIOS pin config */ - - err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); - if (err < 0) - return err; - err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); + int imux_is_smixer; + unsigned int parm, parm2; + /* MUX10 (1eh) = stereo mixer */ + imux_is_smixer = + snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); + + /* outputs */ + /* PW3 (27h), MW3(37h), AOW3 (bh) */ + if (spec->codec_type == VT1705CF) { + parm = AC_PWRST_D3; + update_power_state(codec, 0x27, parm); + update_power_state(codec, 0x37, parm); + } else { + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x27, &parm); + update_power_state(codec, 0x37, parm); + } - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; + /* PW2 (26h), MW2(36h), AOW2 (ah) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + update_power_state(codec, 0x36, parm); + if (smart51_enabled(codec)) { + /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ + set_pin_power_state(codec, 0x2b, &parm); + update_power_state(codec, 0x3b, parm); + update_power_state(codec, 0x1b, parm); + } + update_conv_power_state(codec, 0xa, parm, 2); - spec->input_mux = &spec->private_imux[0]; + /* PW1 (25h), MW1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + update_power_state(codec, 0x35, parm); + if (smart51_enabled(codec)) { + /* PW6(2ah), MW6(3ah), MUX6(1ah) */ + set_pin_power_state(codec, 0x2a, &parm); + update_power_state(codec, 0x3a, parm); + update_power_state(codec, 0x1a, parm); + } + update_conv_power_state(codec, 0x9, parm, 1); - if (spec->hp_mux) - via_hp_build(codec); + /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + update_power_state(codec, 0x38, parm); + update_power_state(codec, 0x18, parm); + if (spec->gen.indep_hp_enabled) + update_conv_power_state(codec, 0xb, parm, 3); + parm2 = parm; /* for pin 0x0b */ - return 1; + /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + update_power_state(codec, 0x34, parm); + if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + parm = parm2; + update_conv_power_state(codec, 0x8, parm, 0); + /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ + update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static struct hda_amp_list vt1812_loopbacks[] = { - { 0x21, HDA_INPUT, 0 }, - { 0x21, HDA_INPUT, 1 }, - { 0x21, HDA_INPUT, 2 }, - { } /* end */ -}; -#endif - - -/* patch for vt1812 */ -static int patch_vt1812(struct hda_codec *codec) +static int patch_vt3476(struct hda_codec *codec) { struct via_spec *spec; int err; @@ -5943,46 +1783,21 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->gen.mixer_nid = 0x3f; + add_secret_dac_path(codec); + /* automatic parse from the BIOS config */ - err = vt1812_parse_auto_config(codec); + err = via_parse_auto_config(codec); if (err < 0) { via_free(codec); return err; - } else if (!err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration " - "from BIOS. Using genenic mode...\n"); } - - spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; - spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; - - spec->stream_name_analog = "VT1812 Analog"; - spec->stream_analog_playback = &vt1812_pcm_analog_playback; - spec->stream_analog_capture = &vt1812_pcm_analog_capture; - - spec->stream_name_digital = "VT1812 Digital"; - spec->stream_digital_playback = &vt1812_pcm_digital_playback; - - - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = vt1812_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); - get_mux_nids(codec); - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - spec->mixers[spec->num_mixers] = vt1812_capture_mixer; - spec->num_mixers++; - } + spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - spec->loopback.amplist = vt1812_loopbacks; -#endif + spec->set_widgets_power_state = set_widgets_power_state_vt3476; return 0; } @@ -5990,43 +1805,43 @@ static int patch_vt1812(struct hda_codec *codec) /* * patch entries */ -static struct hda_codec_preset snd_hda_preset_via[] = { +static const struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, { .id = 0x1106e710, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e711, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e712, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e713, .name = "VT1709 10-Ch", - .patch = patch_vt1709_10ch}, + .patch = patch_vt1709}, { .id = 0x1106e714, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e715, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e716, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e717, .name = "VT1709 6-Ch", - .patch = patch_vt1709_6ch}, + .patch = patch_vt1709}, { .id = 0x1106e720, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e721, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e722, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e723, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B_8ch}, + .patch = patch_vt1708B}, { .id = 0x1106e724, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e725, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e726, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x1106e727, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B_4ch}, + .patch = patch_vt1708B}, { .id = 0x11060397, .name = "VT1708S", .patch = patch_vt1708S}, { .id = 0x11061397, .name = "VT1708S", @@ -6035,7 +1850,7 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1708S}, { .id = 0x11063397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11064397, .name = "VT1708S", + { .id = 0x11064397, .name = "VT1705", .patch = patch_vt1708S}, { .id = 0x11065397, .name = "VT1708S", .patch = patch_vt1708S}, @@ -6076,6 +1891,16 @@ static struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, { .id = 0x11060440, .name = "VT1818S", .patch = patch_vt1708S}, + { .id = 0x11060446, .name = "VT1802", + .patch = patch_vt2002P}, + { .id = 0x11068446, .name = "VT1802", + .patch = patch_vt2002P}, + { .id = 0x11064760, .name = "VT1705CF", + .patch = patch_vt3476}, + { .id = 0x11064761, .name = "VT1708SCE", + .patch = patch_vt3476}, + { .id = 0x11064762, .name = "VT1808", + .patch = patch_vt3476}, {} /* terminator */ }; |
