]> Git Repo - linux.git/blob - drivers/gpu/drm/omapdrm/omap_plane.c
Merge tag 'linux-watchdog-6.14-rc1' of git://www.linux-watchdog.org/linux-watchdog
[linux.git] / drivers / gpu / drm / omapdrm / omap_plane.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
4  * Author: Rob Clark <[email protected]>
5  */
6
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_blend.h>
10 #include <drm/drm_gem_atomic_helper.h>
11 #include <drm/drm_fourcc.h>
12 #include <drm/drm_framebuffer.h>
13
14 #include "omap_dmm_tiler.h"
15 #include "omap_drv.h"
16
17 /*
18  * plane funcs
19  */
20
21 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
22
23 struct omap_plane_state {
24         /* Must be first. */
25         struct drm_plane_state base;
26
27         struct omap_hw_overlay *overlay;
28         struct omap_hw_overlay *r_overlay;  /* right overlay */
29 };
30
31 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
32
33 struct omap_plane {
34         struct drm_plane base;
35         enum omap_plane_id id;
36 };
37
38 bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
39 {
40         struct omap_plane_state *omap_state = to_omap_plane_state(state);
41
42         return !!omap_state->r_overlay;
43 }
44
45 static int omap_plane_prepare_fb(struct drm_plane *plane,
46                                  struct drm_plane_state *new_state)
47 {
48         if (!new_state->fb)
49                 return 0;
50
51         drm_gem_plane_helper_prepare_fb(plane, new_state);
52
53         return omap_framebuffer_pin(new_state->fb);
54 }
55
56 static void omap_plane_cleanup_fb(struct drm_plane *plane,
57                                   struct drm_plane_state *old_state)
58 {
59         if (old_state->fb)
60                 omap_framebuffer_unpin(old_state->fb);
61 }
62
63 static void omap_plane_atomic_update(struct drm_plane *plane,
64                                      struct drm_atomic_state *state)
65 {
66         struct omap_drm_private *priv = plane->dev->dev_private;
67         struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
68                                                                            plane);
69         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
70                                                                            plane);
71         struct omap_plane_state *new_omap_state;
72         struct omap_plane_state *old_omap_state;
73         struct omap_overlay_info info, r_info;
74         enum omap_plane_id ovl_id, r_ovl_id;
75         int ret;
76         bool dual_ovl;
77
78         new_omap_state = to_omap_plane_state(new_state);
79         old_omap_state = to_omap_plane_state(old_state);
80
81         dual_ovl = is_omap_plane_dual_overlay(new_state);
82
83         /* Cleanup previously held overlay if needed */
84         if (old_omap_state->overlay)
85                 omap_overlay_update_state(priv, old_omap_state->overlay);
86         if (old_omap_state->r_overlay)
87                 omap_overlay_update_state(priv, old_omap_state->r_overlay);
88
89         if (!new_omap_state->overlay) {
90                 DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
91                 return;
92         }
93
94         ovl_id = new_omap_state->overlay->id;
95         DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
96             new_state->fb);
97
98         memset(&info, 0, sizeof(info));
99         info.rotation_type = OMAP_DSS_ROT_NONE;
100         info.rotation = DRM_MODE_ROTATE_0;
101         info.global_alpha = new_state->alpha >> 8;
102         info.zorder = new_state->normalized_zpos;
103         if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
104                 info.pre_mult_alpha = 1;
105         else
106                 info.pre_mult_alpha = 0;
107         info.color_encoding = new_state->color_encoding;
108         info.color_range = new_state->color_range;
109
110         r_info = info;
111
112         /* update scanout: */
113         omap_framebuffer_update_scanout(new_state->fb, new_state, &info,
114                                         dual_ovl ? &r_info : NULL);
115
116         DBG("%s: %dx%d -> %dx%d (%d)",
117                         new_omap_state->overlay->name, info.width, info.height,
118                         info.out_width, info.out_height, info.screen_width);
119         DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
120                         &info.paddr, &info.p_uv_addr);
121
122         if (dual_ovl) {
123                 r_ovl_id = new_omap_state->r_overlay->id;
124                 /*
125                  * If the current plane uses 2 hw planes the very next
126                  * zorder is used by the r_overlay so we just use the
127                  * main overlay zorder + 1
128                  */
129                 r_info.zorder = info.zorder + 1;
130
131                 DBG("%s: %dx%d -> %dx%d (%d)",
132                     new_omap_state->r_overlay->name,
133                     r_info.width, r_info.height,
134                     r_info.out_width, r_info.out_height, r_info.screen_width);
135                 DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y,
136                     &r_info.paddr, &r_info.p_uv_addr);
137         }
138
139         /* and finally, update omapdss: */
140         ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
141                               omap_crtc_timings(new_state->crtc), false,
142                               omap_crtc_channel(new_state->crtc));
143         if (ret) {
144                 dev_err(plane->dev->dev, "Failed to setup plane %s\n",
145                         plane->name);
146                 dispc_ovl_enable(priv->dispc, ovl_id, false);
147                 return;
148         }
149
150         dispc_ovl_enable(priv->dispc, ovl_id, true);
151
152         if (dual_ovl) {
153                 ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info,
154                                       omap_crtc_timings(new_state->crtc), false,
155                                       omap_crtc_channel(new_state->crtc));
156                 if (ret) {
157                         dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n",
158                                 plane->name);
159                         dispc_ovl_enable(priv->dispc, r_ovl_id, false);
160                         dispc_ovl_enable(priv->dispc, ovl_id, false);
161                         return;
162                 }
163
164                 dispc_ovl_enable(priv->dispc, r_ovl_id, true);
165         }
166 }
167
168 static void omap_plane_atomic_disable(struct drm_plane *plane,
169                                       struct drm_atomic_state *state)
170 {
171         struct omap_drm_private *priv = plane->dev->dev_private;
172         struct omap_plane *omap_plane = to_omap_plane(plane);
173         struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
174                                                                            plane);
175         struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
176                                                                            plane);
177         struct omap_plane_state *new_omap_state;
178         struct omap_plane_state *old_omap_state;
179
180         new_omap_state = to_omap_plane_state(new_state);
181         old_omap_state = to_omap_plane_state(old_state);
182
183         if (!old_omap_state->overlay)
184                 return;
185
186         new_state->rotation = DRM_MODE_ROTATE_0;
187         new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
188
189         omap_overlay_update_state(priv, old_omap_state->overlay);
190         new_omap_state->overlay = NULL;
191
192         if (is_omap_plane_dual_overlay(old_state)) {
193                 omap_overlay_update_state(priv, old_omap_state->r_overlay);
194                 new_omap_state->r_overlay = NULL;
195         }
196 }
197
198 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
199
200 static int omap_plane_atomic_check(struct drm_plane *plane,
201                                    struct drm_atomic_state *state)
202 {
203         struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
204                                                                                  plane);
205         struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
206                                                                                  plane);
207         struct omap_drm_private *priv = plane->dev->dev_private;
208         struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
209         struct omap_global_state *omap_overlay_global_state;
210         struct drm_crtc_state *crtc_state;
211         bool new_r_hw_overlay = false;
212         bool new_hw_overlay = false;
213         u32 max_width, max_height;
214         struct drm_crtc *crtc;
215         u16 width, height;
216         u32 caps = 0;
217         u32 fourcc;
218         int ret;
219
220         omap_overlay_global_state = omap_get_global_state(state);
221         if (IS_ERR(omap_overlay_global_state))
222                 return PTR_ERR(omap_overlay_global_state);
223
224         dispc_ovl_get_max_size(priv->dispc, &width, &height);
225         max_width = width << 16;
226         max_height = height << 16;
227
228         crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;
229         if (!crtc)
230                 return 0;
231
232         crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
233         /* we should have a crtc state if the plane is attached to a crtc */
234         if (WARN_ON(!crtc_state))
235                 return 0;
236
237         /*
238          * Note: these are just sanity checks to filter out totally bad scaling
239          * factors. The real limits must be calculated case by case, and
240          * unfortunately we currently do those checks only at the commit
241          * phase in dispc.
242          */
243         ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
244                                                   FRAC_16_16(1, 8), FRAC_16_16(8, 1),
245                                                   true, true);
246         if (ret)
247                 return ret;
248
249         DBG("%s: visible %d -> %d", plane->name,
250             old_plane_state->visible, new_plane_state->visible);
251
252         if (!new_plane_state->visible) {
253                 omap_overlay_release(state, omap_state->overlay);
254                 omap_overlay_release(state, omap_state->r_overlay);
255                 omap_state->overlay = NULL;
256                 omap_state->r_overlay = NULL;
257                 return 0;
258         }
259
260         if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)
261                 return -EINVAL;
262
263         if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay)
264                 return -EINVAL;
265
266         if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay)
267                 return -EINVAL;
268
269         /* Make sure dimensions are within bounds. */
270         if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)
271                 return -EINVAL;
272
273
274         if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) {
275                 bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv;
276
277                 if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||
278                                       new_plane_state->crtc_w / 2 & 1)) {
279                         /*
280                          * When calculating the split overlay width
281                          * and it yield an odd value we will need to adjust
282                          * the indivual width +/- 1. So make sure it fits
283                          */
284                         if (new_plane_state->src_w <= ((2 * width - 1) << 16) &&
285                             new_plane_state->crtc_w <= (2 * width - 1))
286                                 new_r_hw_overlay = true;
287                         else
288                                 return -EINVAL;
289                 } else {
290                         if (new_plane_state->src_w <= (2 * max_width) &&
291                             new_plane_state->crtc_w <= (2 * width))
292                                 new_r_hw_overlay = true;
293                         else
294                                 return -EINVAL;
295                 }
296         }
297
298         if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&
299             !omap_framebuffer_supports_rotation(new_plane_state->fb))
300                 return -EINVAL;
301
302         if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
303             (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)
304                 caps |= OMAP_DSS_OVL_CAP_SCALE;
305
306         fourcc = new_plane_state->fb->format->format;
307
308         /*
309          * (re)allocate hw overlay if we don't have one or
310          * there is a caps mismatch
311          */
312         if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {
313                 new_hw_overlay = true;
314         } else {
315                 /* check supported format */
316                 if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,
317                                                     fourcc))
318                         new_hw_overlay = true;
319         }
320
321         /*
322          * check if we need two overlays and only have 1 or
323          * if we had 2 overlays but will only need 1
324          */
325         if ((new_r_hw_overlay && !omap_state->r_overlay) ||
326             (!new_r_hw_overlay && omap_state->r_overlay))
327                 new_hw_overlay = true;
328
329         if (new_hw_overlay) {
330                 struct omap_hw_overlay *old_ovl = omap_state->overlay;
331                 struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay;
332                 struct omap_hw_overlay *new_ovl = NULL;
333                 struct omap_hw_overlay *new_r_ovl = NULL;
334
335                 omap_overlay_release(state, old_ovl);
336                 omap_overlay_release(state, old_r_ovl);
337
338                 ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,
339                                           new_r_hw_overlay ? &new_r_ovl : NULL);
340                 if (ret) {
341                         DBG("%s: failed to assign hw_overlay", plane->name);
342                         omap_state->overlay = NULL;
343                         omap_state->r_overlay = NULL;
344                         return ret;
345                 }
346
347                 omap_state->overlay = new_ovl;
348                 if (new_r_hw_overlay)
349                         omap_state->r_overlay = new_r_ovl;
350                 else
351                         omap_state->r_overlay = NULL;
352         }
353
354         DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
355
356         if (omap_state->r_overlay)
357                 DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);
358
359         return 0;
360 }
361
362 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = {
363         .prepare_fb = omap_plane_prepare_fb,
364         .cleanup_fb = omap_plane_cleanup_fb,
365         .atomic_check = omap_plane_atomic_check,
366         .atomic_update = omap_plane_atomic_update,
367         .atomic_disable = omap_plane_atomic_disable,
368 };
369
370 static void omap_plane_destroy(struct drm_plane *plane)
371 {
372         struct omap_plane *omap_plane = to_omap_plane(plane);
373
374         DBG("%s", plane->name);
375
376         drm_plane_cleanup(plane);
377
378         kfree(omap_plane);
379 }
380
381 /* helper to install properties which are common to planes and crtcs */
382 void omap_plane_install_properties(struct drm_plane *plane,
383                 struct drm_mode_object *obj)
384 {
385         struct drm_device *dev = plane->dev;
386         struct omap_drm_private *priv = dev->dev_private;
387
388         if (priv->has_dmm) {
389                 if (!plane->rotation_property)
390                         drm_plane_create_rotation_property(plane,
391                                                            DRM_MODE_ROTATE_0,
392                                                            DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
393                                                            DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
394                                                            DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
395
396                 /* Attach the rotation property also to the crtc object */
397                 if (plane->rotation_property && obj != &plane->base)
398                         drm_object_attach_property(obj, plane->rotation_property,
399                                                    DRM_MODE_ROTATE_0);
400         }
401
402         drm_object_attach_property(obj, priv->zorder_prop, 0);
403 }
404
405 static void omap_plane_reset(struct drm_plane *plane)
406 {
407         struct omap_plane_state *omap_state;
408
409         if (plane->state)
410                 drm_atomic_helper_plane_destroy_state(plane, plane->state);
411
412         omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL);
413         if (!omap_state)
414                 return;
415
416         __drm_atomic_helper_plane_reset(plane, &omap_state->base);
417 }
418
419 static struct drm_plane_state *
420 omap_plane_atomic_duplicate_state(struct drm_plane *plane)
421 {
422         struct omap_plane_state *state, *current_state;
423
424         if (WARN_ON(!plane->state))
425                 return NULL;
426
427         current_state = to_omap_plane_state(plane->state);
428
429         state = kmalloc(sizeof(*state), GFP_KERNEL);
430         if (!state)
431                 return NULL;
432
433         __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
434
435         state->overlay = current_state->overlay;
436         state->r_overlay = current_state->r_overlay;
437
438         return &state->base;
439 }
440
441 static void omap_plane_atomic_print_state(struct drm_printer *p,
442                                           const struct drm_plane_state *state)
443 {
444         struct omap_plane_state *omap_state = to_omap_plane_state(state);
445
446         if (omap_state->overlay)
447                 drm_printf(p, "\toverlay=%s (caps=0x%x)\n",
448                            omap_state->overlay->name,
449                            omap_state->overlay->caps);
450         else
451                 drm_printf(p, "\toverlay=None\n");
452         if (omap_state->r_overlay)
453                 drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n",
454                            omap_state->r_overlay->name,
455                            omap_state->r_overlay->caps);
456         else
457                 drm_printf(p, "\tr_overlay=None\n");
458 }
459
460 static int omap_plane_atomic_set_property(struct drm_plane *plane,
461                                           struct drm_plane_state *state,
462                                           struct drm_property *property,
463                                           u64 val)
464 {
465         struct omap_drm_private *priv = plane->dev->dev_private;
466
467         if (property == priv->zorder_prop)
468                 state->zpos = val;
469         else
470                 return -EINVAL;
471
472         return 0;
473 }
474
475 static int omap_plane_atomic_get_property(struct drm_plane *plane,
476                                           const struct drm_plane_state *state,
477                                           struct drm_property *property,
478                                           u64 *val)
479 {
480         struct omap_drm_private *priv = plane->dev->dev_private;
481
482         if (property == priv->zorder_prop)
483                 *val = state->zpos;
484         else
485                 return -EINVAL;
486
487         return 0;
488 }
489
490 static const struct drm_plane_funcs omap_plane_funcs = {
491         .update_plane = drm_atomic_helper_update_plane,
492         .disable_plane = drm_atomic_helper_disable_plane,
493         .reset = omap_plane_reset,
494         .destroy = omap_plane_destroy,
495         .atomic_duplicate_state = omap_plane_atomic_duplicate_state,
496         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
497         .atomic_set_property = omap_plane_atomic_set_property,
498         .atomic_get_property = omap_plane_atomic_get_property,
499         .atomic_print_state = omap_plane_atomic_print_state,
500 };
501
502 static bool omap_plane_supports_yuv(struct drm_plane *plane)
503 {
504         struct omap_drm_private *priv = plane->dev->dev_private;
505         struct omap_plane *omap_plane = to_omap_plane(plane);
506         const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
507         u32 i;
508
509         for (i = 0; formats[i]; i++)
510                 if (formats[i] == DRM_FORMAT_YUYV ||
511                     formats[i] == DRM_FORMAT_UYVY ||
512                     formats[i] == DRM_FORMAT_NV12)
513                         return true;
514
515         return false;
516 }
517
518 /* initialize plane */
519 struct drm_plane *omap_plane_init(struct drm_device *dev,
520                 int idx, enum drm_plane_type type,
521                 u32 possible_crtcs)
522 {
523         struct omap_drm_private *priv = dev->dev_private;
524         unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
525         struct drm_plane *plane;
526         struct omap_plane *omap_plane;
527         unsigned int zpos;
528         int ret;
529         u32 nformats;
530         const u32 *formats;
531
532         if (WARN_ON(idx >= num_planes))
533                 return ERR_PTR(-EINVAL);
534
535         omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
536         if (!omap_plane)
537                 return ERR_PTR(-ENOMEM);
538
539         omap_plane->id = idx;
540
541         DBG("%d: type=%d", omap_plane->id, type);
542         DBG("   crtc_mask: 0x%04x", possible_crtcs);
543
544         formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
545         for (nformats = 0; formats[nformats]; ++nformats)
546                 ;
547
548         plane = &omap_plane->base;
549
550         ret = drm_universal_plane_init(dev, plane, possible_crtcs,
551                                        &omap_plane_funcs, formats,
552                                        nformats, NULL, type, NULL);
553         if (ret < 0)
554                 goto error;
555
556         drm_plane_helper_add(plane, &omap_plane_helper_funcs);
557
558         omap_plane_install_properties(plane, &plane->base);
559
560         /*
561          * Set the zpos default depending on whether we are a primary or overlay
562          * plane.
563          */
564         if (plane->type == DRM_PLANE_TYPE_PRIMARY)
565                 zpos = 0;
566         else
567                 zpos = omap_plane->id;
568         drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1);
569         drm_plane_create_alpha_property(plane);
570         drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
571                                              BIT(DRM_MODE_BLEND_COVERAGE));
572
573         if (omap_plane_supports_yuv(plane))
574                 drm_plane_create_color_properties(plane,
575                                                   BIT(DRM_COLOR_YCBCR_BT601) |
576                                                   BIT(DRM_COLOR_YCBCR_BT709),
577                                                   BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
578                                                   BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
579                                                   DRM_COLOR_YCBCR_BT601,
580                                                   DRM_COLOR_YCBCR_FULL_RANGE);
581
582         return plane;
583
584 error:
585         dev_err(dev->dev, "%s(): could not create plane: %d\n",
586                 __func__, omap_plane->id);
587
588         kfree(omap_plane);
589         return NULL;
590 }
This page took 0.066521 seconds and 4 git commands to generate.