1 From 5976ef1d81c8d474eddb55103f29a686f84f22f1 Mon Sep 17 00:00:00 2001
2 From: Maxime Ripard <maxime@cerno.tech>
3 Date: Mon, 25 Oct 2021 16:11:09 +0200
4 Subject: [PATCH] drm/vc4: hdmi: Use a mutex to prevent concurrent
7 The vc4 HDMI controller registers into the KMS, CEC and ALSA
10 However, no particular care is done to prevent the concurrent execution
11 of different framework hooks from happening at the same time.
13 In order to protect against that scenario, let's introduce a mutex that
14 relevant ALSA and KMS hooks will need to take to prevent concurrent
17 CEC is left out at the moment though, since the .get_modes and .detect
18 KMS hooks, when running cec_s_phys_addr_from_edid, can end up calling
19 CEC's .adap_enable hook. This introduces some reentrancy that isn't easy
20 to deal with properly.
22 The CEC hooks also don't share much state with the rest of the driver:
23 the registers are entirely separate, we don't share any variable, the
24 only thing that can conflict is the CEC clock divider setup that can be
25 affected by a mode set.
27 However, after discussing it, it looks like CEC should be able to
28 recover from this if it was to happen.
30 Link: https://lore.kernel.org/r/20211025141113.702757-6-maxime@cerno.tech
31 Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support")
32 Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
33 Signed-off-by: Maxime Ripard <maxime@cerno.tech>
35 drivers/gpu/drm/vc4/vc4_hdmi.c | 118 +++++++++++++++++++++++++++++++--
36 drivers/gpu/drm/vc4/vc4_hdmi.h | 14 ++++
37 2 files changed, 126 insertions(+), 6 deletions(-)
39 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
40 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
41 @@ -188,6 +188,8 @@ vc4_hdmi_connector_detect(struct drm_con
42 struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
43 bool connected = false;
45 + mutex_lock(&vc4_hdmi->mutex);
47 WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev));
49 if (vc4_hdmi->hpd_gpio) {
50 @@ -218,11 +220,13 @@ vc4_hdmi_connector_detect(struct drm_con
52 vc4_hdmi_enable_scrambling(&vc4_hdmi->encoder.base.base);
53 pm_runtime_put(&vc4_hdmi->pdev->dev);
54 + mutex_unlock(&vc4_hdmi->mutex);
55 return connector_status_connected;
58 cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
59 pm_runtime_put(&vc4_hdmi->pdev->dev);
60 + mutex_unlock(&vc4_hdmi->mutex);
61 return connector_status_disconnected;
64 @@ -239,10 +243,14 @@ static int vc4_hdmi_connector_get_modes(
68 + mutex_lock(&vc4_hdmi->mutex);
70 edid = drm_get_edid(connector, vc4_hdmi->ddc);
71 cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
79 vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
81 @@ -262,6 +270,9 @@ static int vc4_hdmi_connector_get_modes(
86 + mutex_unlock(&vc4_hdmi->mutex);
91 @@ -478,6 +489,8 @@ static void vc4_hdmi_set_avi_infoframe(s
92 union hdmi_infoframe frame;
95 + lockdep_assert_held(&vc4_hdmi->mutex);
97 ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
100 @@ -529,6 +542,8 @@ static void vc4_hdmi_set_hdr_infoframe(s
101 struct drm_connector_state *conn_state = connector->state;
102 union hdmi_infoframe frame;
104 + lockdep_assert_held(&vc4_hdmi->mutex);
106 if (!vc4_hdmi->variant->supports_hdr)
109 @@ -545,6 +560,8 @@ static void vc4_hdmi_set_infoframes(stru
111 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
113 + lockdep_assert_held(&vc4_hdmi->mutex);
115 vc4_hdmi_set_avi_infoframe(encoder);
116 vc4_hdmi_set_spd_infoframe(encoder);
118 @@ -564,6 +581,8 @@ static bool vc4_hdmi_supports_scrambling
119 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
120 struct drm_display_info *display = &vc4_hdmi->connector.display_info;
122 + lockdep_assert_held(&vc4_hdmi->mutex);
124 if (!vc4_encoder->hdmi_monitor)
127 @@ -582,6 +601,8 @@ static void vc4_hdmi_enable_scrambling(s
128 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
131 + lockdep_assert_held(&vc4_hdmi->mutex);
133 if (!vc4_hdmi_supports_scrambling(encoder, mode))
136 @@ -651,6 +672,8 @@ static void vc4_hdmi_encoder_post_crtc_d
137 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
140 + mutex_lock(&vc4_hdmi->mutex);
142 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
144 HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
145 @@ -667,6 +690,8 @@ static void vc4_hdmi_encoder_post_crtc_d
146 spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
148 vc4_hdmi_disable_scrambling(encoder);
150 + mutex_unlock(&vc4_hdmi->mutex);
153 static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
154 @@ -676,6 +701,8 @@ static void vc4_hdmi_encoder_post_crtc_p
158 + mutex_lock(&vc4_hdmi->mutex);
160 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
161 HDMI_WRITE(HDMI_VID_CTL,
162 HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
163 @@ -690,6 +717,8 @@ static void vc4_hdmi_encoder_post_crtc_p
164 ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
166 DRM_ERROR("Failed to release power domain: %d\n", ret);
168 + mutex_unlock(&vc4_hdmi->mutex);
171 static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
172 @@ -986,6 +1015,8 @@ static void vc4_hdmi_encoder_pre_crtc_co
176 + mutex_lock(&vc4_hdmi->mutex);
179 * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
180 * be faster than pixel clock, infinitesimally faster, tested in
181 @@ -1006,13 +1037,13 @@ static void vc4_hdmi_encoder_pre_crtc_co
182 ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
184 DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
189 ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
191 DRM_ERROR("Failed to retain power domain: %d\n", ret);
196 ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
197 @@ -1064,13 +1095,16 @@ static void vc4_hdmi_encoder_pre_crtc_co
198 if (vc4_hdmi->variant->set_timings)
199 vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
201 + mutex_unlock(&vc4_hdmi->mutex);
205 err_disable_pixel_clock:
206 clk_disable_unprepare(vc4_hdmi->pixel_clock);
208 pm_runtime_put(&vc4_hdmi->pdev->dev);
211 + mutex_unlock(&vc4_hdmi->mutex);
215 @@ -1082,6 +1116,8 @@ static void vc4_hdmi_encoder_pre_crtc_en
216 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
219 + mutex_lock(&vc4_hdmi->mutex);
221 if (vc4_encoder->hdmi_monitor &&
222 drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
223 if (vc4_hdmi->variant->csc_setup)
224 @@ -1098,6 +1134,8 @@ static void vc4_hdmi_encoder_pre_crtc_en
225 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
226 HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
227 spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
229 + mutex_unlock(&vc4_hdmi->mutex);
232 static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
233 @@ -1111,6 +1149,8 @@ static void vc4_hdmi_encoder_post_crtc_e
237 + mutex_lock(&vc4_hdmi->mutex);
239 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
241 HDMI_WRITE(HDMI_VID_CTL,
242 @@ -1170,6 +1210,8 @@ static void vc4_hdmi_encoder_post_crtc_e
244 vc4_hdmi_recenter_fifo(vc4_hdmi);
245 vc4_hdmi_enable_scrambling(encoder);
247 + mutex_unlock(&vc4_hdmi->mutex);
250 static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
251 @@ -1313,6 +1355,7 @@ static void vc4_hdmi_set_n_cts(struct vc
255 + lockdep_assert_held(&vc4_hdmi->mutex);
256 lockdep_assert_held(&vc4_hdmi->hw_lock);
258 n = 128 * samplerate / 1000;
259 @@ -1346,13 +1389,17 @@ static int vc4_hdmi_audio_startup(struct
260 struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
263 + mutex_lock(&vc4_hdmi->mutex);
266 * If the HDMI encoder hasn't probed, or the encoder is
267 * currently in DVI mode, treat the codec dai as missing.
269 if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
270 - VC4_HDMI_RAM_PACKET_ENABLE))
271 + VC4_HDMI_RAM_PACKET_ENABLE)) {
272 + mutex_unlock(&vc4_hdmi->mutex);
276 vc4_hdmi->audio.streaming = true;
278 @@ -1368,6 +1415,8 @@ static int vc4_hdmi_audio_startup(struct
279 if (vc4_hdmi->variant->phy_rng_enable)
280 vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
282 + mutex_unlock(&vc4_hdmi->mutex);
287 @@ -1378,6 +1427,8 @@ static void vc4_hdmi_audio_reset(struct
291 + lockdep_assert_held(&vc4_hdmi->mutex);
293 vc4_hdmi->audio.streaming = false;
294 ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false);
296 @@ -1397,6 +1448,8 @@ static void vc4_hdmi_audio_shutdown(stru
297 struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
300 + mutex_lock(&vc4_hdmi->mutex);
302 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
304 HDMI_WRITE(HDMI_MAI_CTL,
305 @@ -1411,6 +1464,8 @@ static void vc4_hdmi_audio_shutdown(stru
307 vc4_hdmi->audio.streaming = false;
308 vc4_hdmi_audio_reset(vc4_hdmi);
310 + mutex_unlock(&vc4_hdmi->mutex);
313 static int sample_rate_to_mai_fmt(int samplerate)
314 @@ -1469,6 +1524,8 @@ static int vc4_hdmi_audio_prepare(struct
315 dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
316 sample_rate, params->sample_width, channels);
318 + mutex_lock(&vc4_hdmi->mutex);
320 vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
322 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
323 @@ -1523,6 +1580,8 @@ static int vc4_hdmi_audio_prepare(struct
324 memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea));
325 vc4_hdmi_set_audio_infoframe(encoder);
327 + mutex_unlock(&vc4_hdmi->mutex);
332 @@ -1565,7 +1624,9 @@ static int vc4_hdmi_audio_get_eld(struct
333 struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
334 struct drm_connector *connector = &vc4_hdmi->connector;
336 + mutex_lock(&vc4_hdmi->mutex);
337 memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
338 + mutex_unlock(&vc4_hdmi->mutex);
342 @@ -1902,6 +1963,17 @@ static int vc4_hdmi_cec_enable(struct ce
347 + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
348 + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
349 + * .detect or .get_modes might call .adap_enable, which leads to this
350 + * function being called with that mutex held.
352 + * Concurrency is not an issue for the moment since we don't share any
353 + * state with KMS, so we can ignore the lock for now, but we need to
354 + * keep it in mind if we were to change that assumption.
357 ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
360 @@ -1948,6 +2020,17 @@ static int vc4_hdmi_cec_disable(struct c
361 struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
365 + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
366 + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
367 + * .detect or .get_modes might call .adap_enable, which leads to this
368 + * function being called with that mutex held.
370 + * Concurrency is not an issue for the moment since we don't share any
371 + * state with KMS, so we can ignore the lock for now, but we need to
372 + * keep it in mind if we were to change that assumption.
375 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
377 if (!vc4_hdmi->variant->external_irq_controller)
378 @@ -1976,6 +2059,17 @@ static int vc4_hdmi_cec_adap_log_addr(st
379 struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
383 + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
384 + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
385 + * .detect or .get_modes might call .adap_enable, which leads to this
386 + * function being called with that mutex held.
388 + * Concurrency is not an issue for the moment since we don't share any
389 + * state with KMS, so we can ignore the lock for now, but we need to
390 + * keep it in mind if we were to change that assumption.
393 spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
394 HDMI_WRITE(HDMI_CEC_CNTRL_1,
395 (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
396 @@ -1994,6 +2088,17 @@ static int vc4_hdmi_cec_adap_transmit(st
401 + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
402 + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
403 + * .detect or .get_modes might call .adap_enable, which leads to this
404 + * function being called with that mutex held.
406 + * Concurrency is not an issue for the moment since we don't share any
407 + * state with KMS, so we can ignore the lock for now, but we need to
408 + * keep it in mind if we were to change that assumption.
412 drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
414 @@ -2349,6 +2454,7 @@ static int vc4_hdmi_bind(struct device *
415 vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
418 + mutex_init(&vc4_hdmi->mutex);
419 spin_lock_init(&vc4_hdmi->hw_lock);
420 INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
422 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
423 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
424 @@ -184,6 +184,20 @@ struct vc4_hdmi {
425 * @hw_lock: Spinlock protecting device register access.
430 + * @mutex: Mutex protecting the driver access across multiple
431 + * frameworks (KMS, ALSA).
433 + * NOTE: While supported, CEC has been left out since
434 + * cec_s_phys_addr_from_edid() might call .adap_enable and lead to a
435 + * reentrancy issue between .get_modes (or .detect) and .adap_enable.
436 + * Since we don't share any state between the CEC hooks and KMS', it's
437 + * not a big deal. The only trouble might come from updating the CEC
438 + * clock divider which might be affected by a modeset, but CEC should
439 + * be resilient to that.
441 + struct mutex mutex;
444 static inline struct vc4_hdmi *