diff options
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r-- | sound/pci/hda/patch_via.c | 5860 |
1 files changed, 1600 insertions, 4260 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f43bb0eaed8..0da4f8ff542 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,36 +54,10 @@ #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) - /* 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, @@ -107,6 +81,34 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) +#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; +}; + +/* input-path */ +struct via_input { + hda_nid_t pin; /* input-pin or aa-mix */ + int adc_idx; /* ADC index to be used */ + int mux_idx; /* MUX index (if any) */ + const char *label; /* input-source label */ +}; + +#define VIA_MAX_ADCS 3 + struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; @@ -115,28 +117,45 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; - char *stream_name_analog; + char stream_name_analog[32]; + char stream_name_hp[32]; const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; - char *stream_name_digital; + char stream_name_digital[32]; const struct hda_pcm_stream *stream_digital_playback; const struct hda_pcm_stream *stream_digital_capture; /* playback */ struct hda_multi_out multiout; hda_nid_t slave_dig_outs[2]; + hda_nid_t hp_dac_nid; + bool hp_indep_shared; /* indep HP-DAC is shared with side ch */ + int num_active_streams; + int dac_mixer_idx; + + struct nid_path out_path[HDA_SIDE + 1]; + struct nid_path hp_path; + struct nid_path hp_dep_path; + struct nid_path speaker_path; /* capture */ unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; + hda_nid_t adc_nids[VIA_MAX_ADCS]; + hda_nid_t mux_nids[VIA_MAX_ADCS]; + hda_nid_t aa_mix_nid; 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]; + bool dyn_adc_switch; + int num_inputs; + struct via_input inputs[AUTO_CFG_MAX_INS + 1]; + unsigned int cur_mux[VIA_MAX_ADCS]; + + /* dynamic ADC switching */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; /* PCM information */ struct hda_pcm pcm_rec[3]; @@ -144,28 +163,36 @@ struct via_spec { /* 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; + /* smart51 setup */ + unsigned int smart51_nums; + hda_nid_t smart51_pins[2]; + int smart51_idxs[2]; + const char *smart51_labels[2]; + unsigned int smart51_enabled; + /* work to check hp jack state */ struct hda_codec *codec; struct delayed_work vt1708_hp_work; - int vt1708_jack_detectect; + int vt1708_jack_detect; int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; -#endif + int num_loopbacks; + struct hda_amp_list loopback_list[8]; + + /* bind capture-volume */ + struct hda_bind_ctls *bind_cap_vol; + struct hda_bind_ctls *bind_cap_sw; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -237,33 +264,23 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_JACK_EVENT 0x20 #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 -#define VIA_MONO_EVENT 0x03 -#define VIA_SPEAKER_EVENT 0x04 -#define VIA_BIND_HP_EVENT 0x05 +#define VIA_LINE_EVENT 0x03 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, - VIA_CTL_WIDGET_BIND_PIN_MUTE, -}; - -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 int is_aa_path_mute(struct hda_codec *codec); +static void analog_low_current_mode(struct hda_codec *codec); +static bool is_aa_path_mute(struct hda_codec *codec); static void vt1708_start_hp_work(struct via_spec *spec) { 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); + !spec->vt1708_jack_detect); if (!delayed_work_pending(&spec->vt1708_hp_work)) schedule_delayed_work(&spec->vt1708_hp_work, msecs_to_jiffies(100)); @@ -277,7 +294,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) && !is_aa_path_mute(spec->codec)) return; snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, - !spec->vt1708_jack_detectect); + !spec->vt1708_jack_detect); cancel_delayed_work_sync(&spec->vt1708_hp_work); } @@ -295,7 +312,7 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); set_widgets_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + analog_low_current_mode(snd_kcontrol_chip(kcontrol)); if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { if (is_aa_path_mute(codec)) vt1708_start_hp_work(codec->spec); @@ -315,168 +332,44 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, .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) -{ - 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); - } - } - return change; -} - -#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 const 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 const hda_nid_t vt1708_adc_nids[2] = { - /* ADC1-2 */ - 0x15, 0x27 -}; - -static const hda_nid_t vt1709_adc_nids[3] = { - /* ADC1-2 */ - 0x14, 0x15, 0x16 -}; - -static const hda_nid_t vt1708B_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1708S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 -}; - -static const hda_nid_t vt1702_adc_nids[3] = { - /* ADC1-2 */ - 0x12, 0x20, 0x1F -}; - -static const hda_nid_t vt1718S_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; - -static const hda_nid_t vt1716S_adc_nids[2] = { - /* ADC1-2 */ - 0x13, 0x14 }; -static const hda_nid_t vt2002P_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; -static const hda_nid_t vt1812_adc_nids[2] = { - /* ADC1-2 */ - 0x10, 0x11 -}; +/* add dynamic controls */ +static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, + const struct snd_kcontrol_new *tmpl, + const char *name) +{ + 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; + if (!name) + name = tmpl->name; + if (name) { + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + } + return knew; +} -/* 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); + knew = __via_clone_ctl(spec, &via_control_templates[type], name); if (!knew) return -ENOMEM; - *knew = via_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return -ENOMEM; + knew->index = idx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; @@ -486,21 +379,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name, #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, - const 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; -} +#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) static void via_free_kctls(struct hda_codec *codec) { @@ -535,58 +414,227 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname, return 0; } -static void via_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) + +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int mask) +{ + unsigned int caps; + if (!nid) + return false; + caps = get_wcaps(codec, nid); + if (dir == HDA_INPUT) + caps &= AC_WCAP_IN_AMP; + else + caps &= AC_WCAP_OUT_AMP; + if (!caps) + return false; + if (query_amp_caps(codec, nid, dir) & mask) + return true; + return false; +} + +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) + +static bool is_node_in_path(struct nid_path *path, hda_nid_t nid) +{ + int i; + if (!nid) + return false; + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) + return true; + } + return false; +} + +/* enable/disable the output-route mixers */ +static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, + hda_nid_t mix_nid, int aa_mix_idx, bool enable) +{ + int i, num, val; + bool hp_path, front_path; + struct via_spec *spec = codec->spec; + + if (!path) + return; + num = snd_hda_get_conn_list(codec, mix_nid, NULL); + hp_path = is_node_in_path(path, spec->hp_dac_nid); + front_path = is_node_in_path(path, spec->multiout.dac_nids[0]); + + for (i = 0; i < num; i++) { + if (i == aa_mix_idx) { + if (hp_path) + val = enable ? AMP_IN_MUTE(i) : + AMP_IN_UNMUTE(i); + else if (front_path) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); + } else { + if (hp_path) + val = enable ? AMP_IN_UNMUTE(i) : + AMP_IN_MUTE(i); + else if (front_path) + val = AMP_IN_MUTE(i); + else + val = AMP_IN_UNMUTE(i); + } + snd_hda_codec_write(codec, mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) +{ + int i, val; + struct via_spec *spec = codec->spec; + hda_nid_t aa_mix_nid = spec->aa_mix_nid; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (!force + && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT + && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX) + continue; + if (have_mute(codec, dst, HDA_INPUT)) { + if (dst == aa_mix_nid) { + val = enable ? AMP_IN_UNMUTE(idx) : + AMP_IN_MUTE(idx); + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } else { + idx = get_connection_index(codec, dst, + aa_mix_nid); + if (idx >= 0) { + activate_output_mix(codec, path, + dst, idx, enable); + } + } + } + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } +} + +/* set the given pin as output */ +static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, + int pin_type) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + if (!pin) + return; + snd_hda_codec_write(codec, pin, 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, + if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type, + bool with_aa_mix, bool force) +{ + struct via_spec *spec = codec->spec; + unsigned int caps; + hda_nid_t pin; + + if (!path->depth) + return; + pin = path->path[path->depth - 1]; + + init_output_pin(codec, pin, pin_type); + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + if (caps & AC_AMPCAP_MUTE) { + unsigned int val; + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE | val); + } + + /* initialize the AA-path */ + if (!spec->aa_mix_nid) + return; + activate_output_path(codec, path, true, force); +} static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i; - 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); - } + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) + /* enable aa-mute only for the front channel */ + via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, + i == 0, true); } static void via_auto_init_hp_out(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); + if (!spec->hp_dac_nid) { + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, + true, true); + return; + } + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + via_auto_init_output(codec, &spec->hp_path, PIN_HP, + true, true); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, + true, true); } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); +static void via_auto_init_speaker_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (spec->autocfg.speaker_outs) + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, + true, true); +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); static void via_auto_init_analog_input(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; unsigned int ctl; - int i; + int i, num_conns; + + /* init ADCs */ + for (i = 0; i < spec->num_adc_nids; i++) { + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + /* init pins */ 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)) + if (spec->smart51_enabled && is_smart51_pins(codec, nid)) ctl = PIN_OUT; else if (cfg->inputs[i].type == AUTO_PIN_MIC) ctl = PIN_VREF50; @@ -595,6 +643,32 @@ static void via_auto_init_analog_input(struct hda_codec *codec) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); } + + /* init input-src */ + for (i = 0; i < spec->num_adc_nids; i++) { + int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + if (spec->mux_nids[adc_idx]) { + int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + if (spec->dyn_adc_switch) + break; /* only one input-src */ + } + + /* init aa-mixer */ + if (!spec->aa_mix_nid) + return; + num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn)); + for (i = 0; i < num_conns; i++) { + unsigned int caps = get_wcaps(codec, conn[i]); + if (get_wcaps_type(caps) == AC_WID_PIN) + snd_hda_codec_write(codec, spec->aa_mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, @@ -605,9 +679,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 ((spec->smart51_enabled && 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 */ @@ -618,123 +696,77 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } -/* - * 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_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->input_mux, uinfo); + static const char * const texts[] = { + "Disabled", "Enabled" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +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; - 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); + unsigned int val = !ucontrol->value.enumerated.item[0]; - 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 */ + if (val == spec->no_pin_power_ctl) + return 0; + spec->no_pin_power_ctl = val; set_widgets_power_state(codec); - - return ret; + return 1; } +static const struct snd_kcontrol_new via_pin_power_ctl_enum = { + .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, +}; + + 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 const char * const texts[] = { "OFF", "ON" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; } 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; - case VT2002P: return 0x19; - case VT1802: return 0x15; - case VT1812: return 0x15; - 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; - hda_nid_t sw3 = side_mute_channel(spec); - if (sw3) { - if (VT2002P_COMPATIBLE(spec)) - parm = spec->hp_independent_mode ? - AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); - else - parm = spec->hp_independent_mode ? - AMP_OUT_MUTE : AMP_OUT_UNMUTE; - snd_hda_codec_write(codec, sw3, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); - if (spec->codec_type == VT1812) - snd_hda_codec_write(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm); - } + ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; return 0; } @@ -743,66 +775,41 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { 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]; - unsigned int parm0, parm1; - /* 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); - /* Set correct mute switch for MW3 */ - parm0 = spec->hp_independent_mode ? - AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0); - parm1 = spec->hp_independent_mode ? - AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm0); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_AMP_GAIN_MUTE, parm1); - } - else - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, pinsel); + int cur; - 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 - || VT2002P_COMPATIBLE(spec)) { - activate_ctl(codec, "Headphone Playback Volume", - spec->hp_independent_mode); - activate_ctl(codec, "Headphone Playback Switch", - spec->hp_independent_mode); + /* no independent-hp status change during PCM playback is running */ + if (spec->num_active_streams) + return -EBUSY; + + cur = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode == cur) + return 0; + spec->hp_independent_mode = cur; + if (cur) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + activate_output_path(codec, &spec->hp_path, true, false); + if (spec->hp_indep_shared) + activate_output_path(codec, &spec->out_path[HDA_SIDE], + false, false); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_dep_path, true, false); + if (spec->hp_indep_shared) + activate_output_path(codec, &spec->out_path[HDA_SIDE], + true, false); } + /* update jack power state */ set_widgets_power_state(codec); - return 0; + return 1; } -static const 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 const struct snd_kcontrol_new via_hp_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = via_independent_hp_info, + .get = via_independent_hp_get, + .put = via_independent_hp_put, }; static int via_hp_build(struct hda_codec *codec) @@ -810,61 +817,28 @@ 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: - case VT1802: - 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]); + nid = spec->autocfg.hp_pins[0]; + knew = via_clone_control(spec, &via_hp_mixer); if (knew == NULL) return -ENOMEM; knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - knew->private_value = nid; - - nid = side_mute_channel(spec); - if (nid) { - knew = via_clone_control(spec, &via_hp_mixer[1]); - if (knew == NULL) - return -ENOMEM; - knew->subdevice = nid; - } return 0; } static void notify_aa_path_ctls(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; int i; - struct snd_ctl_elem_id id; - const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; - struct snd_kcontrol *ctl; - - 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]); + + for (i = 0; i < spec->smart51_nums; i++) { + struct snd_kcontrol *ctl; + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); ctl = snd_hda_find_mixer_ctl(codec, id.name); if (ctl) snd_ctl_notify(codec->bus->card, @@ -876,66 +850,28 @@ static void notify_aa_path_ctls(struct hda_codec *codec) 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 val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 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, + for (i = 0; i < spec->smart51_nums; i++) { + if (spec->smart51_idxs[i] < 0) + continue; + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, + HDA_INPUT, spec->smart51_idxs[i], HDA_AMP_MUTE, val); } } -static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) { - const struct auto_pin_cfg *cfg = &spec->autocfg; + struct via_spec *spec = codec->spec; 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_BOOLEA |