aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-04-08 05:43:14 -0400
committerTakashi Iwai <tiwai@suse.de>2015-04-08 07:50:42 -0400
commit664c715573c2c116c2d8f5de7d59ce85a98a1751 (patch)
tree78629587a6d0ddfc478fa59d0248baa815f7132f /sound/pci
parent142267c9e026827ca5fa622f1f13780b6db26cf8 (diff)
ALSA: hda - Work around races of power up/down with runtime PM
Currently, snd_hdac_power_up()/down() helpers checks whether the codec is being in pm (suspend/resume), and skips the call of runtime get/put during it. This is needed as there are lots of power up/down sequences called in the paths that are also used in the PM itself. An example is found in hda_codec.c::codec_exec_verb(), where this can power up the codec while it may be called again in its power up sequence, too. The above works in most cases, but sometimes we really want to wait for the real power up. For example, the control element get/put may want explicit power up so that the value change is assured to reach to the hardware. Using the current snd_hdac_power_up(), however, results in a race, e.g. when it's called during the runtime suspend is being performed. In the worst case, as found in patch_ca0132.c, it can even lead to the deadlock because the code assumes the power up while it was skipped due to the check above. For dealing with such cases, this patch makes snd_hdac_power_up() and _down() to two variants: with and without in_pm flag check. The version with pm flag check is named as snd_hdac_power_up_pm() while the version without pm flag check is still kept as snd_hdac_power_up(). (Just because the usage of the former is fewer.) Then finally, the patch replaces each call potentially done in PM with the new _pm() variant. In theory, we can implement a unified version -- if we can distinguish the current context whether it's in the pm path. But such an implementation is cumbersome, so leave the code like this a bit messy way for now... Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=96271 Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/hda_codec.c8
-rw-r--r--sound/pci/hda/hda_codec.h2
-rw-r--r--sound/pci/hda/patch_ca0132.c12
-rw-r--r--sound/pci/hda/patch_hdmi.c4
4 files changed, 14 insertions, 12 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 16dfa1ed10dd..e70a7fb393dd 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -137,7 +137,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
137 return -1; 137 return -1;
138 138
139 again: 139 again:
140 snd_hda_power_up(codec); 140 snd_hda_power_up_pm(codec);
141 mutex_lock(&bus->core.cmd_mutex); 141 mutex_lock(&bus->core.cmd_mutex);
142 if (flags & HDA_RW_NO_RESPONSE_FALLBACK) 142 if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
143 bus->no_response_fallback = 1; 143 bus->no_response_fallback = 1;
@@ -145,7 +145,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
145 cmd, res); 145 cmd, res);
146 bus->no_response_fallback = 0; 146 bus->no_response_fallback = 0;
147 mutex_unlock(&bus->core.cmd_mutex); 147 mutex_unlock(&bus->core.cmd_mutex);
148 snd_hda_power_down(codec); 148 snd_hda_power_down_pm(codec);
149 if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { 149 if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) {
150 if (bus->response_reset) { 150 if (bus->response_reset) {
151 codec_dbg(codec, 151 codec_dbg(codec,
@@ -3951,7 +3951,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
3951 if (!(v & HDA_AMP_MUTE) && v > 0) { 3951 if (!(v & HDA_AMP_MUTE) && v > 0) {
3952 if (!check->power_on) { 3952 if (!check->power_on) {
3953 check->power_on = 1; 3953 check->power_on = 1;
3954 snd_hda_power_up(codec); 3954 snd_hda_power_up_pm(codec);
3955 } 3955 }
3956 return 1; 3956 return 1;
3957 } 3957 }
@@ -3959,7 +3959,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
3959 } 3959 }
3960 if (check->power_on) { 3960 if (check->power_on) {
3961 check->power_on = 0; 3961 check->power_on = 0;
3962 snd_hda_power_down(codec); 3962 snd_hda_power_down_pm(codec);
3963 } 3963 }
3964 return 0; 3964 return 0;
3965} 3965}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index acf868c6a785..9075ac28dc4b 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -508,7 +508,9 @@ const char *snd_hda_get_jack_location(u32 cfg);
508 * power saving 508 * power saving
509 */ 509 */
510#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) 510#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core)
511#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core)
511#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) 512#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core)
513#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core)
512#ifdef CONFIG_PM 514#ifdef CONFIG_PM
513void snd_hda_set_power_save(struct hda_bus *bus, int delay); 515void snd_hda_set_power_save(struct hda_bus *bus, int delay);
514void snd_hda_update_power_acct(struct hda_codec *codec); 516void snd_hda_update_power_acct(struct hda_codec *codec);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 5aff35a09fd4..4a4e7b282e4f 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -3131,7 +3131,7 @@ static int ca0132_select_out(struct hda_codec *codec)
3131 3131
3132 codec_dbg(codec, "ca0132_select_out\n"); 3132 codec_dbg(codec, "ca0132_select_out\n");
3133 3133
3134 snd_hda_power_up(codec); 3134 snd_hda_power_up_pm(codec);
3135 3135
3136 auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; 3136 auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
3137 3137
@@ -3215,7 +3215,7 @@ static int ca0132_select_out(struct hda_codec *codec)
3215 } 3215 }
3216 3216
3217exit: 3217exit:
3218 snd_hda_power_down(codec); 3218 snd_hda_power_down_pm(codec);
3219 3219
3220 return err < 0 ? err : 0; 3220 return err < 0 ? err : 0;
3221} 3221}
@@ -3293,7 +3293,7 @@ static int ca0132_select_mic(struct hda_codec *codec)
3293 3293
3294 codec_dbg(codec, "ca0132_select_mic\n"); 3294 codec_dbg(codec, "ca0132_select_mic\n");
3295 3295
3296 snd_hda_power_up(codec); 3296 snd_hda_power_up_pm(codec);
3297 3297
3298 auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; 3298 auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
3299 3299
@@ -3326,7 +3326,7 @@ static int ca0132_select_mic(struct hda_codec *codec)
3326 ca0132_effects_set(codec, VOICE_FOCUS, 0); 3326 ca0132_effects_set(codec, VOICE_FOCUS, 0);
3327 } 3327 }
3328 3328
3329 snd_hda_power_down(codec); 3329 snd_hda_power_down_pm(codec);
3330 3330
3331 return 0; 3331 return 0;
3332} 3332}
@@ -4546,7 +4546,7 @@ static int ca0132_init(struct hda_codec *codec)
4546 spec->dsp_state = DSP_DOWNLOAD_INIT; 4546 spec->dsp_state = DSP_DOWNLOAD_INIT;
4547 spec->curr_chip_addx = INVALID_CHIP_ADDRESS; 4547 spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
4548 4548
4549 snd_hda_power_up(codec); 4549 snd_hda_power_up_pm(codec);
4550 4550
4551 ca0132_init_unsol(codec); 4551 ca0132_init_unsol(codec);
4552 4552
@@ -4577,7 +4577,7 @@ static int ca0132_init(struct hda_codec *codec)
4577 4577
4578 snd_hda_jack_report_sync(codec); 4578 snd_hda_jack_report_sync(codec);
4579 4579
4580 snd_hda_power_down(codec); 4580 snd_hda_power_down_pm(codec);
4581 4581
4582 return 0; 4582 return 0;
4583} 4583}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ca0c05e1c42e..5f44f60a6389 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1545,7 +1545,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
1545 bool eld_changed = false; 1545 bool eld_changed = false;
1546 bool ret; 1546 bool ret;
1547 1547
1548 snd_hda_power_up(codec); 1548 snd_hda_power_up_pm(codec);
1549 present = snd_hda_pin_sense(codec, pin_nid); 1549 present = snd_hda_pin_sense(codec, pin_nid);
1550 1550
1551 mutex_lock(&per_pin->lock); 1551 mutex_lock(&per_pin->lock);
@@ -1631,7 +1631,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
1631 jack->block_report = !ret; 1631 jack->block_report = !ret;
1632 1632
1633 mutex_unlock(&per_pin->lock); 1633 mutex_unlock(&per_pin->lock);
1634 snd_hda_power_down(codec); 1634 snd_hda_power_down_pm(codec);
1635 return ret; 1635 return ret;
1636} 1636}
1637 1637