Loading sound/pci/hda/patch_via.c +239 −1 Original line number Diff line number Diff line Loading @@ -128,6 +128,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, }; enum { Loading Loading @@ -177,9 +178,34 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, return 0; } 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 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); 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 struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, }; Loading Loading @@ -303,7 +329,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; Loading Loading @@ -362,6 +388,131 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { unsigned parm; unsigned def_conf = snd_hda_codec_get_pincfg(codec, 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_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) >> 31; if ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { *affected_parm = AC_PWRST_D0; /* if it's connected */ parm = AC_PWRST_D0; } else parm = AC_PWRST_D3; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } static void set_jack_power_state(struct hda_codec *codec) { 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); 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); 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); } } } /* * input MUX handling */ Loading Loading @@ -504,6 +655,93 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { }, { } /* end */ }; /* check AA path's mute statue */ static int 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: nid_mixer = 0x16; start_idx = 2; end_idx = 4; break; case VT1702: nid_mixer = 0x1a; start_idx = 1; end_idx = 3; 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; } } } return mute; } /* enter/exit analog low-current mode */ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) { 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; if (stream_idle == -1) /* stream status did not change */ enable = enable && saved_stream_idle; else { enable = enable && stream_idle; saved_stream_idle = stream_idle; } /* decide low current mode's verb & parameter */ switch (spec->codec_type) { case VT1708B_8CH: case VT1708B_4CH: verb = 0xf70; parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ break; case VT1708S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; case VT1702: verb = 0xf73; parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; default: return; /* other codecs are not supported */ } /* send verb */ snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } /* * generic initialization of ADC, input mixers and output mixers */ Loading Loading
sound/pci/hda/patch_via.c +239 −1 Original line number Diff line number Diff line Loading @@ -128,6 +128,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, }; enum { Loading Loading @@ -177,9 +178,34 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, return 0; } 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 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); 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 struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, }; Loading Loading @@ -303,7 +329,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; Loading Loading @@ -362,6 +388,131 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { unsigned parm; unsigned def_conf = snd_hda_codec_get_pincfg(codec, 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_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) >> 31; if ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { *affected_parm = AC_PWRST_D0; /* if it's connected */ parm = AC_PWRST_D0; } else parm = AC_PWRST_D3; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } static void set_jack_power_state(struct hda_codec *codec) { 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); 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); 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); } } } /* * input MUX handling */ Loading Loading @@ -504,6 +655,93 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { }, { } /* end */ }; /* check AA path's mute statue */ static int 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: nid_mixer = 0x16; start_idx = 2; end_idx = 4; break; case VT1702: nid_mixer = 0x1a; start_idx = 1; end_idx = 3; 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; } } } return mute; } /* enter/exit analog low-current mode */ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) { 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; if (stream_idle == -1) /* stream status did not change */ enable = enable && saved_stream_idle; else { enable = enable && stream_idle; saved_stream_idle = stream_idle; } /* decide low current mode's verb & parameter */ switch (spec->codec_type) { case VT1708B_8CH: case VT1708B_4CH: verb = 0xf70; parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ break; case VT1708S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; case VT1702: verb = 0xf73; parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; default: return; /* other codecs are not supported */ } /* send verb */ snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } /* * generic initialization of ADC, input mixers and output mixers */ Loading