aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_generic.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-08-10 11:21:45 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:58:46 -0400
commitcb53c626e1145edf1d619bc4953f6293d3a77ace (patch)
tree715c2ef3d56a5ac7c79498800e888f562c1aa961 /sound/pci/hda/hda_generic.c
parentcca3b3718ca96dca51daf1129ac03003bcede751 (diff)
[ALSA] hda-intel - Add POWER_SAVE option
Added CONFIG_SND_HDA_POWER_SAVE kconfig. It's an experimental option to achieve an aggressive power-saving. With this option, the driver will turn on/off the power of each codec and controller chip dynamically on demand. The patch introduces a new module option 'power_save'. It specifies the second of time-out for automatic power-down. As default, it's 10 seconds. Setting 0 means to suppress the power-saving feature. The codec may have analog-input loopbacks, which are usually represented by mixer elements such as 'Mic Playback Switch' or 'CD Playback Switch'. When these are on, we cannot turn off the mixer and the codec chip has to be kept on. For bookkeeping these states, a new codec-callback is introduced. For the bus-controller side, a new callback pm_notify is introduced, which can be used to turn on/off the contoller appropriately. Note that this power-saving might cause slight click-noise at power-on/off. Also, it might take some time to wake up the codec, and might even drop some tones at the very beginning. This seems to be the side-effect of turning off the controller chip. This turn-off of the controller can be disabled by undefining HDA_POWER_SAVE_RESET_CONTOLLER in hda_intel.c. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda/hda_generic.c')
-rw-r--r--sound/pci/hda/hda_generic.c55
1 files changed, 50 insertions, 5 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 91cd9b9ea5d1..819c804a579f 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -70,6 +70,13 @@ struct hda_gspec {
70 struct hda_pcm pcm_rec; /* PCM information */ 70 struct hda_pcm pcm_rec; /* PCM information */
71 71
72 struct list_head nid_list; /* list of widgets */ 72 struct list_head nid_list; /* list of widgets */
73
74#ifdef CONFIG_SND_HDA_POWER_SAVE
75#define MAX_LOOPBACK_AMPS 7
76 struct hda_loopback_check loopback;
77 int num_loopbacks;
78 struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
79#endif
73}; 80};
74 81
75/* 82/*
@@ -682,11 +689,33 @@ static int parse_input(struct hda_codec *codec)
682 return 0; 689 return 0;
683} 690}
684 691
692#ifdef CONFIG_SND_HDA_POWER_SAVE
693static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
694 int dir, int idx)
695{
696 struct hda_gspec *spec = codec->spec;
697 struct hda_amp_list *p;
698
699 if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
700 snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
701 return;
702 }
703 p = &spec->loopback_list[spec->num_loopbacks++];
704 p->nid = nid;
705 p->dir = dir;
706 p->idx = idx;
707 spec->loopback.amplist = spec->loopback_list;
708}
709#else
710#define add_input_loopback(codec,nid,dir,idx)
711#endif
712
685/* 713/*
686 * create mixer controls if possible 714 * create mixer controls if possible
687 */ 715 */
688static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, 716static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
689 unsigned int index, const char *type, const char *dir_sfx) 717 unsigned int index, const char *type,
718 const char *dir_sfx, int is_loopback)
690{ 719{
691 char name[32]; 720 char name[32];
692 int err; 721 int err;
@@ -700,6 +729,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
700 if ((node->wid_caps & AC_WCAP_IN_AMP) && 729 if ((node->wid_caps & AC_WCAP_IN_AMP) &&
701 (node->amp_in_caps & AC_AMPCAP_MUTE)) { 730 (node->amp_in_caps & AC_AMPCAP_MUTE)) {
702 knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); 731 knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
732 if (is_loopback)
733 add_input_loopback(codec, node->nid, HDA_INPUT, index);
703 snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); 734 snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
704 if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) 735 if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
705 return err; 736 return err;
@@ -707,6 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
707 } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && 738 } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
708 (node->amp_out_caps & AC_AMPCAP_MUTE)) { 739 (node->amp_out_caps & AC_AMPCAP_MUTE)) {
709 knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); 740 knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
741 if (is_loopback)
742 add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
710 snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); 743 snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
711 if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) 744 if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
712 return err; 745 return err;
@@ -765,7 +798,7 @@ static int create_output_mixers(struct hda_codec *codec, const char **names)
765 for (i = 0; i < spec->pcm_vol_nodes; i++) { 798 for (i = 0; i < spec->pcm_vol_nodes; i++) {
766 err = create_mixer(codec, spec->pcm_vol[i].node, 799 err = create_mixer(codec, spec->pcm_vol[i].node,
767 spec->pcm_vol[i].index, 800 spec->pcm_vol[i].index,
768 names[i], "Playback"); 801 names[i], "Playback", 0);
769 if (err < 0) 802 if (err < 0)
770 return err; 803 return err;
771 } 804 }
@@ -782,7 +815,7 @@ static int build_output_controls(struct hda_codec *codec)
782 case 1: 815 case 1:
783 return create_mixer(codec, spec->pcm_vol[0].node, 816 return create_mixer(codec, spec->pcm_vol[0].node,
784 spec->pcm_vol[0].index, 817 spec->pcm_vol[0].index,
785 "Master", "Playback"); 818 "Master", "Playback", 0);
786 case 2: 819 case 2:
787 if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) 820 if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
788 return create_output_mixers(codec, types_speaker); 821 return create_output_mixers(codec, types_speaker);
@@ -818,7 +851,7 @@ static int build_input_controls(struct hda_codec *codec)
818 if (spec->input_mux.num_items == 1) { 851 if (spec->input_mux.num_items == 1) {
819 err = create_mixer(codec, adc_node, 852 err = create_mixer(codec, adc_node,
820 spec->input_mux.items[0].index, 853 spec->input_mux.items[0].index,
821 NULL, "Capture"); 854 NULL, "Capture", 0);
822 if (err < 0) 855 if (err < 0)
823 return err; 856 return err;
824 return 0; 857 return 0;
@@ -884,7 +917,8 @@ static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
884 return err; 917 return err;
885 else if (err >= 1) { 918 else if (err >= 1) {
886 if (err == 1) { 919 if (err == 1) {
887 err = create_mixer(codec, node, i, type, "Playback"); 920 err = create_mixer(codec, node, i, type,
921 "Playback", 1);
888 if (err < 0) 922 if (err < 0)
889 return err; 923 return err;
890 if (err > 0) 924 if (err > 0)
@@ -1020,6 +1054,14 @@ static int build_generic_pcms(struct hda_codec *codec)
1020 return 0; 1054 return 0;
1021} 1055}
1022 1056
1057#ifdef CONFIG_SND_HDA_POWER_SAVE
1058static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1059{
1060 struct hda_gspec *spec = codec->spec;
1061 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1062}
1063#endif
1064
1023 1065
1024/* 1066/*
1025 */ 1067 */
@@ -1027,6 +1069,9 @@ static struct hda_codec_ops generic_patch_ops = {
1027 .build_controls = build_generic_controls, 1069 .build_controls = build_generic_controls,
1028 .build_pcms = build_generic_pcms, 1070 .build_pcms = build_generic_pcms,
1029 .free = snd_hda_generic_free, 1071 .free = snd_hda_generic_free,
1072#ifdef CONFIG_SND_HDA_POWER_SAVE
1073 .check_power_status = generic_check_power_status,
1074#endif
1030}; 1075};
1031 1076
1032/* 1077/*