aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-03-18 04:23:10 -0400
committerTakashi Iwai <tiwai@suse.de>2015-03-18 04:23:10 -0400
commit5ccf835cc76d89bc0d426659c63d81f609050842 (patch)
treea601871aa85d8115b63f999c00a8a2808b79678f
parent688b12cc3ca8a5155b95ce8d01e0e43006813b27 (diff)
ALSA: hda - Adjust power of beep widget and outputs
As the widget PM may turn off the pins, this might lead to the silent output for beep when no explicit paths are given. This patch adds fake output paths for the beep widget so that the output pins are dynamically powered upon beep on/off. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/pci/hda/hda_beep.c29
-rw-r--r--sound/pci/hda/hda_beep.h1
-rw-r--r--sound/pci/hda/hda_generic.c69
3 files changed, 86 insertions, 13 deletions
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 581b7fdef0e3..4cdac3a71cae 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -33,30 +33,36 @@ enum {
33 DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ 33 DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
34}; 34};
35 35
36static void snd_hda_generate_beep(struct work_struct *work) 36/* generate or stop tone */
37static void generate_tone(struct hda_beep *beep, int tone)
37{ 38{
38 struct hda_beep *beep =
39 container_of(work, struct hda_beep, beep_work);
40 struct hda_codec *codec = beep->codec; 39 struct hda_codec *codec = beep->codec;
41 int tone;
42 40
43 if (!beep->enabled)
44 return;
45
46 tone = beep->tone;
47 if (tone && !beep->playing) { 41 if (tone && !beep->playing) {
48 snd_hda_power_up(codec); 42 snd_hda_power_up(codec);
43 if (beep->power_hook)
44 beep->power_hook(beep, true);
49 beep->playing = 1; 45 beep->playing = 1;
50 } 46 }
51 /* generate tone */
52 snd_hda_codec_write(codec, beep->nid, 0, 47 snd_hda_codec_write(codec, beep->nid, 0,
53 AC_VERB_SET_BEEP_CONTROL, tone); 48 AC_VERB_SET_BEEP_CONTROL, tone);
54 if (!tone && beep->playing) { 49 if (!tone && beep->playing) {
55 beep->playing = 0; 50 beep->playing = 0;
51 if (beep->power_hook)
52 beep->power_hook(beep, false);
56 snd_hda_power_down(codec); 53 snd_hda_power_down(codec);
57 } 54 }
58} 55}
59 56
57static void snd_hda_generate_beep(struct work_struct *work)
58{
59 struct hda_beep *beep =
60 container_of(work, struct hda_beep, beep_work);
61
62 if (beep->enabled)
63 generate_tone(beep, beep->tone);
64}
65
60/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 66/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
61 * 67 *
62 * The tone frequency of beep generator on IDT/STAC codecs is 68 * The tone frequency of beep generator on IDT/STAC codecs is
@@ -130,10 +136,7 @@ static void turn_off_beep(struct hda_beep *beep)
130 cancel_work_sync(&beep->beep_work); 136 cancel_work_sync(&beep->beep_work);
131 if (beep->playing) { 137 if (beep->playing) {
132 /* turn off beep */ 138 /* turn off beep */
133 snd_hda_codec_write(beep->codec, beep->nid, 0, 139 generate_tone(beep, 0);
134 AC_VERB_SET_BEEP_CONTROL, 0);
135 beep->playing = 0;
136 snd_hda_power_down(beep->codec);
137 } 140 }
138} 141}
139 142
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index a63b5e077332..46524ff7e79e 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -40,6 +40,7 @@ struct hda_beep {
40 unsigned int playing:1; 40 unsigned int playing:1;
41 struct work_struct beep_work; /* scheduled task for beep event */ 41 struct work_struct beep_work; /* scheduled task for beep event */
42 struct mutex mutex; 42 struct mutex mutex;
43 void (*power_hook)(struct hda_beep *beep, bool on);
43}; 44};
44 45
45#ifdef CONFIG_SND_HDA_INPUT_BEEP 46#ifdef CONFIG_SND_HDA_INPUT_BEEP
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 8a5055d296f5..d7ca388651da 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -654,6 +654,9 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
654 int type = get_wcaps_type(get_wcaps(codec, nid)); 654 int type = get_wcaps_type(get_wcaps(codec, nid));
655 int i, n; 655 int i, n;
656 656
657 if (nid == codec->afg)
658 return true;
659
657 for (n = 0; n < spec->paths.used; n++) { 660 for (n = 0; n < spec->paths.used; n++) {
658 struct nid_path *path = snd_array_elem(&spec->paths, n); 661 struct nid_path *path = snd_array_elem(&spec->paths, n);
659 if (!path->active) 662 if (!path->active)
@@ -829,6 +832,8 @@ static hda_nid_t path_power_update(struct hda_codec *codec,
829 832
830 for (i = 0; i < path->depth; i++) { 833 for (i = 0; i < path->depth; i++) {
831 nid = path->path[i]; 834 nid = path->path[i];
835 if (nid == codec->afg)
836 continue;
832 if (!allow_powerdown || is_active_nid_for_any(codec, nid)) 837 if (!allow_powerdown || is_active_nid_for_any(codec, nid))
833 state = AC_PWRST_D0; 838 state = AC_PWRST_D0;
834 else 839 else
@@ -4073,6 +4078,64 @@ static void sync_all_pin_power_ctls(struct hda_codec *codec)
4073 sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); 4078 sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin);
4074} 4079}
4075 4080
4081/* add fake paths if not present yet */
4082static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid,
4083 int num_pins, const hda_nid_t *pins)
4084{
4085 struct hda_gen_spec *spec = codec->spec;
4086 struct nid_path *path;
4087 int i;
4088
4089 for (i = 0; i < num_pins; i++) {
4090 if (!pins[i])
4091 break;
4092 if (get_nid_path(codec, nid, pins[i], 0))
4093 continue;
4094 path = snd_array_new(&spec->paths);
4095 if (!path)
4096 return -ENOMEM;
4097 memset(path, 0, sizeof(*path));
4098 path->depth = 2;
4099 path->path[0] = nid;
4100 path->path[1] = pins[i];
4101 path->active = true;
4102 }
4103 return 0;
4104}
4105
4106/* create fake paths to all outputs from beep */
4107static int add_fake_beep_paths(struct hda_codec *codec)
4108{
4109 struct hda_gen_spec *spec = codec->spec;
4110 struct auto_pin_cfg *cfg = &spec->autocfg;
4111 hda_nid_t nid = spec->beep_nid;
4112 int err;
4113
4114 if (!codec->power_mgmt || !nid)
4115 return 0;
4116 err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins);
4117 if (err < 0)
4118 return err;
4119 if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
4120 err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins);
4121 if (err < 0)
4122 return err;
4123 }
4124 if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
4125 err = add_fake_paths(codec, nid, cfg->speaker_outs,
4126 cfg->speaker_pins);
4127 if (err < 0)
4128 return err;
4129 }
4130 return 0;
4131}
4132
4133/* power up/down beep widget and its output paths */
4134static void beep_power_hook(struct hda_beep *beep, bool on)
4135{
4136 set_path_power(beep->codec, beep->nid, -1, on);
4137}
4138
4076/* 4139/*
4077 * Jack detections for HP auto-mute and mic-switch 4140 * Jack detections for HP auto-mute and mic-switch
4078 */ 4141 */
@@ -4837,6 +4900,12 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
4837 err = snd_hda_attach_beep_device(codec, spec->beep_nid); 4900 err = snd_hda_attach_beep_device(codec, spec->beep_nid);
4838 if (err < 0) 4901 if (err < 0)
4839 return err; 4902 return err;
4903 if (codec->beep && codec->power_mgmt) {
4904 err = add_fake_beep_paths(codec);
4905 if (err < 0)
4906 return err;
4907 codec->beep->power_hook = beep_power_hook;
4908 }
4840 } 4909 }
4841 4910
4842 return 1; 4911 return 1;