Blob Blame History Raw
From 58d043690deb7c145f8a0bd07370dcaefa97a733 Mon Sep 17 00:00:00 2001
From: Maxime Ripard <maxime@cerno.tech>
Date: Tue, 27 Oct 2020 11:15:58 +0100
Subject: drm/vc4: hdmi: Avoid sleeping in atomic context
Git-commit: 58d043690deb7c145f8a0bd07370dcaefa97a733
Patch-mainline: v5.10-rc2
References: bsc#1129770

When running the trigger hook, ALSA by default will take a spinlock, and
thus will run the trigger hook in atomic context.

However, our HDMI driver will send the infoframes as part of the trigger
hook, and part of that process is to wait for a bit to be cleared for up to
100ms. To be nicer to the system, that wait has some usleep_range that
interact poorly with the atomic context.

There's several ways we can fix this, but the more obvious one is to make
ALSA take a mutex instead by setting the nonatomic flag on the DAI link.
That doesn't work though, since now the cyclic callback installed by the
dmaengine helpers in ALSA will take a mutex, while that callback is run by
dmaengine's virt-chan code in a tasklet where sleeping is not allowed
either.

Given the delay we need to poll the bit for, changing the usleep_range for
a udelay and keep running it from a context where interrupts are disabled
is not really a good option either.

However, we can move the infoframe setup code in the hw_params hook, like
is usually done in other HDMI controllers, that isn't protected by a
spinlock and thus where we can sleep. Infoframes will be sent on a regular
basis anyway, and since hw_params is where the audio parameters that end up
in the infoframes are setup, this also makes a bit more sense.

Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support")
Suggested-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20201027101558.427256-1-maxime@cerno.tech
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -931,7 +931,6 @@ static int vc4_hdmi_audio_trigger(struct
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		vc4_hdmi_set_audio_infoframe(encoder);
 		HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
 			   HDMI_READ(VC4_HDMI_TX_PHY_CTL0) &
 			   ~VC4_HDMI_TX_PHY_RNG_PWRDN);
@@ -981,10 +980,13 @@ static int vc4_hdmi_audio_eld_ctl_get(st
 {
 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 	struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
+	struct drm_encoder *encoder = hdmi->encoder;
 
 	memcpy(ucontrol->value.bytes.data, hdmi->connector->eld,
 	       sizeof(hdmi->connector->eld));
 
+	vc4_hdmi_set_audio_infoframe(encoder);
+
 	return 0;
 }