ALSA: hda - Fix mutex deadlock in HDMI codec driver

The commit ade49db337 ("ALSA: hda/hdmi - Allow audio component for
AMD/ATI and Nvidia HDMI") introduced the spec->pcm_lock mutex lock to
the whole generic_hdmi_init() function for avoiding the race with the
audio component registration.  However, this caused a dead lock when
the unsolicited event is handled without the audio component, as the
codec gets runtime-resumed in hdmi_present_sense() which is already
inside the spec->pcm_lock in its caller.

For avoiding this deadlock, add a new mutex only for the audio
component binding that is used in both generic_hdmi_init() and the
audio notifier registration where the jack callbacks are handled /
re-registered.

Fixes: ade49db337 ("ALSA: hda/hdmi - Allow audio component for AMD/ATI and Nvidia HDMI")
Reported-and-tested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://lore.kernel.org/r/s5himo7i89i.wl-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2019-10-29 21:41:20 +01:00
parent 1a7f60b9df
commit 302d5a80d2

View File

@ -145,6 +145,7 @@ struct hdmi_spec {
struct snd_array pins; /* struct hdmi_spec_per_pin */ struct snd_array pins; /* struct hdmi_spec_per_pin */
struct hdmi_pcm pcm_rec[16]; struct hdmi_pcm pcm_rec[16];
struct mutex pcm_lock; struct mutex pcm_lock;
struct mutex bind_lock; /* for audio component binding */
/* pcm_bitmap means which pcms have been assigned to pins*/ /* pcm_bitmap means which pcms have been assigned to pins*/
unsigned long pcm_bitmap; unsigned long pcm_bitmap;
int pcm_used; /* counter of pcm_rec[] */ int pcm_used; /* counter of pcm_rec[] */
@ -2258,7 +2259,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
int pin_idx; int pin_idx;
mutex_lock(&spec->pcm_lock); mutex_lock(&spec->bind_lock);
spec->use_jack_detect = !codec->jackpoll_interval; spec->use_jack_detect = !codec->jackpoll_interval;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@ -2275,7 +2276,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
snd_hda_jack_detect_enable_callback(codec, pin_nid, snd_hda_jack_detect_enable_callback(codec, pin_nid,
jack_callback); jack_callback);
} }
mutex_unlock(&spec->pcm_lock); mutex_unlock(&spec->bind_lock);
return 0; return 0;
} }
@ -2382,6 +2383,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
spec->ops = generic_standard_hdmi_ops; spec->ops = generic_standard_hdmi_ops;
spec->dev_num = 1; /* initialize to 1 */ spec->dev_num = 1; /* initialize to 1 */
mutex_init(&spec->pcm_lock); mutex_init(&spec->pcm_lock);
mutex_init(&spec->bind_lock);
snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.get_chmap = hdmi_get_chmap;
@ -2451,7 +2453,7 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
int i; int i;
spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops); spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
mutex_lock(&spec->pcm_lock); mutex_lock(&spec->bind_lock);
spec->use_acomp_notifier = use_acomp; spec->use_acomp_notifier = use_acomp;
spec->codec->relaxed_resume = use_acomp; spec->codec->relaxed_resume = use_acomp;
/* reprogram each jack detection logic depending on the notifier */ /* reprogram each jack detection logic depending on the notifier */
@ -2461,7 +2463,7 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
get_pin(spec, i)->pin_nid, get_pin(spec, i)->pin_nid,
use_acomp); use_acomp);
} }
mutex_unlock(&spec->pcm_lock); mutex_unlock(&spec->bind_lock);
} }
/* enable / disable the notifier via master bind / unbind */ /* enable / disable the notifier via master bind / unbind */