diff options
author | Lydia Wang <lydiawang@viatech.com.cn> | 2009-10-10 07:07:35 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-10-11 11:54:47 -0400 |
commit | f5271101faf1655d862849f42518c2a88ef394fb (patch) | |
tree | 9a83d326430df953640c25262305d0fefa15dbed /sound/pci/hda/patch_via.c | |
parent | c2c02ea326d3683f551120e74a297b354a223357 (diff) |
ALSA HDA VIA: Add VIA_CTL_WIDGET_ANALOG_MUTE control type
Enter low power state if AA-Path volume is muted.
Signed-off-by: Lydia Wang <lydiawang@viatech.com.cn>
Signed-off-by: Logan Li <loganli@viatech.com.cn>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r-- | sound/pci/hda/patch_via.c | 240 |
1 files changed, 239 insertions, 1 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e62698984287..d6bee620ced6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c | |||
@@ -128,6 +128,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) | |||
128 | enum { | 128 | enum { |
129 | VIA_CTL_WIDGET_VOL, | 129 | VIA_CTL_WIDGET_VOL, |
130 | VIA_CTL_WIDGET_MUTE, | 130 | VIA_CTL_WIDGET_MUTE, |
131 | VIA_CTL_WIDGET_ANALOG_MUTE, | ||
131 | }; | 132 | }; |
132 | 133 | ||
133 | enum { | 134 | enum { |
@@ -177,9 +178,34 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, | |||
177 | return 0; | 178 | return 0; |
178 | } | 179 | } |
179 | 180 | ||
181 | static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); | ||
182 | static void set_jack_power_state(struct hda_codec *codec); | ||
183 | |||
184 | static int analog_input_switch_put(struct snd_kcontrol *kcontrol, | ||
185 | struct snd_ctl_elem_value *ucontrol) | ||
186 | { | ||
187 | int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); | ||
188 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
189 | |||
190 | set_jack_power_state(codec); | ||
191 | analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); | ||
192 | return change; | ||
193 | } | ||
194 | |||
195 | /* modify .put = snd_hda_mixer_amp_switch_put */ | ||
196 | #define ANALOG_INPUT_MUTE \ | ||
197 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
198 | .name = NULL, \ | ||
199 | .index = 0, \ | ||
200 | .info = snd_hda_mixer_amp_switch_info, \ | ||
201 | .get = snd_hda_mixer_amp_switch_get, \ | ||
202 | .put = analog_input_switch_put, \ | ||
203 | .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } | ||
204 | |||
180 | static struct snd_kcontrol_new vt1708_control_templates[] = { | 205 | static struct snd_kcontrol_new vt1708_control_templates[] = { |
181 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), | 206 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), |
182 | HDA_CODEC_MUTE(NULL, 0, 0, 0), | 207 | HDA_CODEC_MUTE(NULL, 0, 0, 0), |
208 | ANALOG_INPUT_MUTE, | ||
183 | }; | 209 | }; |
184 | 210 | ||
185 | 211 | ||
@@ -303,7 +329,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, | |||
303 | if (err < 0) | 329 | if (err < 0) |
304 | return err; | 330 | return err; |
305 | sprintf(name, "%s Playback Switch", ctlname); | 331 | sprintf(name, "%s Playback Switch", ctlname); |
306 | err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, | 332 | err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, |
307 | HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); | 333 | HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); |
308 | if (err < 0) | 334 | if (err < 0) |
309 | return err; | 335 | return err; |
@@ -362,6 +388,131 @@ static void via_auto_init_analog_input(struct hda_codec *codec) | |||
362 | 388 | ||
363 | } | 389 | } |
364 | } | 390 | } |
391 | |||
392 | static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, | ||
393 | unsigned int *affected_parm) | ||
394 | { | ||
395 | unsigned parm; | ||
396 | unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); | ||
397 | unsigned no_presence = (def_conf & AC_DEFCFG_MISC) | ||
398 | >> AC_DEFCFG_MISC_SHIFT | ||
399 | & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ | ||
400 | unsigned present = snd_hda_codec_read(codec, nid, 0, | ||
401 | AC_VERB_GET_PIN_SENSE, 0) >> 31; | ||
402 | |||
403 | if ((no_presence || present) && get_defcfg_connect(def_conf) | ||
404 | != AC_JACK_PORT_NONE) { | ||
405 | *affected_parm = AC_PWRST_D0; /* if it's connected */ | ||
406 | parm = AC_PWRST_D0; | ||
407 | } else | ||
408 | parm = AC_PWRST_D3; | ||
409 | |||
410 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); | ||
411 | } | ||
412 | |||
413 | static void set_jack_power_state(struct hda_codec *codec) | ||
414 | { | ||
415 | struct via_spec *spec = codec->spec; | ||
416 | int imux_is_smixer; | ||
417 | unsigned int parm; | ||
418 | |||
419 | if (spec->codec_type == VT1702) { | ||
420 | imux_is_smixer = snd_hda_codec_read( | ||
421 | codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; | ||
422 | /* inputs */ | ||
423 | /* PW 1/2/5 (14h/15h/18h) */ | ||
424 | parm = AC_PWRST_D3; | ||
425 | set_pin_power_state(codec, 0x14, &parm); | ||
426 | set_pin_power_state(codec, 0x15, &parm); | ||
427 | set_pin_power_state(codec, 0x18, &parm); | ||
428 | if (imux_is_smixer) | ||
429 | parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ | ||
430 | /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ | ||
431 | snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, | ||
432 | parm); | ||
433 | snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, | ||
434 | parm); | ||
435 | snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, | ||
436 | parm); | ||
437 | snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, | ||
438 | parm); | ||
439 | |||
440 | /* outputs */ | ||
441 | /* PW 3/4 (16h/17h) */ | ||
442 | parm = AC_PWRST_D3; | ||
443 | set_pin_power_state(codec, 0x16, &parm); | ||
444 | set_pin_power_state(codec, 0x17, &parm); | ||
445 | /* MW0 (1ah), AOW 0/1 (10h/1dh) */ | ||
446 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, | ||
447 | imux_is_smixer ? AC_PWRST_D0 : parm); | ||
448 | snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, | ||
449 | parm); | ||
450 | snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, | ||
451 | parm); | ||
452 | } else if (spec->codec_type == VT1708B_8CH | ||
453 | || spec->codec_type == VT1708B_4CH | ||
454 | || spec->codec_type == VT1708S) { | ||
455 | /* SW0 (17h) = stereo mixer */ | ||
456 | int is_8ch = spec->codec_type != VT1708B_4CH; | ||
457 | imux_is_smixer = snd_hda_codec_read( | ||
458 | codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) | ||
459 | == ((spec->codec_type == VT1708S) ? 5 : 0); | ||
460 | /* inputs */ | ||
461 | /* PW 1/2/5 (1ah/1bh/1eh) */ | ||
462 | parm = AC_PWRST_D3; | ||
463 | set_pin_power_state(codec, 0x1a, &parm); | ||
464 | set_pin_power_state(codec, 0x1b, &parm); | ||
465 | set_pin_power_state(codec, 0x1e, &parm); | ||
466 | if (imux_is_smixer) | ||
467 | parm = AC_PWRST_D0; | ||
468 | /* SW0 (17h), AIW 0/1 (13h/14h) */ | ||
469 | snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, | ||
470 | parm); | ||
471 | snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, | ||
472 | parm); | ||
473 | snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, | ||
474 | parm); | ||
475 | |||
476 | /* outputs */ | ||
477 | /* PW0 (19h), SW1 (18h), AOW1 (11h) */ | ||
478 | parm = AC_PWRST_D3; | ||
479 | set_pin_power_state(codec, 0x19, &parm); | ||
480 | snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, | ||
481 | parm); | ||
482 | snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, | ||
483 | parm); | ||
484 | |||
485 | /* PW6 (22h), SW2 (26h), AOW2 (24h) */ | ||
486 | if (is_8ch) { | ||
487 | parm = AC_PWRST_D3; | ||
488 | set_pin_power_state(codec, 0x22, &parm); | ||
489 | snd_hda_codec_write(codec, 0x26, 0, | ||
490 | AC_VERB_SET_POWER_STATE, parm); | ||
491 | snd_hda_codec_write(codec, 0x24, 0, | ||
492 | AC_VERB_SET_POWER_STATE, parm); | ||
493 | } | ||
494 | |||
495 | /* PW 3/4/7 (1ch/1dh/23h) */ | ||
496 | parm = AC_PWRST_D3; | ||
497 | /* force to D0 for internal Speaker */ | ||
498 | set_pin_power_state(codec, 0x1c, &parm); | ||
499 | set_pin_power_state(codec, 0x1d, &parm); | ||
500 | if (is_8ch) | ||
501 | set_pin_power_state(codec, 0x23, &parm); | ||
502 | /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ | ||
503 | snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, | ||
504 | imux_is_smixer ? AC_PWRST_D0 : parm); | ||
505 | snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, | ||
506 | parm); | ||
507 | if (is_8ch) { | ||
508 | snd_hda_codec_write(codec, 0x25, 0, | ||
509 | AC_VERB_SET_POWER_STATE, parm); | ||
510 | snd_hda_codec_write(codec, 0x27, 0, | ||
511 | AC_VERB_SET_POWER_STATE, parm); | ||
512 | } | ||
513 | } | ||
514 | } | ||
515 | |||
365 | /* | 516 | /* |
366 | * input MUX handling | 517 | * input MUX handling |
367 | */ | 518 | */ |
@@ -504,6 +655,93 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { | |||
504 | }, | 655 | }, |
505 | { } /* end */ | 656 | { } /* end */ |
506 | }; | 657 | }; |
658 | |||
659 | /* check AA path's mute statue */ | ||
660 | static int is_aa_path_mute(struct hda_codec *codec) | ||
661 | { | ||
662 | int mute = 1; | ||
663 | hda_nid_t nid_mixer; | ||
664 | int start_idx; | ||
665 | int end_idx; | ||
666 | int i; | ||
667 | struct via_spec *spec = codec->spec; | ||
668 | /* get nid of MW0 and start & end index */ | ||
669 | switch (spec->codec_type) { | ||
670 | case VT1708B_8CH: | ||
671 | case VT1708B_4CH: | ||
672 | case VT1708S: | ||
673 | nid_mixer = 0x16; | ||
674 | start_idx = 2; | ||
675 | end_idx = 4; | ||
676 | break; | ||
677 | case VT1702: | ||
678 | nid_mixer = 0x1a; | ||
679 | start_idx = 1; | ||
680 | end_idx = 3; | ||
681 | break; | ||
682 | default: | ||
683 | return 0; | ||
684 | } | ||
685 | /* check AA path's mute status */ | ||
686 | for (i = start_idx; i <= end_idx; i++) { | ||
687 | unsigned int con_list = snd_hda_codec_read( | ||
688 | codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); | ||
689 | int shift = 8 * (i % 4); | ||
690 | hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; | ||
691 | unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); | ||
692 | if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { | ||
693 | /* check mute status while the pin is connected */ | ||
694 | int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, | ||
695 | HDA_INPUT, i) >> 7; | ||
696 | int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, | ||
697 | HDA_INPUT, i) >> 7; | ||
698 | if (!mute_l || !mute_r) { | ||
699 | mute = 0; | ||
700 | break; | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | return mute; | ||
705 | } | ||
706 | |||
707 | /* enter/exit analog low-current mode */ | ||
708 | static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) | ||
709 | { | ||
710 | struct via_spec *spec = codec->spec; | ||
711 | static int saved_stream_idle = 1; /* saved stream idle status */ | ||
712 | int enable = is_aa_path_mute(codec); | ||
713 | unsigned int verb = 0; | ||
714 | unsigned int parm = 0; | ||
715 | |||
716 | if (stream_idle == -1) /* stream status did not change */ | ||
717 | enable = enable && saved_stream_idle; | ||
718 | else { | ||
719 | enable = enable && stream_idle; | ||
720 | saved_stream_idle = stream_idle; | ||
721 | } | ||
722 | |||
723 | /* decide low current mode's verb & parameter */ | ||
724 | switch (spec->codec_type) { | ||
725 | case VT1708B_8CH: | ||
726 | case VT1708B_4CH: | ||
727 | verb = 0xf70; | ||
728 | parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ | ||
729 | break; | ||
730 | case VT1708S: | ||
731 | verb = 0xf73; | ||
732 | parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ | ||
733 | break; | ||
734 | case VT1702: | ||
735 | verb = 0xf73; | ||
736 | parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ | ||
737 | break; | ||
738 | default: | ||
739 | return; /* other codecs are not supported */ | ||
740 | } | ||
741 | /* send verb */ | ||
742 | snd_hda_codec_write(codec, codec->afg, 0, verb, parm); | ||
743 | } | ||
744 | |||
507 | /* | 745 | /* |
508 | * generic initialization of ADC, input mixers and output mixers | 746 | * generic initialization of ADC, input mixers and output mixers |
509 | */ | 747 | */ |