aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2022-05-16 23:40:32 +0200
committerÁlvaro Fernández Rojas <noltari@gmail.com>2022-05-17 15:11:22 +0200
commit20ea6adbf199097c4f5f591ffee088340630dae4 (patch)
treed6719d95e136611a1c25bbf7789652d6d402779d /target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch
parentbca05bd072180dc38ef740b37ded9572a6db1981 (diff)
downloadupstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.gz
upstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.bz2
upstream-20ea6adbf199097c4f5f591ffee088340630dae4.zip
bcm27xx: add support for linux v5.15
Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch907
1 files changed, 907 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch b/target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch
new file mode 100644
index 0000000000..64b97d8962
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch
@@ -0,0 +1,907 @@
+From d91a953904e1aeddf24a95af40fc1ae7ba2319fd Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 25 Oct 2021 16:11:08 +0200
+Subject: [PATCH] drm/vc4: hdmi: Add a spinlock to protect register
+ access
+
+The vc4 HDMI driver has multiple path shared between the CEC, ALSA and
+KMS frameworks, plus two interrupt handlers (CEC and hotplug) that will
+read and modify a number of registers.
+
+Even though not bug has been reported so far, it's definitely unsafe, so
+let's just add a spinlock to protect the register access of the HDMI
+controller.
+
+Link: https://lore.kernel.org/r/20211025141113.702757-5-maxime@cerno.tech
+Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.")
+Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 202 ++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 5 +
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 37 +++++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 2 +
+ 4 files changed, 236 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -118,6 +118,10 @@ static int vc4_hdmi_debugfs_regs(struct
+
+ static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST);
+ udelay(1);
+ HDMI_WRITE(HDMI_M_CTL, 0);
+@@ -129,24 +133,36 @@ static void vc4_hdmi_reset(struct vc4_hd
+ VC4_HDMI_SW_RESET_FORMAT_DETECT);
+
+ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
+ reset_control_reset(vc4_hdmi->reset);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_DVP_CTL, 0);
+
+ HDMI_WRITE(HDMI_CLOCK_STOP,
+ HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
++ unsigned long flags;
+ u16 clk_cnt;
+ u32 value;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ value = HDMI_READ(HDMI_CEC_CNTRL_1);
+ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+
+@@ -154,9 +170,11 @@ static void vc4_hdmi_cec_update_clk_div(
+ * Set the clock divider: the hsm_clock rate and this divider
+ * setting will give a 40 kHz CEC clock.
+ */
+- clk_cnt = clk_get_rate(vc4_hdmi->cec_clock) / CEC_CLOCK_FREQ;
++ clk_cnt = cec_rate / CEC_CLOCK_FREQ;
+ value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+ #else
+ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
+@@ -175,8 +193,16 @@ vc4_hdmi_connector_detect(struct drm_con
+ if (vc4_hdmi->hpd_gpio) {
+ if (gpiod_get_value_cansleep(vc4_hdmi->hpd_gpio))
+ connected = true;
+- } else if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) {
+- connected = true;
++ } else {
++ unsigned long flags;
++ u32 hotplug;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++ hotplug = HDMI_READ(HDMI_HOTPLUG);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
++ if (hotplug & VC4_HDMI_HOTPLUG_CONNECTED)
++ connected = true;
+ }
+
+ if (connected) {
+@@ -370,9 +396,12 @@ static int vc4_hdmi_stop_packet(struct d
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ u32 packet_id = type - 0x80;
++ unsigned long flags;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ if (!poll)
+ return 0;
+@@ -392,6 +421,7 @@ static void vc4_hdmi_write_infoframe(str
+ void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
+ ram_packet_start->reg);
+ uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
++ unsigned long flags;
+ ssize_t len, i;
+ int ret;
+
+@@ -409,6 +439,8 @@ static void vc4_hdmi_write_infoframe(str
+ return;
+ }
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ for (i = 0; i < len; i += 7) {
+ writel(buffer[i + 0] << 0 |
+ buffer[i + 1] << 8 |
+@@ -426,6 +458,9 @@ static void vc4_hdmi_write_infoframe(str
+
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) &
+ BIT(packet_id)), 100);
+ if (ret)
+@@ -545,6 +580,7 @@ static void vc4_hdmi_enable_scrambling(s
+ {
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ unsigned long flags;
+
+ if (!vc4_hdmi_supports_scrambling(encoder, mode))
+ return;
+@@ -555,8 +591,10 @@ static void vc4_hdmi_enable_scrambling(s
+ drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
+ drm_scdc_set_scrambling(vc4_hdmi->ddc, true);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) |
+ VC5_HDMI_SCRAMBLER_CTL_ENABLE);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work,
+ msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
+@@ -566,6 +604,7 @@ static void vc4_hdmi_disable_scrambling(
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
++ unsigned long flags;
+
+ /*
+ * At boot, encoder->crtc will be NULL. Since we don't know the
+@@ -581,8 +620,10 @@ static void vc4_hdmi_disable_scrambling(
+ if (delayed_work_pending(&vc4_hdmi->scrambling_work))
+ cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
+ ~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ drm_scdc_set_scrambling(vc4_hdmi->ddc, false);
+ drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false);
+@@ -608,15 +649,23 @@ static void vc4_hdmi_encoder_post_crtc_d
+ struct drm_atomic_state *state)
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+
+ HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ mdelay(1);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ vc4_hdmi_disable_scrambling(encoder);
+ }
+
+@@ -624,10 +673,13 @@ static void vc4_hdmi_encoder_post_crtc_p
+ struct drm_atomic_state *state)
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ unsigned long flags;
+ int ret;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ if (vc4_hdmi->variant->phy_disable)
+ vc4_hdmi->variant->phy_disable(vc4_hdmi);
+@@ -646,8 +698,11 @@ static void vc4_hdmi_encoder_disable(str
+
+ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+ {
++ unsigned long flags;
+ u32 csc_ctl;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+ VC4_HD_CSC_CTL_ORDER);
+
+@@ -677,14 +732,19 @@ static void vc4_hdmi_csc_setup(struct vc
+
+ /* The RGB order applies even when CSC is disabled. */
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+ {
++ unsigned long flags;
+ u32 csc_ctl;
+
+ csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ if (enable) {
+ /* CEA VICs other than #1 requre limited range RGB
+ * output unless overridden by an AVI infoframe.
+@@ -716,6 +776,8 @@ static void vc5_hdmi_csc_setup(struct vc
+ }
+
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+@@ -739,6 +801,9 @@ static void vc4_hdmi_set_timings(struct
+ mode->crtc_vsync_end -
+ interlaced,
+ VC4_HDMI_VERTB_VBP));
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+ HDMI_WRITE(HDMI_HORZA,
+ (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+@@ -762,6 +827,8 @@ static void vc4_hdmi_set_timings(struct
+
+ HDMI_WRITE(HDMI_VERTB0, vertb_even);
+ HDMI_WRITE(HDMI_VERTB1, vertb);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+@@ -785,10 +852,13 @@ static void vc5_hdmi_set_timings(struct
+ mode->crtc_vsync_end -
+ interlaced,
+ VC4_HDMI_VERTB_VBP));
++ unsigned long flags;
+ unsigned char gcp;
+ bool gcp_en;
+ u32 reg;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
+ HDMI_WRITE(HDMI_HORZA,
+ (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) |
+@@ -847,13 +917,18 @@ static void vc5_hdmi_set_timings(struct
+ HDMI_WRITE(HDMI_GCP_CONFIG, reg);
+
+ HDMI_WRITE(HDMI_CLOCK_STOP, 0);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
+ u32 drift;
+ int ret;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ drift = HDMI_READ(HDMI_FIFO_CTL);
+ drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
+
+@@ -861,12 +936,20 @@ static void vc4_hdmi_recenter_fifo(struc
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ usleep_range(1000, 1100);
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) &
+ VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
+ WARN_ONCE(ret, "Timeout waiting for "
+@@ -900,6 +983,7 @@ static void vc4_hdmi_encoder_pre_crtc_co
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ unsigned long pixel_rate = vc4_conn_state->pixel_rate;
+ unsigned long bvb_rate, hsm_rate;
++ unsigned long flags;
+ int ret;
+
+ /*
+@@ -968,11 +1052,15 @@ static void vc4_hdmi_encoder_pre_crtc_co
+ if (vc4_hdmi->variant->phy_init)
+ vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
+ VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
+ VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ if (vc4_hdmi->variant->set_timings)
+ vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
+
+@@ -992,6 +1080,7 @@ static void vc4_hdmi_encoder_pre_crtc_en
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ unsigned long flags;
+
+ if (vc4_encoder->hdmi_monitor &&
+ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
+@@ -1006,7 +1095,9 @@ static void vc4_hdmi_encoder_pre_crtc_en
+ vc4_encoder->limited_rgb_range = false;
+ }
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
+@@ -1017,8 +1108,11 @@ static void vc4_hdmi_encoder_post_crtc_e
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
++ unsigned long flags;
+ int ret;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_VID_CTL,
+ VC4_HD_VID_CTL_ENABLE |
+ VC4_HD_VID_CTL_CLRRGB |
+@@ -1035,6 +1129,8 @@ static void vc4_hdmi_encoder_post_crtc_e
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
+ VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000);
+ WARN_ONCE(ret, "Timeout waiting for "
+@@ -1047,6 +1143,8 @@ static void vc4_hdmi_encoder_post_crtc_e
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000);
+ WARN_ONCE(ret, "Timeout waiting for "
+@@ -1054,6 +1152,8 @@ static void vc4_hdmi_encoder_post_crtc_e
+ }
+
+ if (vc4_encoder->hdmi_monitor) {
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+@@ -1063,6 +1163,8 @@ static void vc4_hdmi_encoder_post_crtc_e
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ VC4_HDMI_RAM_PACKET_ENABLE);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ vc4_hdmi_set_infoframes(encoder);
+ }
+
+@@ -1186,6 +1288,7 @@ static void vc4_hdmi_audio_set_mai_clock
+ unsigned int samplerate)
+ {
+ u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
++ unsigned long flags;
+ unsigned long n, m;
+
+ rational_best_approximation(hsm_clock, samplerate,
+@@ -1195,9 +1298,11 @@ static void vc4_hdmi_audio_set_mai_clock
+ VC4_HD_MAI_SMP_M_SHIFT) + 1,
+ &n, &m);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_MAI_SMP,
+ VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
+ VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate)
+@@ -1208,6 +1313,8 @@ static void vc4_hdmi_set_n_cts(struct vc
+ u32 n, cts;
+ u64 tmp;
+
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
+ n = 128 * samplerate / 1000;
+ tmp = (u64)(mode->clock * 1000) * n;
+ do_div(tmp, 128 * samplerate);
+@@ -1237,6 +1344,7 @@ static int vc4_hdmi_audio_startup(struct
+ {
+ struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
++ unsigned long flags;
+
+ /*
+ * If the HDMI encoder hasn't probed, or the encoder is
+@@ -1248,12 +1356,14 @@ static int vc4_hdmi_audio_startup(struct
+
+ vc4_hdmi->audio.streaming = true;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_RESET |
+ VC4_HD_MAI_CTL_FLUSH |
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ if (vc4_hdmi->variant->phy_rng_enable)
+ vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
+@@ -1265,6 +1375,7 @@ static void vc4_hdmi_audio_reset(struct
+ {
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct device *dev = &vc4_hdmi->pdev->dev;
++ unsigned long flags;
+ int ret;
+
+ vc4_hdmi->audio.streaming = false;
+@@ -1272,20 +1383,29 @@ static void vc4_hdmi_audio_reset(struct
+ if (ret)
+ dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET);
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
+ {
+ struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ if (vc4_hdmi->variant->phy_rng_disable)
+ vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
+
+@@ -1340,6 +1460,7 @@ static int vc4_hdmi_audio_prepare(struct
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ unsigned int sample_rate = params->sample_rate;
+ unsigned int channels = params->channels;
++ unsigned long flags;
+ u32 audio_packet_config, channel_mask;
+ u32 channel_map;
+ u32 mai_audio_format;
+@@ -1348,14 +1469,15 @@ static int vc4_hdmi_audio_prepare(struct
+ dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
+ sample_rate, params->sample_width, channels);
+
++ vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_SET_FIELD(channels, VC4_HD_MAI_CTL_CHNUM) |
+ VC4_HD_MAI_CTL_WHOLSMP |
+ VC4_HD_MAI_CTL_CHALIGN |
+ VC4_HD_MAI_CTL_ENABLE);
+
+- vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
+-
+ mai_sample_rate = sample_rate_to_mai_fmt(sample_rate);
+ if (params->iec.status[0] & IEC958_AES0_NONAUDIO &&
+ params->channels == 8)
+@@ -1393,8 +1515,11 @@ static int vc4_hdmi_audio_prepare(struct
+ channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask);
+ HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map);
+ HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
++
+ vc4_hdmi_set_n_cts(vc4_hdmi, sample_rate);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea));
+ vc4_hdmi_set_audio_infoframe(encoder);
+
+@@ -1668,6 +1793,8 @@ static void vc4_cec_read_msg(struct vc4_
+ struct cec_msg *msg = &vc4_hdmi->cec_rx_msg;
+ unsigned int i;
+
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
+ msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+ VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
+
+@@ -1686,11 +1813,12 @@ static void vc4_cec_read_msg(struct vc4_
+ }
+ }
+
+-static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv)
++static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct vc4_hdmi *vc4_hdmi = priv;
+ u32 cntrl1;
+
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
+ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+ vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+ cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+@@ -1699,11 +1827,24 @@ static irqreturn_t vc4_cec_irq_handler_t
+ return IRQ_WAKE_THREAD;
+ }
+
+-static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv)
++static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv)
+ {
+ struct vc4_hdmi *vc4_hdmi = priv;
++ irqreturn_t ret;
++
++ spin_lock(&vc4_hdmi->hw_lock);
++ ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi);
++ spin_unlock(&vc4_hdmi->hw_lock);
++
++ return ret;
++}
++
++static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi)
++{
+ u32 cntrl1;
+
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
+ vc4_hdmi->cec_rx_msg.len = 0;
+ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+ vc4_cec_read_msg(vc4_hdmi, cntrl1);
+@@ -1716,6 +1857,18 @@ static irqreturn_t vc4_cec_irq_handler_r
+ return IRQ_WAKE_THREAD;
+ }
+
++static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv)
++{
++ struct vc4_hdmi *vc4_hdmi = priv;
++ irqreturn_t ret;
++
++ spin_lock(&vc4_hdmi->hw_lock);
++ ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi);
++ spin_unlock(&vc4_hdmi->hw_lock);
++
++ return ret;
++}
++
+ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+ {
+ struct vc4_hdmi *vc4_hdmi = priv;
+@@ -1726,14 +1879,17 @@ static irqreturn_t vc4_cec_irq_handler(i
+ if (!(stat & VC4_HDMI_CPU_CEC))
+ return IRQ_NONE;
+
++ spin_lock(&vc4_hdmi->hw_lock);
+ cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
+ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+ if (vc4_hdmi->cec_irq_was_rx)
+- ret = vc4_cec_irq_handler_rx_bare(irq, priv);
++ ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi);
+ else
+- ret = vc4_cec_irq_handler_tx_bare(irq, priv);
++ ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi);
+
+ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
++ spin_unlock(&vc4_hdmi->hw_lock);
++
+ return ret;
+ }
+
+@@ -1742,6 +1898,7 @@ static int vc4_hdmi_cec_enable(struct ce
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ /* clock period in microseconds */
+ const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
++ unsigned long flags;
+ u32 val;
+ int ret;
+
+@@ -1749,6 +1906,8 @@ static int vc4_hdmi_cec_enable(struct ce
+ if (ret)
+ return ret;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ val = HDMI_READ(HDMI_CEC_CNTRL_5);
+ val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
+ VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
+@@ -1779,12 +1938,17 @@ static int vc4_hdmi_cec_enable(struct ce
+ if (!vc4_hdmi->variant->external_irq_controller)
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ return 0;
+ }
+
+ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
+ {
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+ if (!vc4_hdmi->variant->external_irq_controller)
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
+@@ -1792,6 +1956,8 @@ static int vc4_hdmi_cec_disable(struct c
+ HDMI_WRITE(HDMI_CEC_CNTRL_5, HDMI_READ(HDMI_CEC_CNTRL_5) |
+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ pm_runtime_put(&vc4_hdmi->pdev->dev);
+
+ return 0;
+@@ -1808,10 +1974,14 @@ static int vc4_hdmi_cec_adap_enable(stru
+ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+ {
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
++ unsigned long flags;
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1,
+ (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+ (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ return 0;
+ }
+
+@@ -1820,6 +1990,7 @@ static int vc4_hdmi_cec_adap_transmit(st
+ {
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ struct drm_device *dev = vc4_hdmi->connector.dev;
++ unsigned long flags;
+ u32 val;
+ unsigned int i;
+
+@@ -1828,6 +1999,8 @@ static int vc4_hdmi_cec_adap_transmit(st
+ return -ENOMEM;
+ }
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ for (i = 0; i < msg->len; i += 4)
+ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i >> 2),
+ (msg->msg[i]) |
+@@ -1843,6 +2016,9 @@ static int vc4_hdmi_cec_adap_transmit(st
+ val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
+
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++
+ return 0;
+ }
+
+@@ -1857,6 +2033,7 @@ static int vc4_hdmi_cec_init(struct vc4_
+ struct cec_connector_info conn_info;
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ struct device *dev = &pdev->dev;
++ unsigned long flags;
+ u32 value;
+ int ret;
+
+@@ -1876,10 +2053,12 @@ static int vc4_hdmi_cec_init(struct vc4_
+ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
+ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ value = HDMI_READ(HDMI_CEC_CNTRL_1);
+ /* Set the logical address to Unregistered */
+ value |= VC4_HDMI_CEC_ADDR_MASK;
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ vc4_hdmi_cec_update_clk_div(vc4_hdmi);
+
+@@ -1898,7 +2077,9 @@ static int vc4_hdmi_cec_init(struct vc4_
+ if (ret)
+ goto err_remove_cec_rx_handler;
+ } else {
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ ret = request_threaded_irq(platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+@@ -2168,6 +2349,7 @@ static int vc4_hdmi_bind(struct device *
+ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
+ if (!vc4_hdmi)
+ return -ENOMEM;
++ spin_lock_init(&vc4_hdmi->hw_lock);
+ INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
+
+ dev_set_drvdata(dev, vc4_hdmi);
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -179,6 +179,11 @@ struct vc4_hdmi {
+
+ struct debugfs_regset32 hdmi_regset;
+ struct debugfs_regset32 hd_regset;
++
++ /**
++ * @hw_lock: Spinlock protecting device register access.
++ */
++ spinlock_t hw_lock;
+ };
+
+ static inline struct vc4_hdmi *
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -130,31 +130,49 @@
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *conn_state)
+ {
++ unsigned long flags;
++
+ /* PHY should be in reset, like
+ * vc4_hdmi_encoder_disable() does.
+ */
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+ HDMI_READ(HDMI_TX_PHY_CTL_0) &
+ ~VC4_HDMI_TX_PHY_RNG_PWRDN);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+ HDMI_READ(HDMI_TX_PHY_CTL_0) |
+ VC4_HDMI_TX_PHY_RNG_PWRDN);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ static unsigned long long
+@@ -336,6 +354,8 @@ phy_get_channel_settings(enum vc4_hdmi_p
+
+ static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
+ {
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x0f);
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, BIT(10));
+ }
+@@ -348,10 +368,13 @@ void vc5_hdmi_phy_init(struct vc4_hdmi *
+ unsigned long long pixel_freq = conn_state->pixel_rate;
+ unsigned long long vco_freq;
+ unsigned char word_sel;
++ unsigned long flags;
+ u8 vco_sel, vco_div;
+
+ vco_freq = phy_get_vco_freq(pixel_freq, &vco_sel, &vco_div);
+
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
+ vc5_hdmi_reset_phy(vc4_hdmi);
+
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+@@ -501,23 +524,37 @@ void vc5_hdmi_phy_init(struct vc4_hdmi *
+ HDMI_READ(HDMI_TX_PHY_RESET_CTL) |
+ VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB |
+ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ vc5_hdmi_reset_phy(vc4_hdmi);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) &
+ ~VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+
+ void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) |
+ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -442,6 +442,8 @@ static inline void vc4_hdmi_write(struct
+ const struct vc4_hdmi_variant *variant = hdmi->variant;
+ void __iomem *base;
+
++ lockdep_assert_held(&hdmi->hw_lock);
++
+ WARN_ON(!pm_runtime_active(&hdmi->pdev->dev));
+
+ if (reg >= variant->num_registers) {