Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
0004  * Author: Rob Clark <rob.clark@linaro.org>
0005  */
0006 
0007 #include <drm/drm_atomic.h>
0008 #include <drm/drm_atomic_helper.h>
0009 #include <drm/drm_blend.h>
0010 #include <drm/drm_gem_atomic_helper.h>
0011 #include <drm/drm_plane_helper.h>
0012 #include <drm/drm_fourcc.h>
0013 #include <drm/drm_framebuffer.h>
0014 
0015 #include "omap_dmm_tiler.h"
0016 #include "omap_drv.h"
0017 
0018 /*
0019  * plane funcs
0020  */
0021 
0022 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
0023 
0024 struct omap_plane_state {
0025     /* Must be first. */
0026     struct drm_plane_state base;
0027 
0028     struct omap_hw_overlay *overlay;
0029     struct omap_hw_overlay *r_overlay;  /* right overlay */
0030 };
0031 
0032 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
0033 
0034 struct omap_plane {
0035     struct drm_plane base;
0036     enum omap_plane_id id;
0037 };
0038 
0039 bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
0040 {
0041     struct omap_plane_state *omap_state = to_omap_plane_state(state);
0042 
0043     return !!omap_state->r_overlay;
0044 }
0045 
0046 static int omap_plane_prepare_fb(struct drm_plane *plane,
0047                  struct drm_plane_state *new_state)
0048 {
0049     if (!new_state->fb)
0050         return 0;
0051 
0052     drm_gem_plane_helper_prepare_fb(plane, new_state);
0053 
0054     return omap_framebuffer_pin(new_state->fb);
0055 }
0056 
0057 static void omap_plane_cleanup_fb(struct drm_plane *plane,
0058                   struct drm_plane_state *old_state)
0059 {
0060     if (old_state->fb)
0061         omap_framebuffer_unpin(old_state->fb);
0062 }
0063 
0064 static void omap_plane_atomic_update(struct drm_plane *plane,
0065                      struct drm_atomic_state *state)
0066 {
0067     struct omap_drm_private *priv = plane->dev->dev_private;
0068     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0069                                        plane);
0070     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0071                                        plane);
0072     struct omap_plane_state *new_omap_state;
0073     struct omap_plane_state *old_omap_state;
0074     struct omap_overlay_info info, r_info;
0075     enum omap_plane_id ovl_id, r_ovl_id;
0076     int ret;
0077     bool dual_ovl;
0078 
0079     new_omap_state = to_omap_plane_state(new_state);
0080     old_omap_state = to_omap_plane_state(old_state);
0081 
0082     dual_ovl = is_omap_plane_dual_overlay(new_state);
0083 
0084     /* Cleanup previously held overlay if needed */
0085     if (old_omap_state->overlay)
0086         omap_overlay_update_state(priv, old_omap_state->overlay);
0087     if (old_omap_state->r_overlay)
0088         omap_overlay_update_state(priv, old_omap_state->r_overlay);
0089 
0090     if (!new_omap_state->overlay) {
0091         DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
0092         return;
0093     }
0094 
0095     ovl_id = new_omap_state->overlay->id;
0096     DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
0097         new_state->fb);
0098 
0099     memset(&info, 0, sizeof(info));
0100     info.rotation_type = OMAP_DSS_ROT_NONE;
0101     info.rotation = DRM_MODE_ROTATE_0;
0102     info.global_alpha = new_state->alpha >> 8;
0103     info.zorder = new_state->normalized_zpos;
0104     if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
0105         info.pre_mult_alpha = 1;
0106     else
0107         info.pre_mult_alpha = 0;
0108     info.color_encoding = new_state->color_encoding;
0109     info.color_range = new_state->color_range;
0110 
0111     r_info = info;
0112 
0113     /* update scanout: */
0114     omap_framebuffer_update_scanout(new_state->fb, new_state, &info,
0115                     dual_ovl ? &r_info : NULL);
0116 
0117     DBG("%s: %dx%d -> %dx%d (%d)",
0118             new_omap_state->overlay->name, info.width, info.height,
0119             info.out_width, info.out_height, info.screen_width);
0120     DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
0121             &info.paddr, &info.p_uv_addr);
0122 
0123     if (dual_ovl) {
0124         r_ovl_id = new_omap_state->r_overlay->id;
0125         /*
0126          * If the current plane uses 2 hw planes the very next
0127          * zorder is used by the r_overlay so we just use the
0128          * main overlay zorder + 1
0129          */
0130         r_info.zorder = info.zorder + 1;
0131 
0132         DBG("%s: %dx%d -> %dx%d (%d)",
0133             new_omap_state->r_overlay->name,
0134             r_info.width, r_info.height,
0135             r_info.out_width, r_info.out_height, r_info.screen_width);
0136         DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y,
0137             &r_info.paddr, &r_info.p_uv_addr);
0138     }
0139 
0140     /* and finally, update omapdss: */
0141     ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
0142                   omap_crtc_timings(new_state->crtc), false,
0143                   omap_crtc_channel(new_state->crtc));
0144     if (ret) {
0145         dev_err(plane->dev->dev, "Failed to setup plane %s\n",
0146             plane->name);
0147         dispc_ovl_enable(priv->dispc, ovl_id, false);
0148         return;
0149     }
0150 
0151     dispc_ovl_enable(priv->dispc, ovl_id, true);
0152 
0153     if (dual_ovl) {
0154         ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info,
0155                       omap_crtc_timings(new_state->crtc), false,
0156                       omap_crtc_channel(new_state->crtc));
0157         if (ret) {
0158             dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n",
0159                 plane->name);
0160             dispc_ovl_enable(priv->dispc, r_ovl_id, false);
0161             dispc_ovl_enable(priv->dispc, ovl_id, false);
0162             return;
0163         }
0164 
0165         dispc_ovl_enable(priv->dispc, r_ovl_id, true);
0166     }
0167 }
0168 
0169 static void omap_plane_atomic_disable(struct drm_plane *plane,
0170                       struct drm_atomic_state *state)
0171 {
0172     struct omap_drm_private *priv = plane->dev->dev_private;
0173     struct omap_plane *omap_plane = to_omap_plane(plane);
0174     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0175                                        plane);
0176     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0177                                        plane);
0178     struct omap_plane_state *new_omap_state;
0179     struct omap_plane_state *old_omap_state;
0180 
0181     new_omap_state = to_omap_plane_state(new_state);
0182     old_omap_state = to_omap_plane_state(old_state);
0183 
0184     if (!old_omap_state->overlay)
0185         return;
0186 
0187     new_state->rotation = DRM_MODE_ROTATE_0;
0188     new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
0189 
0190     omap_overlay_update_state(priv, old_omap_state->overlay);
0191     new_omap_state->overlay = NULL;
0192 
0193     if (is_omap_plane_dual_overlay(old_state)) {
0194         omap_overlay_update_state(priv, old_omap_state->r_overlay);
0195         new_omap_state->r_overlay = NULL;
0196     }
0197 }
0198 
0199 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
0200 
0201 static int omap_plane_atomic_check(struct drm_plane *plane,
0202                    struct drm_atomic_state *state)
0203 {
0204     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0205                                          plane);
0206     struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
0207                                          plane);
0208     struct omap_drm_private *priv = plane->dev->dev_private;
0209     struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
0210     struct omap_global_state *omap_overlay_global_state;
0211     struct drm_crtc_state *crtc_state;
0212     bool new_r_hw_overlay = false;
0213     bool new_hw_overlay = false;
0214     u32 max_width, max_height;
0215     struct drm_crtc *crtc;
0216     u16 width, height;
0217     u32 caps = 0;
0218     u32 fourcc;
0219     int ret;
0220 
0221     omap_overlay_global_state = omap_get_global_state(state);
0222     if (IS_ERR(omap_overlay_global_state))
0223         return PTR_ERR(omap_overlay_global_state);
0224 
0225     dispc_ovl_get_max_size(priv->dispc, &width, &height);
0226     max_width = width << 16;
0227     max_height = height << 16;
0228 
0229     crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;
0230     if (!crtc)
0231         return 0;
0232 
0233     crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
0234     /* we should have a crtc state if the plane is attached to a crtc */
0235     if (WARN_ON(!crtc_state))
0236         return 0;
0237 
0238     /*
0239      * Note: these are just sanity checks to filter out totally bad scaling
0240      * factors. The real limits must be calculated case by case, and
0241      * unfortunately we currently do those checks only at the commit
0242      * phase in dispc.
0243      */
0244     ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
0245                           FRAC_16_16(1, 8), FRAC_16_16(8, 1),
0246                           true, true);
0247     if (ret)
0248         return ret;
0249 
0250     DBG("%s: visible %d -> %d", plane->name,
0251         old_plane_state->visible, new_plane_state->visible);
0252 
0253     if (!new_plane_state->visible) {
0254         omap_overlay_release(state, omap_state->overlay);
0255         omap_overlay_release(state, omap_state->r_overlay);
0256         omap_state->overlay = NULL;
0257         omap_state->r_overlay = NULL;
0258         return 0;
0259     }
0260 
0261     if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)
0262         return -EINVAL;
0263 
0264     if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay)
0265         return -EINVAL;
0266 
0267     if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay)
0268         return -EINVAL;
0269 
0270     /* Make sure dimensions are within bounds. */
0271     if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)
0272         return -EINVAL;
0273 
0274 
0275     if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) {
0276         bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv;
0277 
0278         if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||
0279                       new_plane_state->crtc_w / 2 & 1)) {
0280             /*
0281              * When calculating the split overlay width
0282              * and it yield an odd value we will need to adjust
0283              * the indivual width +/- 1. So make sure it fits
0284              */
0285             if (new_plane_state->src_w <= ((2 * width - 1) << 16) &&
0286                 new_plane_state->crtc_w <= (2 * width - 1))
0287                 new_r_hw_overlay = true;
0288             else
0289                 return -EINVAL;
0290         } else {
0291             if (new_plane_state->src_w <= (2 * max_width) &&
0292                 new_plane_state->crtc_w <= (2 * width))
0293                 new_r_hw_overlay = true;
0294             else
0295                 return -EINVAL;
0296         }
0297     }
0298 
0299     if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&
0300         !omap_framebuffer_supports_rotation(new_plane_state->fb))
0301         return -EINVAL;
0302 
0303     if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
0304         (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)
0305         caps |= OMAP_DSS_OVL_CAP_SCALE;
0306 
0307     fourcc = new_plane_state->fb->format->format;
0308 
0309     /*
0310      * (re)allocate hw overlay if we don't have one or
0311      * there is a caps mismatch
0312      */
0313     if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {
0314         new_hw_overlay = true;
0315     } else {
0316         /* check supported format */
0317         if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,
0318                             fourcc))
0319             new_hw_overlay = true;
0320     }
0321 
0322     /*
0323      * check if we need two overlays and only have 1 or
0324      * if we had 2 overlays but will only need 1
0325      */
0326     if ((new_r_hw_overlay && !omap_state->r_overlay) ||
0327         (!new_r_hw_overlay && omap_state->r_overlay))
0328         new_hw_overlay = true;
0329 
0330     if (new_hw_overlay) {
0331         struct omap_hw_overlay *old_ovl = omap_state->overlay;
0332         struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay;
0333         struct omap_hw_overlay *new_ovl = NULL;
0334         struct omap_hw_overlay *new_r_ovl = NULL;
0335 
0336         omap_overlay_release(state, old_ovl);
0337         omap_overlay_release(state, old_r_ovl);
0338 
0339         ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,
0340                       new_r_hw_overlay ? &new_r_ovl : NULL);
0341         if (ret) {
0342             DBG("%s: failed to assign hw_overlay", plane->name);
0343             omap_state->overlay = NULL;
0344             omap_state->r_overlay = NULL;
0345             return ret;
0346         }
0347 
0348         omap_state->overlay = new_ovl;
0349         if (new_r_hw_overlay)
0350             omap_state->r_overlay = new_r_ovl;
0351         else
0352             omap_state->r_overlay = NULL;
0353     }
0354 
0355     DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
0356 
0357     if (omap_state->r_overlay)
0358         DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);
0359 
0360     return 0;
0361 }
0362 
0363 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = {
0364     .prepare_fb = omap_plane_prepare_fb,
0365     .cleanup_fb = omap_plane_cleanup_fb,
0366     .atomic_check = omap_plane_atomic_check,
0367     .atomic_update = omap_plane_atomic_update,
0368     .atomic_disable = omap_plane_atomic_disable,
0369 };
0370 
0371 static void omap_plane_destroy(struct drm_plane *plane)
0372 {
0373     struct omap_plane *omap_plane = to_omap_plane(plane);
0374 
0375     DBG("%s", plane->name);
0376 
0377     drm_plane_cleanup(plane);
0378 
0379     kfree(omap_plane);
0380 }
0381 
0382 /* helper to install properties which are common to planes and crtcs */
0383 void omap_plane_install_properties(struct drm_plane *plane,
0384         struct drm_mode_object *obj)
0385 {
0386     struct drm_device *dev = plane->dev;
0387     struct omap_drm_private *priv = dev->dev_private;
0388 
0389     if (priv->has_dmm) {
0390         if (!plane->rotation_property)
0391             drm_plane_create_rotation_property(plane,
0392                                DRM_MODE_ROTATE_0,
0393                                DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
0394                                DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
0395                                DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
0396 
0397         /* Attach the rotation property also to the crtc object */
0398         if (plane->rotation_property && obj != &plane->base)
0399             drm_object_attach_property(obj, plane->rotation_property,
0400                            DRM_MODE_ROTATE_0);
0401     }
0402 
0403     drm_object_attach_property(obj, priv->zorder_prop, 0);
0404 }
0405 
0406 static void omap_plane_reset(struct drm_plane *plane)
0407 {
0408     struct omap_plane_state *omap_state;
0409 
0410     if (plane->state)
0411         drm_atomic_helper_plane_destroy_state(plane, plane->state);
0412 
0413     omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL);
0414     if (!omap_state)
0415         return;
0416 
0417     __drm_atomic_helper_plane_reset(plane, &omap_state->base);
0418 }
0419 
0420 static struct drm_plane_state *
0421 omap_plane_atomic_duplicate_state(struct drm_plane *plane)
0422 {
0423     struct omap_plane_state *state, *current_state;
0424 
0425     if (WARN_ON(!plane->state))
0426         return NULL;
0427 
0428     current_state = to_omap_plane_state(plane->state);
0429 
0430     state = kmalloc(sizeof(*state), GFP_KERNEL);
0431     if (!state)
0432         return NULL;
0433 
0434     __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
0435 
0436     state->overlay = current_state->overlay;
0437     state->r_overlay = current_state->r_overlay;
0438 
0439     return &state->base;
0440 }
0441 
0442 static void omap_plane_atomic_print_state(struct drm_printer *p,
0443                       const struct drm_plane_state *state)
0444 {
0445     struct omap_plane_state *omap_state = to_omap_plane_state(state);
0446 
0447     if (omap_state->overlay)
0448         drm_printf(p, "\toverlay=%s (caps=0x%x)\n",
0449                omap_state->overlay->name,
0450                omap_state->overlay->caps);
0451     else
0452         drm_printf(p, "\toverlay=None\n");
0453     if (omap_state->r_overlay)
0454         drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n",
0455                omap_state->r_overlay->name,
0456                omap_state->r_overlay->caps);
0457     else
0458         drm_printf(p, "\tr_overlay=None\n");
0459 }
0460 
0461 static int omap_plane_atomic_set_property(struct drm_plane *plane,
0462                       struct drm_plane_state *state,
0463                       struct drm_property *property,
0464                       u64 val)
0465 {
0466     struct omap_drm_private *priv = plane->dev->dev_private;
0467 
0468     if (property == priv->zorder_prop)
0469         state->zpos = val;
0470     else
0471         return -EINVAL;
0472 
0473     return 0;
0474 }
0475 
0476 static int omap_plane_atomic_get_property(struct drm_plane *plane,
0477                       const struct drm_plane_state *state,
0478                       struct drm_property *property,
0479                       u64 *val)
0480 {
0481     struct omap_drm_private *priv = plane->dev->dev_private;
0482 
0483     if (property == priv->zorder_prop)
0484         *val = state->zpos;
0485     else
0486         return -EINVAL;
0487 
0488     return 0;
0489 }
0490 
0491 static const struct drm_plane_funcs omap_plane_funcs = {
0492     .update_plane = drm_atomic_helper_update_plane,
0493     .disable_plane = drm_atomic_helper_disable_plane,
0494     .reset = omap_plane_reset,
0495     .destroy = omap_plane_destroy,
0496     .atomic_duplicate_state = omap_plane_atomic_duplicate_state,
0497     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
0498     .atomic_set_property = omap_plane_atomic_set_property,
0499     .atomic_get_property = omap_plane_atomic_get_property,
0500     .atomic_print_state = omap_plane_atomic_print_state,
0501 };
0502 
0503 static bool omap_plane_supports_yuv(struct drm_plane *plane)
0504 {
0505     struct omap_drm_private *priv = plane->dev->dev_private;
0506     struct omap_plane *omap_plane = to_omap_plane(plane);
0507     const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
0508     u32 i;
0509 
0510     for (i = 0; formats[i]; i++)
0511         if (formats[i] == DRM_FORMAT_YUYV ||
0512             formats[i] == DRM_FORMAT_UYVY ||
0513             formats[i] == DRM_FORMAT_NV12)
0514             return true;
0515 
0516     return false;
0517 }
0518 
0519 /* initialize plane */
0520 struct drm_plane *omap_plane_init(struct drm_device *dev,
0521         int idx, enum drm_plane_type type,
0522         u32 possible_crtcs)
0523 {
0524     struct omap_drm_private *priv = dev->dev_private;
0525     unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
0526     struct drm_plane *plane;
0527     struct omap_plane *omap_plane;
0528     unsigned int zpos;
0529     int ret;
0530     u32 nformats;
0531     const u32 *formats;
0532 
0533     if (WARN_ON(idx >= num_planes))
0534         return ERR_PTR(-EINVAL);
0535 
0536     omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
0537     if (!omap_plane)
0538         return ERR_PTR(-ENOMEM);
0539 
0540     omap_plane->id = idx;
0541 
0542     DBG("%d: type=%d", omap_plane->id, type);
0543     DBG("   crtc_mask: 0x%04x", possible_crtcs);
0544 
0545     formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
0546     for (nformats = 0; formats[nformats]; ++nformats)
0547         ;
0548 
0549     plane = &omap_plane->base;
0550 
0551     ret = drm_universal_plane_init(dev, plane, possible_crtcs,
0552                        &omap_plane_funcs, formats,
0553                        nformats, NULL, type, NULL);
0554     if (ret < 0)
0555         goto error;
0556 
0557     drm_plane_helper_add(plane, &omap_plane_helper_funcs);
0558 
0559     omap_plane_install_properties(plane, &plane->base);
0560 
0561     /*
0562      * Set the zpos default depending on whether we are a primary or overlay
0563      * plane.
0564      */
0565     if (plane->type == DRM_PLANE_TYPE_PRIMARY)
0566         zpos = 0;
0567     else
0568         zpos = omap_plane->id;
0569     drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1);
0570     drm_plane_create_alpha_property(plane);
0571     drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
0572                          BIT(DRM_MODE_BLEND_COVERAGE));
0573 
0574     if (omap_plane_supports_yuv(plane))
0575         drm_plane_create_color_properties(plane,
0576                           BIT(DRM_COLOR_YCBCR_BT601) |
0577                           BIT(DRM_COLOR_YCBCR_BT709),
0578                           BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
0579                           BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
0580                           DRM_COLOR_YCBCR_BT601,
0581                           DRM_COLOR_YCBCR_FULL_RANGE);
0582 
0583     return plane;
0584 
0585 error:
0586     dev_err(dev->dev, "%s(): could not create plane: %d\n",
0587         __func__, omap_plane->id);
0588 
0589     kfree(omap_plane);
0590     return NULL;
0591 }