950-0008-drm-vc4-hdmi-Use-a-mutex-to-prevent-concurrent-frame.patch 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. From 5976ef1d81c8d474eddb55103f29a686f84f22f1 Mon Sep 17 00:00:00 2001
  2. From: Maxime Ripard <[email protected]>
  3. Date: Mon, 25 Oct 2021 16:11:09 +0200
  4. Subject: [PATCH] drm/vc4: hdmi: Use a mutex to prevent concurrent
  5. framework access
  6. The vc4 HDMI controller registers into the KMS, CEC and ALSA
  7. frameworks.
  8. However, no particular care is done to prevent the concurrent execution
  9. of different framework hooks from happening at the same time.
  10. In order to protect against that scenario, let's introduce a mutex that
  11. relevant ALSA and KMS hooks will need to take to prevent concurrent
  12. execution.
  13. CEC is left out at the moment though, since the .get_modes and .detect
  14. KMS hooks, when running cec_s_phys_addr_from_edid, can end up calling
  15. CEC's .adap_enable hook. This introduces some reentrancy that isn't easy
  16. to deal with properly.
  17. The CEC hooks also don't share much state with the rest of the driver:
  18. the registers are entirely separate, we don't share any variable, the
  19. only thing that can conflict is the CEC clock divider setup that can be
  20. affected by a mode set.
  21. However, after discussing it, it looks like CEC should be able to
  22. recover from this if it was to happen.
  23. Link: https://lore.kernel.org/r/[email protected]
  24. Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support")
  25. Acked-by: Daniel Vetter <[email protected]>
  26. Signed-off-by: Maxime Ripard <[email protected]>
  27. ---
  28. drivers/gpu/drm/vc4/vc4_hdmi.c | 118 +++++++++++++++++++++++++++++++--
  29. drivers/gpu/drm/vc4/vc4_hdmi.h | 14 ++++
  30. 2 files changed, 126 insertions(+), 6 deletions(-)
  31. --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
  32. +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
  33. @@ -192,6 +192,8 @@ vc4_hdmi_connector_detect(struct drm_con
  34. struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
  35. bool connected = false;
  36. + mutex_lock(&vc4_hdmi->mutex);
  37. +
  38. WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev));
  39. if (vc4_hdmi->hpd_gpio) {
  40. @@ -222,11 +224,13 @@ vc4_hdmi_connector_detect(struct drm_con
  41. vc4_hdmi_enable_scrambling(&vc4_hdmi->encoder.base.base);
  42. pm_runtime_put(&vc4_hdmi->pdev->dev);
  43. + mutex_unlock(&vc4_hdmi->mutex);
  44. return connector_status_connected;
  45. }
  46. cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
  47. pm_runtime_put(&vc4_hdmi->pdev->dev);
  48. + mutex_unlock(&vc4_hdmi->mutex);
  49. return connector_status_disconnected;
  50. }
  51. @@ -243,10 +247,14 @@ static int vc4_hdmi_connector_get_modes(
  52. int ret = 0;
  53. struct edid *edid;
  54. + mutex_lock(&vc4_hdmi->mutex);
  55. +
  56. edid = drm_get_edid(connector, vc4_hdmi->ddc);
  57. cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
  58. - if (!edid)
  59. - return -ENODEV;
  60. + if (!edid) {
  61. + ret = -ENODEV;
  62. + goto out;
  63. + }
  64. vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
  65. @@ -266,6 +274,9 @@ static int vc4_hdmi_connector_get_modes(
  66. }
  67. }
  68. +out:
  69. + mutex_unlock(&vc4_hdmi->mutex);
  70. +
  71. return ret;
  72. }
  73. @@ -482,6 +493,8 @@ static void vc4_hdmi_set_avi_infoframe(s
  74. union hdmi_infoframe frame;
  75. int ret;
  76. + lockdep_assert_held(&vc4_hdmi->mutex);
  77. +
  78. ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
  79. connector, mode);
  80. if (ret < 0) {
  81. @@ -533,6 +546,8 @@ static void vc4_hdmi_set_hdr_infoframe(s
  82. struct drm_connector_state *conn_state = connector->state;
  83. union hdmi_infoframe frame;
  84. + lockdep_assert_held(&vc4_hdmi->mutex);
  85. +
  86. if (!vc4_hdmi->variant->supports_hdr)
  87. return;
  88. @@ -549,6 +564,8 @@ static void vc4_hdmi_set_infoframes(stru
  89. {
  90. struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
  91. + lockdep_assert_held(&vc4_hdmi->mutex);
  92. +
  93. vc4_hdmi_set_avi_infoframe(encoder);
  94. vc4_hdmi_set_spd_infoframe(encoder);
  95. /*
  96. @@ -568,6 +585,8 @@ static bool vc4_hdmi_supports_scrambling
  97. struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
  98. struct drm_display_info *display = &vc4_hdmi->connector.display_info;
  99. + lockdep_assert_held(&vc4_hdmi->mutex);
  100. +
  101. if (!vc4_encoder->hdmi_monitor)
  102. return false;
  103. @@ -586,6 +605,8 @@ static void vc4_hdmi_enable_scrambling(s
  104. struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
  105. unsigned long flags;
  106. + lockdep_assert_held(&vc4_hdmi->mutex);
  107. +
  108. if (!vc4_hdmi_supports_scrambling(encoder, mode))
  109. return;
  110. @@ -655,6 +676,8 @@ static void vc4_hdmi_encoder_post_crtc_d
  111. struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
  112. unsigned long flags;
  113. + mutex_lock(&vc4_hdmi->mutex);
  114. +
  115. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  116. HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
  117. @@ -671,6 +694,8 @@ static void vc4_hdmi_encoder_post_crtc_d
  118. spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
  119. vc4_hdmi_disable_scrambling(encoder);
  120. +
  121. + mutex_unlock(&vc4_hdmi->mutex);
  122. }
  123. static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
  124. @@ -680,6 +705,8 @@ static void vc4_hdmi_encoder_post_crtc_p
  125. unsigned long flags;
  126. int ret;
  127. + mutex_lock(&vc4_hdmi->mutex);
  128. +
  129. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  130. HDMI_WRITE(HDMI_VID_CTL,
  131. HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
  132. @@ -694,6 +721,8 @@ static void vc4_hdmi_encoder_post_crtc_p
  133. ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
  134. if (ret < 0)
  135. DRM_ERROR("Failed to release power domain: %d\n", ret);
  136. +
  137. + mutex_unlock(&vc4_hdmi->mutex);
  138. }
  139. static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
  140. @@ -996,6 +1025,8 @@ static void vc4_hdmi_encoder_pre_crtc_co
  141. unsigned long flags;
  142. int ret;
  143. + mutex_lock(&vc4_hdmi->mutex);
  144. +
  145. /*
  146. * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
  147. * be faster than pixel clock, infinitesimally faster, tested in
  148. @@ -1016,13 +1047,13 @@ static void vc4_hdmi_encoder_pre_crtc_co
  149. ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
  150. if (ret) {
  151. DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
  152. - return;
  153. + goto out;
  154. }
  155. ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
  156. if (ret < 0) {
  157. DRM_ERROR("Failed to retain power domain: %d\n", ret);
  158. - return;
  159. + goto out;
  160. }
  161. ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
  162. @@ -1074,13 +1105,16 @@ static void vc4_hdmi_encoder_pre_crtc_co
  163. if (vc4_hdmi->variant->set_timings)
  164. vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
  165. + mutex_unlock(&vc4_hdmi->mutex);
  166. +
  167. return;
  168. err_disable_pixel_clock:
  169. clk_disable_unprepare(vc4_hdmi->pixel_clock);
  170. err_put_runtime_pm:
  171. pm_runtime_put(&vc4_hdmi->pdev->dev);
  172. -
  173. +out:
  174. + mutex_unlock(&vc4_hdmi->mutex);
  175. return;
  176. }
  177. @@ -1092,6 +1126,8 @@ static void vc4_hdmi_encoder_pre_crtc_en
  178. struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
  179. unsigned long flags;
  180. + mutex_lock(&vc4_hdmi->mutex);
  181. +
  182. if (vc4_encoder->hdmi_monitor &&
  183. drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
  184. if (vc4_hdmi->variant->csc_setup)
  185. @@ -1108,6 +1144,8 @@ static void vc4_hdmi_encoder_pre_crtc_en
  186. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  187. HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
  188. spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
  189. +
  190. + mutex_unlock(&vc4_hdmi->mutex);
  191. }
  192. static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
  193. @@ -1121,6 +1159,8 @@ static void vc4_hdmi_encoder_post_crtc_e
  194. unsigned long flags;
  195. int ret;
  196. + mutex_lock(&vc4_hdmi->mutex);
  197. +
  198. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  199. HDMI_WRITE(HDMI_VID_CTL,
  200. @@ -1180,6 +1220,8 @@ static void vc4_hdmi_encoder_post_crtc_e
  201. vc4_hdmi_recenter_fifo(vc4_hdmi);
  202. vc4_hdmi_enable_scrambling(encoder);
  203. +
  204. + mutex_unlock(&vc4_hdmi->mutex);
  205. }
  206. static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
  207. @@ -1323,6 +1365,7 @@ static void vc4_hdmi_set_n_cts(struct vc
  208. u32 n, cts;
  209. u64 tmp;
  210. + lockdep_assert_held(&vc4_hdmi->mutex);
  211. lockdep_assert_held(&vc4_hdmi->hw_lock);
  212. n = 128 * samplerate / 1000;
  213. @@ -1356,13 +1399,17 @@ static int vc4_hdmi_audio_startup(struct
  214. struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
  215. unsigned long flags;
  216. + mutex_lock(&vc4_hdmi->mutex);
  217. +
  218. /*
  219. * If the HDMI encoder hasn't probed, or the encoder is
  220. * currently in DVI mode, treat the codec dai as missing.
  221. */
  222. if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
  223. - VC4_HDMI_RAM_PACKET_ENABLE))
  224. + VC4_HDMI_RAM_PACKET_ENABLE)) {
  225. + mutex_unlock(&vc4_hdmi->mutex);
  226. return -ENODEV;
  227. + }
  228. vc4_hdmi->audio.streaming = true;
  229. @@ -1378,6 +1425,8 @@ static int vc4_hdmi_audio_startup(struct
  230. if (vc4_hdmi->variant->phy_rng_enable)
  231. vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
  232. + mutex_unlock(&vc4_hdmi->mutex);
  233. +
  234. return 0;
  235. }
  236. @@ -1388,6 +1437,8 @@ static void vc4_hdmi_audio_reset(struct
  237. unsigned long flags;
  238. int ret;
  239. + lockdep_assert_held(&vc4_hdmi->mutex);
  240. +
  241. vc4_hdmi->audio.streaming = false;
  242. ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false);
  243. if (ret)
  244. @@ -1407,6 +1458,8 @@ static void vc4_hdmi_audio_shutdown(stru
  245. struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
  246. unsigned long flags;
  247. + mutex_lock(&vc4_hdmi->mutex);
  248. +
  249. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  250. HDMI_WRITE(HDMI_MAI_CTL,
  251. @@ -1421,6 +1474,8 @@ static void vc4_hdmi_audio_shutdown(stru
  252. vc4_hdmi->audio.streaming = false;
  253. vc4_hdmi_audio_reset(vc4_hdmi);
  254. +
  255. + mutex_unlock(&vc4_hdmi->mutex);
  256. }
  257. static int sample_rate_to_mai_fmt(int samplerate)
  258. @@ -1479,6 +1534,8 @@ static int vc4_hdmi_audio_prepare(struct
  259. dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
  260. sample_rate, params->sample_width, channels);
  261. + mutex_lock(&vc4_hdmi->mutex);
  262. +
  263. vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
  264. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  265. @@ -1533,6 +1590,8 @@ static int vc4_hdmi_audio_prepare(struct
  266. memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea));
  267. vc4_hdmi_set_audio_infoframe(encoder);
  268. + mutex_unlock(&vc4_hdmi->mutex);
  269. +
  270. return 0;
  271. }
  272. @@ -1575,7 +1634,9 @@ static int vc4_hdmi_audio_get_eld(struct
  273. struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
  274. struct drm_connector *connector = &vc4_hdmi->connector;
  275. + mutex_lock(&vc4_hdmi->mutex);
  276. memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
  277. + mutex_unlock(&vc4_hdmi->mutex);
  278. return 0;
  279. }
  280. @@ -1912,6 +1973,17 @@ static int vc4_hdmi_cec_enable(struct ce
  281. u32 val;
  282. int ret;
  283. + /*
  284. + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
  285. + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
  286. + * .detect or .get_modes might call .adap_enable, which leads to this
  287. + * function being called with that mutex held.
  288. + *
  289. + * Concurrency is not an issue for the moment since we don't share any
  290. + * state with KMS, so we can ignore the lock for now, but we need to
  291. + * keep it in mind if we were to change that assumption.
  292. + */
  293. +
  294. ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
  295. if (ret)
  296. return ret;
  297. @@ -1958,6 +2030,17 @@ static int vc4_hdmi_cec_disable(struct c
  298. struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
  299. unsigned long flags;
  300. + /*
  301. + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
  302. + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
  303. + * .detect or .get_modes might call .adap_enable, which leads to this
  304. + * function being called with that mutex held.
  305. + *
  306. + * Concurrency is not an issue for the moment since we don't share any
  307. + * state with KMS, so we can ignore the lock for now, but we need to
  308. + * keep it in mind if we were to change that assumption.
  309. + */
  310. +
  311. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  312. if (!vc4_hdmi->variant->external_irq_controller)
  313. @@ -1986,6 +2069,17 @@ static int vc4_hdmi_cec_adap_log_addr(st
  314. struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
  315. unsigned long flags;
  316. + /*
  317. + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
  318. + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
  319. + * .detect or .get_modes might call .adap_enable, which leads to this
  320. + * function being called with that mutex held.
  321. + *
  322. + * Concurrency is not an issue for the moment since we don't share any
  323. + * state with KMS, so we can ignore the lock for now, but we need to
  324. + * keep it in mind if we were to change that assumption.
  325. + */
  326. +
  327. spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
  328. HDMI_WRITE(HDMI_CEC_CNTRL_1,
  329. (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
  330. @@ -2004,6 +2098,17 @@ static int vc4_hdmi_cec_adap_transmit(st
  331. u32 val;
  332. unsigned int i;
  333. + /*
  334. + * NOTE: This function should really take vc4_hdmi->mutex, but doing so
  335. + * results in a reentrancy since cec_s_phys_addr_from_edid() called in
  336. + * .detect or .get_modes might call .adap_enable, which leads to this
  337. + * function being called with that mutex held.
  338. + *
  339. + * Concurrency is not an issue for the moment since we don't share any
  340. + * state with KMS, so we can ignore the lock for now, but we need to
  341. + * keep it in mind if we were to change that assumption.
  342. + */
  343. +
  344. if (msg->len > 16) {
  345. drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
  346. return -ENOMEM;
  347. @@ -2360,6 +2465,7 @@ static int vc4_hdmi_bind(struct device *
  348. vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
  349. if (!vc4_hdmi)
  350. return -ENOMEM;
  351. + mutex_init(&vc4_hdmi->mutex);
  352. spin_lock_init(&vc4_hdmi->hw_lock);
  353. INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
  354. --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
  355. +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
  356. @@ -184,6 +184,20 @@ struct vc4_hdmi {
  357. * @hw_lock: Spinlock protecting device register access.
  358. */
  359. spinlock_t hw_lock;
  360. +
  361. + /**
  362. + * @mutex: Mutex protecting the driver access across multiple
  363. + * frameworks (KMS, ALSA).
  364. + *
  365. + * NOTE: While supported, CEC has been left out since
  366. + * cec_s_phys_addr_from_edid() might call .adap_enable and lead to a
  367. + * reentrancy issue between .get_modes (or .detect) and .adap_enable.
  368. + * Since we don't share any state between the CEC hooks and KMS', it's
  369. + * not a big deal. The only trouble might come from updating the CEC
  370. + * clock divider which might be affected by a modeset, but CEC should
  371. + * be resilient to that.
  372. + */
  373. + struct mutex mutex;
  374. };
  375. static inline struct vc4_hdmi *