c298d2cdee9eb3c101813c14a02be92dc3fb684c
[openwrt/staging/blocktrron.git] /
1 From d6ecbdcba5174488d403ccddc016721243eb1797 Mon Sep 17 00:00:00 2001
2 From: Maxime Ripard <maxime@cerno.tech>
3 Date: Tue, 19 Oct 2021 14:19:29 +0200
4 Subject: [PATCH] drm/vc4: hdmi: Prevent access to crtc->state outside
5 of KMS
6
7 Accessing the crtc->state pointer from outside the modesetting context
8 is not allowed. We thus need to copy whatever we need from the KMS state
9 to our structure in order to access it.
10
11 However, in the vc4 HDMI driver we do use that pointer in the ALSA code
12 path, and potentially in the hotplug interrupt handler path.
13
14 These paths both need access to the CRTC adjusted mode in order for the
15 proper dividers to be set for ALSA, and the scrambler state to be
16 reinstated properly for hotplug.
17
18 Let's copy this mode into our private encoder structure and reference it
19 from there when needed. Since that part is shared between KMS and other
20 paths, we need to protect it using our mutex.
21
22 Link: https://lore.kernel.org/all/YWgteNaNeaS9uWDe@phenom.ffwll.local/
23 Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support")
24 Signed-off-by: Maxime Ripard <maxime@cerno.tech>
25 ---
26 drivers/gpu/drm/vc4/vc4_hdmi.c | 38 +++++++++++++++++++++++-----------
27 drivers/gpu/drm/vc4/vc4_hdmi.h | 6 ++++++
28 2 files changed, 32 insertions(+), 12 deletions(-)
29
30 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
31 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
32 @@ -488,8 +488,7 @@ static void vc4_hdmi_set_avi_infoframe(s
33 struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
34 struct drm_connector *connector = &vc4_hdmi->connector;
35 struct drm_connector_state *cstate = connector->state;
36 - struct drm_crtc *crtc = encoder->crtc;
37 - const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
38 + const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
39 union hdmi_infoframe frame;
40 int ret;
41
42 @@ -601,8 +600,8 @@ static bool vc4_hdmi_supports_scrambling
43
44 static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
45 {
46 - struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
47 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
48 + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
49 unsigned long flags;
50
51 lockdep_assert_held(&vc4_hdmi->mutex);
52 @@ -628,18 +627,21 @@ static void vc4_hdmi_enable_scrambling(s
53 static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
54 {
55 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
56 + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
57 struct drm_crtc *crtc = encoder->crtc;
58 unsigned long flags;
59
60 + lockdep_assert_held(&vc4_hdmi->mutex);
61 +
62 /*
63 * At boot, encoder->crtc will be NULL. Since we don't know the
64 * state of the scrambler and in order to avoid any
65 * inconsistency, let's disable it all the time.
66 */
67 - if (crtc && !vc4_hdmi_supports_scrambling(encoder, &crtc->mode))
68 + if (crtc && !vc4_hdmi_supports_scrambling(encoder, mode))
69 return;
70
71 - if (crtc && !vc4_hdmi_mode_needs_scrambling(&crtc->mode))
72 + if (crtc && !vc4_hdmi_mode_needs_scrambling(mode))
73 return;
74
75 if (delayed_work_pending(&vc4_hdmi->scrambling_work))
76 @@ -1018,8 +1020,8 @@ static void vc4_hdmi_encoder_pre_crtc_co
77 vc4_hdmi_encoder_get_connector_state(encoder, state);
78 struct vc4_hdmi_connector_state *vc4_conn_state =
79 conn_state_to_vc4_hdmi_conn_state(conn_state);
80 - struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
81 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
82 + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
83 unsigned long pixel_rate = vc4_conn_state->pixel_rate;
84 unsigned long bvb_rate, hsm_rate;
85 unsigned long flags;
86 @@ -1121,9 +1123,9 @@ out:
87 static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
88 struct drm_atomic_state *state)
89 {
90 - struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
91 - struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
92 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
93 + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
94 + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
95 unsigned long flags;
96
97 mutex_lock(&vc4_hdmi->mutex);
98 @@ -1151,8 +1153,8 @@ static void vc4_hdmi_encoder_pre_crtc_en
99 static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
100 struct drm_atomic_state *state)
101 {
102 - struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
103 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
104 + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
105 struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
106 bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
107 bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
108 @@ -1228,6 +1230,19 @@ static void vc4_hdmi_encoder_enable(stru
109 {
110 }
111
112 +static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
113 + struct drm_crtc_state *crtc_state,
114 + struct drm_connector_state *conn_state)
115 +{
116 + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
117 +
118 + mutex_lock(&vc4_hdmi->mutex);
119 + memcpy(&vc4_hdmi->saved_adjusted_mode,
120 + &crtc_state->adjusted_mode,
121 + sizeof(vc4_hdmi->saved_adjusted_mode));
122 + mutex_unlock(&vc4_hdmi->mutex);
123 +}
124 +
125 #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
126 #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL
127
128 @@ -1306,6 +1321,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_e
129
130 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
131 .atomic_check = vc4_hdmi_encoder_atomic_check,
132 + .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set,
133 .mode_valid = vc4_hdmi_encoder_mode_valid,
134 .disable = vc4_hdmi_encoder_disable,
135 .enable = vc4_hdmi_encoder_enable,
136 @@ -1359,9 +1375,7 @@ static void vc4_hdmi_audio_set_mai_clock
137
138 static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate)
139 {
140 - struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
141 - struct drm_crtc *crtc = encoder->crtc;
142 - const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
143 + const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
144 u32 n, cts;
145 u64 tmp;
146
147 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
148 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
149 @@ -198,6 +198,12 @@ struct vc4_hdmi {
150 * be resilient to that.
151 */
152 struct mutex mutex;
153 +
154 + /**
155 + * @saved_adjusted_mode: Copy of @drm_crtc_state.adjusted_mode
156 + * for use by ALSA hooks and interrupt handlers. Protected by @mutex.
157 + */
158 + struct drm_display_mode saved_adjusted_mode;
159 };
160
161 static inline struct vc4_hdmi *