1 From a4e8051901a5d858a69732a3f9734835afc00af5 Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.org>
3 Date: Fri, 19 Jul 2019 15:35:13 +0100
4 Subject: [PATCH 718/806] drm/vc4: Add support for margins to fkms
6 Allows for overscan to be configured under FKMS.
7 NB This is rescaling the planes, not reducing the size of the
10 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
12 drivers/gpu/drm/vc4/vc4_firmware_kms.c | 241 +++++++++++++++++++------
13 1 file changed, 190 insertions(+), 51 deletions(-)
15 --- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
16 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
17 @@ -256,6 +256,23 @@ static inline struct vc4_crtc *to_vc4_cr
18 return container_of(crtc, struct vc4_crtc, base);
21 +struct vc4_crtc_state {
22 + struct drm_crtc_state base;
28 + unsigned int bottom;
32 +static inline struct vc4_crtc_state *
33 +to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
35 + return (struct vc4_crtc_state *)crtc_state;
38 struct vc4_fkms_encoder {
39 struct drm_encoder base;
41 @@ -365,17 +382,127 @@ static int vc4_plane_set_blank(struct dr
45 +static void vc4_fkms_crtc_get_margins(struct drm_crtc_state *state,
46 + unsigned int *left, unsigned int *right,
47 + unsigned int *top, unsigned int *bottom)
49 + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
50 + struct drm_connector_state *conn_state;
51 + struct drm_connector *conn;
54 + *left = vc4_state->margins.left;
55 + *right = vc4_state->margins.right;
56 + *top = vc4_state->margins.top;
57 + *bottom = vc4_state->margins.bottom;
59 + /* We have to interate over all new connector states because
60 + * vc4_fkms_crtc_get_margins() might be called before
61 + * vc4_fkms_crtc_atomic_check() which means margins info in
62 + * vc4_crtc_state might be outdated.
64 + for_each_new_connector_in_state(state->state, conn, conn_state, i) {
65 + if (conn_state->crtc != state->crtc)
68 + *left = conn_state->tv.margins.left;
69 + *right = conn_state->tv.margins.right;
70 + *top = conn_state->tv.margins.top;
71 + *bottom = conn_state->tv.margins.bottom;
76 +static int vc4_fkms_margins_adj(struct drm_plane_state *pstate,
77 + struct set_plane *plane)
79 + unsigned int left, right, top, bottom;
80 + int adjhdisplay, adjvdisplay;
81 + struct drm_crtc_state *crtc_state;
83 + crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
86 + vc4_fkms_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
88 + if (!left && !right && !top && !bottom)
91 + if (left + right >= crtc_state->mode.hdisplay ||
92 + top + bottom >= crtc_state->mode.vdisplay)
95 + adjhdisplay = crtc_state->mode.hdisplay - (left + right);
96 + plane->dst_x = DIV_ROUND_CLOSEST(plane->dst_x * adjhdisplay,
97 + (int)crtc_state->mode.hdisplay);
98 + plane->dst_x += left;
99 + if (plane->dst_x > (int)(crtc_state->mode.hdisplay - left))
100 + plane->dst_x = crtc_state->mode.hdisplay - left;
102 + adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
103 + plane->dst_y = DIV_ROUND_CLOSEST(plane->dst_y * adjvdisplay,
104 + (int)crtc_state->mode.vdisplay);
105 + plane->dst_y += top;
106 + if (plane->dst_y > (int)(crtc_state->mode.vdisplay - top))
107 + plane->dst_y = crtc_state->mode.vdisplay - top;
109 + plane->dst_w = DIV_ROUND_CLOSEST(plane->dst_w * adjhdisplay,
110 + crtc_state->mode.hdisplay);
111 + plane->dst_h = DIV_ROUND_CLOSEST(plane->dst_h * adjvdisplay,
112 + crtc_state->mode.vdisplay);
114 + if (!plane->dst_w || !plane->dst_h)
120 static void vc4_plane_atomic_update(struct drm_plane *plane,
121 struct drm_plane_state *old_state)
123 struct drm_plane_state *state = plane->state;
126 + * Do NOT set now, as we haven't checked if the crtc is active or not.
127 + * Set from vc4_plane_set_blank instead.
129 + * If the CRTC is on (or going to be on) and we're enabled,
130 + * then unblank. Otherwise, stay blank until CRTC enable.
132 + if (state->crtc->state->active)
133 + vc4_plane_set_blank(plane, false);
136 +static void vc4_plane_atomic_disable(struct drm_plane *plane,
137 + struct drm_plane_state *old_state)
139 + struct drm_plane_state *state = plane->state;
140 + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
142 + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
143 + plane->base.id, plane->name,
146 + vc4_plane->mb.plane.vc_image_type,
149 + vc4_plane_set_blank(plane, true);
152 +static bool plane_enabled(struct drm_plane_state *state)
154 + return state->fb && state->crtc;
157 +static int vc4_plane_to_mb(struct drm_plane *plane,
158 + struct mailbox_set_plane *mb,
159 + struct drm_plane_state *state)
161 struct drm_framebuffer *fb = state->fb;
162 struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
163 const struct drm_format_info *drm_fmt = fb->format;
164 const struct vc_image_format *vc_fmt =
165 vc4_get_vc_image_fmt(drm_fmt->format);
166 - struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
167 - struct mailbox_set_plane *mb = &vc4_plane->mb;
168 int num_planes = fb->format->num_planes;
169 struct drm_display_mode *mode = &state->crtc->mode;
170 unsigned int rotation = SUPPORTED_ROTATIONS;
171 @@ -417,25 +544,7 @@ static void vc4_plane_atomic_update(stru
175 - /* FIXME: If the dest rect goes off screen then clip the src rect so we
176 - * don't have off-screen pixels.
178 - if (plane->type == DRM_PLANE_TYPE_CURSOR) {
179 - /* There is no scaling on the cursor plane, therefore the calcs
180 - * to alter the source crop as the cursor goes off the screen
183 - if (mb->plane.dst_x + mb->plane.dst_w > mode->hdisplay) {
184 - mb->plane.dst_w = mode->hdisplay - mb->plane.dst_x;
185 - mb->plane.src_w = (mode->hdisplay - mb->plane.dst_x)
188 - if (mb->plane.dst_y + mb->plane.dst_h > mode->vdisplay) {
189 - mb->plane.dst_h = mode->vdisplay - mb->plane.dst_y;
190 - mb->plane.src_h = (mode->vdisplay - mb->plane.dst_y)
194 + vc4_fkms_margins_adj(state, &mb->plane);
196 if (num_planes > 1) {
197 /* Assume this must be YUV */
198 @@ -525,38 +634,19 @@ static void vc4_plane_atomic_update(stru
200 state->normalized_zpos);
203 - * Do NOT set now, as we haven't checked if the crtc is active or not.
204 - * Set from vc4_plane_set_blank instead.
206 - * If the CRTC is on (or going to be on) and we're enabled,
207 - * then unblank. Otherwise, stay blank until CRTC enable.
209 - if (state->crtc->state->active)
210 - vc4_plane_set_blank(plane, false);
214 -static void vc4_plane_atomic_disable(struct drm_plane *plane,
215 - struct drm_plane_state *old_state)
216 +static int vc4_plane_atomic_check(struct drm_plane *plane,
217 + struct drm_plane_state *state)
219 - //struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
220 - struct drm_plane_state *state = plane->state;
221 struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
223 - DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
224 - plane->base.id, plane->name,
227 - vc4_plane->mb.plane.vc_image_type,
230 - vc4_plane_set_blank(plane, true);
232 + if (!plane_enabled(state))
235 + return vc4_plane_to_mb(plane, &vc4_plane->mb, state);
237 -static int vc4_plane_atomic_check(struct drm_plane *plane,
238 - struct drm_plane_state *state)
243 static void vc4_plane_destroy(struct drm_plane *plane)
244 @@ -878,8 +968,23 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
245 static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
246 struct drm_crtc_state *state)
248 - DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n",
250 + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
251 + struct drm_connector *conn;
252 + struct drm_connector_state *conn_state;
255 + DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", crtc->base.id);
257 + for_each_new_connector_in_state(state->state, conn, conn_state, i) {
258 + if (conn_state->crtc != crtc)
261 + vc4_state->margins.left = conn_state->tv.margins.left;
262 + vc4_state->margins.right = conn_state->tv.margins.right;
263 + vc4_state->margins.top = conn_state->tv.margins.top;
264 + vc4_state->margins.bottom = conn_state->tv.margins.bottom;
270 @@ -980,6 +1085,33 @@ static int vc4_page_flip(struct drm_crtc
271 return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
274 +static struct drm_crtc_state *
275 +vc4_crtc_duplicate_state(struct drm_crtc *crtc)
277 + struct vc4_crtc_state *vc4_state, *old_vc4_state;
279 + vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
283 + old_vc4_state = to_vc4_crtc_state(crtc->state);
284 + vc4_state->margins = old_vc4_state->margins;
286 + __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
287 + return &vc4_state->base;
291 +vc4_crtc_reset(struct drm_crtc *crtc)
294 + __drm_atomic_helper_crtc_destroy_state(crtc->state);
296 + crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
298 + crtc->state->crtc = crtc;
301 static int vc4_fkms_enable_vblank(struct drm_crtc *crtc)
303 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
304 @@ -1007,8 +1139,8 @@ static const struct drm_crtc_funcs vc4_c
305 .set_property = NULL,
306 .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
307 .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
308 - .reset = drm_atomic_helper_crtc_reset,
309 - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
310 + .reset = vc4_crtc_reset,
311 + .atomic_duplicate_state = vc4_crtc_duplicate_state,
312 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
313 .enable_vblank = vc4_fkms_enable_vblank,
314 .disable_vblank = vc4_fkms_disable_vblank,
315 @@ -1267,6 +1399,13 @@ vc4_fkms_connector_init(struct drm_devic
316 connector->interlace_allowed = 0;
319 + /* Create and attach TV margin props to this connector. */
320 + ret = drm_mode_create_tv_margin_properties(dev);
322 + return ERR_PTR(ret);
324 + drm_connector_attach_tv_margin_properties(connector);
326 connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
327 DRM_CONNECTOR_POLL_DISCONNECT);