Commit aaa23f86 authored by Chris Wilson's avatar Chris Wilson Committed by Takashi Iwai

ALSA: hda - Handle pm failure during hotplug

Obtaining the runtime pm wakeref can fail, especially in a hotplug
scenario where i915.ko has been unloaded. If we do not catch the
failure, we end up with an unbalanced pm.

v2 additions by tiwai:
hdmi_present_sense() checks the return value and handle only a
negative error case and bails out only if it's really still suspended.
Also, snd_hda_power_down() is called at the error path so that the
refcount is balanced.

Along with it, the spec->pcm_lock is taken outside
hdmi_present_sense() in the caller side, so that it won't cause
deadlock at reentrace via runtime resume.

v3 fix by tiwai:
Missing linux/pm_runtime.h is included.

References: 222bde03 ("ALSA: hda - Fix mutex deadlock at HDMI/DP hotplug")
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent c9a4c638
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
...@@ -764,8 +765,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, ...@@ -764,8 +765,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
if (pin_idx < 0) if (pin_idx < 0)
return; return;
mutex_lock(&spec->pcm_lock);
if (hdmi_present_sense(get_pin(spec, pin_idx), 1)) if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
snd_hda_jack_report_sync(codec); snd_hda_jack_report_sync(codec);
mutex_unlock(&spec->pcm_lock);
} }
static void jack_callback(struct hda_codec *codec, static void jack_callback(struct hda_codec *codec,
...@@ -1628,21 +1631,23 @@ static void sync_eld_via_acomp(struct hda_codec *codec, ...@@ -1628,21 +1631,23 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
{ {
struct hda_codec *codec = per_pin->codec; struct hda_codec *codec = per_pin->codec;
struct hdmi_spec *spec = codec->spec;
int ret; int ret;
/* no temporary power up/down needed for component notifier */ /* no temporary power up/down needed for component notifier */
if (!codec_has_acomp(codec)) if (!codec_has_acomp(codec)) {
snd_hda_power_up_pm(codec); ret = snd_hda_power_up_pm(codec);
if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec))) {
snd_hda_power_down_pm(codec);
return false;
}
}
mutex_lock(&spec->pcm_lock);
if (codec_has_acomp(codec)) { if (codec_has_acomp(codec)) {
sync_eld_via_acomp(codec, per_pin); sync_eld_via_acomp(codec, per_pin);
ret = false; /* don't call snd_hda_jack_report_sync() */ ret = false; /* don't call snd_hda_jack_report_sync() */
} else { } else {
ret = hdmi_present_sense_via_verbs(per_pin, repoll); ret = hdmi_present_sense_via_verbs(per_pin, repoll);
} }
mutex_unlock(&spec->pcm_lock);
if (!codec_has_acomp(codec)) if (!codec_has_acomp(codec))
snd_hda_power_down_pm(codec); snd_hda_power_down_pm(codec);
...@@ -1654,12 +1659,16 @@ static void hdmi_repoll_eld(struct work_struct *work) ...@@ -1654,12 +1659,16 @@ static void hdmi_repoll_eld(struct work_struct *work)
{ {
struct hdmi_spec_per_pin *per_pin = struct hdmi_spec_per_pin *per_pin =
container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
struct hda_codec *codec = per_pin->codec;
struct hdmi_spec *spec = codec->spec;
if (per_pin->repoll_count++ > 6) if (per_pin->repoll_count++ > 6)
per_pin->repoll_count = 0; per_pin->repoll_count = 0;
mutex_lock(&spec->pcm_lock);
if (hdmi_present_sense(per_pin, per_pin->repoll_count)) if (hdmi_present_sense(per_pin, per_pin->repoll_count))
snd_hda_jack_report_sync(per_pin->codec); snd_hda_jack_report_sync(per_pin->codec);
mutex_unlock(&spec->pcm_lock);
} }
static void intel_haswell_fixup_connect_list(struct hda_codec *codec, static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment