diff options
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 |
commit | 20ea6adbf199097c4f5f591ffee088340630dae4 (patch) | |
tree | d6719d95e136611a1c25bbf7789652d6d402779d /target/linux/bcm27xx/patches-5.15/950-0007-drm-vc4-hdmi-Add-a-spinlock-to-protect-register-acce.patch | |
parent | bca05bd072180dc38ef740b37ded9572a6db1981 (diff) | |
download | upstream-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.patch | 907 |
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, ¶ms->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) { |