kernel: bump 5.4 to 5.4.48
[oweals/openwrt.git] / target / linux / bcm27xx / patches-5.4 / 950-0277-drm-vc4-Add-support-for-margins-to-fkms.patch
1 From 8a7170d2ad05ae00733e0535b281ce2e682c6f65 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] drm/vc4: Add support for margins to fkms
5
6 Allows for overscan to be configured under FKMS.
7 NB This is rescaling the planes, not reducing the size of the
8 display mode.
9
10 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
11 ---
12  drivers/gpu/drm/vc4/vc4_firmware_kms.c | 241 +++++++++++++++++++------
13  1 file changed, 190 insertions(+), 51 deletions(-)
14
15 --- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
16 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
17 @@ -259,6 +259,23 @@ static inline struct vc4_crtc *to_vc4_cr
18         return container_of(crtc, struct vc4_crtc, base);
19  }
20  
21 +struct vc4_crtc_state {
22 +       struct drm_crtc_state base;
23 +
24 +       struct {
25 +               unsigned int left;
26 +               unsigned int right;
27 +               unsigned int top;
28 +               unsigned int bottom;
29 +       } margins;
30 +};
31 +
32 +static inline struct vc4_crtc_state *
33 +to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
34 +{
35 +       return (struct vc4_crtc_state *)crtc_state;
36 +}
37 +
38  struct vc4_fkms_encoder {
39         struct drm_encoder base;
40         bool hdmi_monitor;
41 @@ -367,17 +384,127 @@ static int vc4_plane_set_blank(struct dr
42         return ret;
43  }
44  
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)
48 +{
49 +       struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
50 +       struct drm_connector_state *conn_state;
51 +       struct drm_connector *conn;
52 +       int i;
53 +
54 +       *left = vc4_state->margins.left;
55 +       *right = vc4_state->margins.right;
56 +       *top = vc4_state->margins.top;
57 +       *bottom = vc4_state->margins.bottom;
58 +
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.
63 +        */
64 +       for_each_new_connector_in_state(state->state, conn, conn_state, i) {
65 +               if (conn_state->crtc != state->crtc)
66 +                       continue;
67 +
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;
72 +               break;
73 +       }
74 +}
75 +
76 +static int vc4_fkms_margins_adj(struct drm_plane_state *pstate,
77 +                               struct set_plane *plane)
78 +{
79 +       unsigned int left, right, top, bottom;
80 +       int adjhdisplay, adjvdisplay;
81 +       struct drm_crtc_state *crtc_state;
82 +
83 +       crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
84 +                                                  pstate->crtc);
85 +
86 +       vc4_fkms_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
87 +
88 +       if (!left && !right && !top && !bottom)
89 +               return 0;
90 +
91 +       if (left + right >= crtc_state->mode.hdisplay ||
92 +           top + bottom >= crtc_state->mode.vdisplay)
93 +               return -EINVAL;
94 +
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;
101 +
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;
108 +
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);
113 +
114 +       if (!plane->dst_w || !plane->dst_h)
115 +               return -EINVAL;
116 +
117 +       return 0;
118 +}
119 +
120  static void vc4_plane_atomic_update(struct drm_plane *plane,
121                                     struct drm_plane_state *old_state)
122  {
123         struct drm_plane_state *state = plane->state;
124 +
125 +       /*
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.
128 +        *
129 +        * If the CRTC is on (or going to be on) and we're enabled,
130 +        * then unblank.  Otherwise, stay blank until CRTC enable.
131 +        */
132 +       if (state->crtc->state->active)
133 +               vc4_plane_set_blank(plane, false);
134 +}
135 +
136 +static void vc4_plane_atomic_disable(struct drm_plane *plane,
137 +                                    struct drm_plane_state *old_state)
138 +{
139 +       struct drm_plane_state *state = plane->state;
140 +       struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
141 +
142 +       DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
143 +                        plane->base.id, plane->name,
144 +                        state->crtc_w,
145 +                        state->crtc_h,
146 +                        vc4_plane->mb.plane.vc_image_type,
147 +                        state->crtc_x,
148 +                        state->crtc_y);
149 +       vc4_plane_set_blank(plane, true);
150 +}
151 +
152 +static bool plane_enabled(struct drm_plane_state *state)
153 +{
154 +       return state->fb && state->crtc;
155 +}
156 +
157 +static int vc4_plane_to_mb(struct drm_plane *plane,
158 +                          struct mailbox_set_plane *mb,
159 +                          struct drm_plane_state *state)
160 +{
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 @@ -419,25 +546,7 @@ static void vc4_plane_atomic_update(stru
172                 break;
173         }
174  
175 -       /* FIXME: If the dest rect goes off screen then clip the src rect so we
176 -        * don't have off-screen pixels.
177 -        */
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
181 -                * are simple.
182 -                */
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)
186 -                                                                       << 16;
187 -               }
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)
191 -                                                                       << 16;
192 -               }
193 -       }
194 +       vc4_fkms_margins_adj(state, &mb->plane);
195  
196         if (num_planes > 1) {
197                 /* Assume this must be YUV */
198 @@ -527,38 +636,19 @@ static void vc4_plane_atomic_update(stru
199                          state->alpha,
200                          state->normalized_zpos);
201  
202 -       /*
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.
205 -        *
206 -        * If the CRTC is on (or going to be on) and we're enabled,
207 -        * then unblank.  Otherwise, stay blank until CRTC enable.
208 -        */
209 -       if (state->crtc->state->active)
210 -               vc4_plane_set_blank(plane, false);
211 +       return 0;
212  }
213  
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)
218  {
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);
222  
223 -       DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
224 -                        plane->base.id, plane->name,
225 -                        state->crtc_w,
226 -                        state->crtc_h,
227 -                        vc4_plane->mb.plane.vc_image_type,
228 -                        state->crtc_x,
229 -                        state->crtc_y);
230 -       vc4_plane_set_blank(plane, true);
231 -}
232 +       if (!plane_enabled(state))
233 +               return 0;
234 +
235 +       return vc4_plane_to_mb(plane, &vc4_plane->mb, state);
236  
237 -static int vc4_plane_atomic_check(struct drm_plane *plane,
238 -                                 struct drm_plane_state *state)
239 -{
240 -       return 0;
241  }
242  
243  /* Called during init to allocate the plane's atomic state. */
244 @@ -909,8 +999,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)
247  {
248 -       DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n",
249 -                     crtc->base.id);
250 +       struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
251 +       struct drm_connector *conn;
252 +       struct drm_connector_state *conn_state;
253 +       int i;
254 +
255 +       DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", crtc->base.id);
256 +
257 +       for_each_new_connector_in_state(state->state, conn, conn_state, i) {
258 +               if (conn_state->crtc != crtc)
259 +                       continue;
260 +
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;
265 +               break;
266 +       }
267         return 0;
268  }
269  
270 @@ -1011,6 +1116,33 @@ static int vc4_page_flip(struct drm_crtc
271         return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
272  }
273  
274 +static struct drm_crtc_state *
275 +vc4_crtc_duplicate_state(struct drm_crtc *crtc)
276 +{
277 +       struct vc4_crtc_state *vc4_state, *old_vc4_state;
278 +
279 +       vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
280 +       if (!vc4_state)
281 +               return NULL;
282 +
283 +       old_vc4_state = to_vc4_crtc_state(crtc->state);
284 +       vc4_state->margins = old_vc4_state->margins;
285 +
286 +       __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
287 +       return &vc4_state->base;
288 +}
289 +
290 +static void
291 +vc4_crtc_reset(struct drm_crtc *crtc)
292 +{
293 +       if (crtc->state)
294 +               __drm_atomic_helper_crtc_destroy_state(crtc->state);
295 +
296 +       crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
297 +       if (crtc->state)
298 +               crtc->state->crtc = crtc;
299 +}
300 +
301  static int vc4_fkms_enable_vblank(struct drm_crtc *crtc)
302  {
303         struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
304 @@ -1038,8 +1170,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 @@ -1291,6 +1423,13 @@ vc4_fkms_connector_init(struct drm_devic
316                 connector->interlace_allowed = 0;
317         }
318  
319 +       /* Create and attach TV margin props to this connector. */
320 +       ret = drm_mode_create_tv_margin_properties(dev);
321 +       if (ret)
322 +               return ERR_PTR(ret);
323 +
324 +       drm_connector_attach_tv_margin_properties(connector);
325 +
326         connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
327                              DRM_CONNECTOR_POLL_DISCONNECT);
328