diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0009-drm-vc4-hdmi-Prevent-access-to-crtc-state-outside-of.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.15/950-0009-drm-vc4-hdmi-Prevent-access-to-crtc-state-outside-of.patch | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0009-drm-vc4-hdmi-Prevent-access-to-crtc-state-outside-of.patch b/target/linux/bcm27xx/patches-5.15/950-0009-drm-vc4-hdmi-Prevent-access-to-crtc-state-outside-of.patch new file mode 100644 index 0000000000..7d29e95514 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.15/950-0009-drm-vc4-hdmi-Prevent-access-to-crtc-state-outside-of.patch @@ -0,0 +1,161 @@ +From d6ecbdcba5174488d403ccddc016721243eb1797 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 19 Oct 2021 14:19:29 +0200 +Subject: [PATCH] drm/vc4: hdmi: Prevent access to crtc->state outside + of KMS + +Accessing the crtc->state pointer from outside the modesetting context +is not allowed. We thus need to copy whatever we need from the KMS state +to our structure in order to access it. + +However, in the vc4 HDMI driver we do use that pointer in the ALSA code +path, and potentially in the hotplug interrupt handler path. + +These paths both need access to the CRTC adjusted mode in order for the +proper dividers to be set for ALSA, and the scrambler state to be +reinstated properly for hotplug. + +Let's copy this mode into our private encoder structure and reference it +from there when needed. Since that part is shared between KMS and other +paths, we need to protect it using our mutex. + +Link: https://lore.kernel.org/all/YWgteNaNeaS9uWDe@phenom.ffwll.local/ +Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support") +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 38 +++++++++++++++++++++++----------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 6 ++++++ + 2 files changed, 32 insertions(+), 12 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -484,8 +484,7 @@ static void vc4_hdmi_set_avi_infoframe(s + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_connector_state *cstate = connector->state; +- struct drm_crtc *crtc = encoder->crtc; +- const struct drm_display_mode *mode = &crtc->state->adjusted_mode; ++ const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + union hdmi_infoframe frame; + int ret; + +@@ -597,8 +596,8 @@ static bool vc4_hdmi_supports_scrambling + + static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) + { +- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + unsigned long flags; + + lockdep_assert_held(&vc4_hdmi->mutex); +@@ -624,18 +623,21 @@ static void vc4_hdmi_enable_scrambling(s + static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) + { + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + struct drm_crtc *crtc = encoder->crtc; + unsigned long flags; + ++ lockdep_assert_held(&vc4_hdmi->mutex); ++ + /* + * At boot, encoder->crtc will be NULL. Since we don't know the + * state of the scrambler and in order to avoid any + * inconsistency, let's disable it all the time. + */ +- if (crtc && !vc4_hdmi_supports_scrambling(encoder, &crtc->mode)) ++ if (crtc && !vc4_hdmi_supports_scrambling(encoder, mode)) + return; + +- if (crtc && !vc4_hdmi_mode_needs_scrambling(&crtc->mode)) ++ if (crtc && !vc4_hdmi_mode_needs_scrambling(mode)) + return; + + if (delayed_work_pending(&vc4_hdmi->scrambling_work)) +@@ -1008,8 +1010,8 @@ static void vc4_hdmi_encoder_pre_crtc_co + vc4_hdmi_encoder_get_connector_state(encoder, state); + struct vc4_hdmi_connector_state *vc4_conn_state = + conn_state_to_vc4_hdmi_conn_state(conn_state); +- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + unsigned long pixel_rate = vc4_conn_state->pixel_rate; + unsigned long bvb_rate, hsm_rate; + unsigned long flags; +@@ -1111,9 +1113,9 @@ out: + static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) + { +- 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); ++ struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; ++ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + unsigned long flags; + + mutex_lock(&vc4_hdmi->mutex); +@@ -1141,8 +1143,8 @@ static void vc4_hdmi_encoder_pre_crtc_en + static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) + { +- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + 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; +@@ -1218,6 +1220,19 @@ static void vc4_hdmi_encoder_enable(stru + { + } + ++static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ ++ mutex_lock(&vc4_hdmi->mutex); ++ memcpy(&vc4_hdmi->saved_adjusted_mode, ++ &crtc_state->adjusted_mode, ++ sizeof(vc4_hdmi->saved_adjusted_mode)); ++ mutex_unlock(&vc4_hdmi->mutex); ++} ++ + #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL + #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL + +@@ -1296,6 +1311,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_e + + static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { + .atomic_check = vc4_hdmi_encoder_atomic_check, ++ .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set, + .mode_valid = vc4_hdmi_encoder_mode_valid, + .disable = vc4_hdmi_encoder_disable, + .enable = vc4_hdmi_encoder_enable, +@@ -1349,9 +1365,7 @@ static void vc4_hdmi_audio_set_mai_clock + + static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate) + { +- struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_crtc *crtc = encoder->crtc; +- const struct drm_display_mode *mode = &crtc->state->adjusted_mode; ++ const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + u32 n, cts; + u64 tmp; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -198,6 +198,12 @@ struct vc4_hdmi { + * be resilient to that. + */ + struct mutex mutex; ++ ++ /** ++ * @saved_adjusted_mode: Copy of @drm_crtc_state.adjusted_mode ++ * for use by ALSA hooks and interrupt handlers. Protected by @mutex. ++ */ ++ struct drm_display_mode saved_adjusted_mode; + }; + + static inline struct vc4_hdmi * |