linux/drivers/gpu/drm/imx/dcss/dcss-plane.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 NXP.
   4 */
   5
   6#include <drm/drm_atomic.h>
   7#include <drm/drm_atomic_helper.h>
   8#include <drm/drm_fb_cma_helper.h>
   9#include <drm/drm_gem_atomic_helper.h>
  10#include <drm/drm_gem_cma_helper.h>
  11
  12#include "dcss-dev.h"
  13#include "dcss-kms.h"
  14
  15static const u32 dcss_common_formats[] = {
  16        /* RGB */
  17        DRM_FORMAT_ARGB8888,
  18        DRM_FORMAT_XRGB8888,
  19        DRM_FORMAT_ABGR8888,
  20        DRM_FORMAT_XBGR8888,
  21        DRM_FORMAT_RGBA8888,
  22        DRM_FORMAT_RGBX8888,
  23        DRM_FORMAT_BGRA8888,
  24        DRM_FORMAT_BGRX8888,
  25        DRM_FORMAT_XRGB2101010,
  26        DRM_FORMAT_XBGR2101010,
  27        DRM_FORMAT_RGBX1010102,
  28        DRM_FORMAT_BGRX1010102,
  29        DRM_FORMAT_ARGB2101010,
  30        DRM_FORMAT_ABGR2101010,
  31        DRM_FORMAT_RGBA1010102,
  32        DRM_FORMAT_BGRA1010102,
  33};
  34
  35static const u64 dcss_video_format_modifiers[] = {
  36        DRM_FORMAT_MOD_LINEAR,
  37        DRM_FORMAT_MOD_INVALID,
  38};
  39
  40static const u64 dcss_graphics_format_modifiers[] = {
  41        DRM_FORMAT_MOD_VIVANTE_TILED,
  42        DRM_FORMAT_MOD_VIVANTE_SUPER_TILED,
  43        DRM_FORMAT_MOD_LINEAR,
  44        DRM_FORMAT_MOD_INVALID,
  45};
  46
  47static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p)
  48{
  49        return container_of(p, struct dcss_plane, base);
  50}
  51
  52static inline bool dcss_plane_fb_is_linear(const struct drm_framebuffer *fb)
  53{
  54        return ((fb->flags & DRM_MODE_FB_MODIFIERS) == 0) ||
  55               ((fb->flags & DRM_MODE_FB_MODIFIERS) != 0 &&
  56                fb->modifier == DRM_FORMAT_MOD_LINEAR);
  57}
  58
  59static void dcss_plane_destroy(struct drm_plane *plane)
  60{
  61        struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane,
  62                                                     base);
  63
  64        drm_plane_cleanup(plane);
  65        kfree(dcss_plane);
  66}
  67
  68static bool dcss_plane_format_mod_supported(struct drm_plane *plane,
  69                                            u32 format,
  70                                            u64 modifier)
  71{
  72        switch (plane->type) {
  73        case DRM_PLANE_TYPE_PRIMARY:
  74                switch (format) {
  75                case DRM_FORMAT_ARGB8888:
  76                case DRM_FORMAT_XRGB8888:
  77                case DRM_FORMAT_ARGB2101010:
  78                        return modifier == DRM_FORMAT_MOD_LINEAR ||
  79                               modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
  80                               modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED;
  81                default:
  82                        return modifier == DRM_FORMAT_MOD_LINEAR;
  83                }
  84                break;
  85        case DRM_PLANE_TYPE_OVERLAY:
  86                return modifier == DRM_FORMAT_MOD_LINEAR;
  87        default:
  88                return false;
  89        }
  90}
  91
  92static const struct drm_plane_funcs dcss_plane_funcs = {
  93        .update_plane           = drm_atomic_helper_update_plane,
  94        .disable_plane          = drm_atomic_helper_disable_plane,
  95        .destroy                = dcss_plane_destroy,
  96        .reset                  = drm_atomic_helper_plane_reset,
  97        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
  98        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
  99        .format_mod_supported   = dcss_plane_format_mod_supported,
 100};
 101
 102static bool dcss_plane_can_rotate(const struct drm_format_info *format,
 103                                  bool mod_present, u64 modifier,
 104                                  unsigned int rotation)
 105{
 106        bool linear_format = !mod_present || modifier == DRM_FORMAT_MOD_LINEAR;
 107        u32 supported_rotation = DRM_MODE_ROTATE_0;
 108
 109        if (!format->is_yuv && linear_format)
 110                supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
 111                                     DRM_MODE_REFLECT_MASK;
 112        else if (!format->is_yuv &&
 113                 (modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
 114                  modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED))
 115                supported_rotation = DRM_MODE_ROTATE_MASK |
 116                                     DRM_MODE_REFLECT_MASK;
 117        else if (format->is_yuv && linear_format &&
 118                 (format->format == DRM_FORMAT_NV12 ||
 119                  format->format == DRM_FORMAT_NV21))
 120                supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
 121                                     DRM_MODE_REFLECT_MASK;
 122
 123        return !!(rotation & supported_rotation);
 124}
 125
 126static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt)
 127{
 128        if (src_w < 64 &&
 129            (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21))
 130                return false;
 131        else if (src_w < 32 &&
 132                 (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY ||
 133                  pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU))
 134                return false;
 135
 136        return src_w >= 16 && src_h >= 8;
 137}
 138
 139static int dcss_plane_atomic_check(struct drm_plane *plane,
 140                                   struct drm_atomic_state *state)
 141{
 142        struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
 143                                                                                 plane);
 144        struct dcss_plane *dcss_plane = to_dcss_plane(plane);
 145        struct dcss_dev *dcss = plane->dev->dev_private;
 146        struct drm_framebuffer *fb = new_plane_state->fb;
 147        bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY;
 148        struct drm_gem_cma_object *cma_obj;
 149        struct drm_crtc_state *crtc_state;
 150        int hdisplay, vdisplay;
 151        int min, max;
 152        int ret;
 153
 154        if (!fb || !new_plane_state->crtc)
 155                return 0;
 156
 157        cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 158        WARN_ON(!cma_obj);
 159
 160        crtc_state = drm_atomic_get_existing_crtc_state(state,
 161                                                        new_plane_state->crtc);
 162
 163        hdisplay = crtc_state->adjusted_mode.hdisplay;
 164        vdisplay = crtc_state->adjusted_mode.vdisplay;
 165
 166        if (!dcss_plane_is_source_size_allowed(new_plane_state->src_w >> 16,
 167                                               new_plane_state->src_h >> 16,
 168                                               fb->format->format)) {
 169                DRM_DEBUG_KMS("Source plane size is not allowed!\n");
 170                return -EINVAL;
 171        }
 172
 173        dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num,
 174                                       &min, &max);
 175
 176        ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
 177                                                  min, max, !is_primary_plane,
 178                                                  false);
 179        if (ret)
 180                return ret;
 181
 182        if (!new_plane_state->visible)
 183                return 0;
 184
 185        if (!dcss_plane_can_rotate(fb->format,
 186                                   !!(fb->flags & DRM_MODE_FB_MODIFIERS),
 187                                   fb->modifier,
 188                                   new_plane_state->rotation)) {
 189                DRM_DEBUG_KMS("requested rotation is not allowed!\n");
 190                return -EINVAL;
 191        }
 192
 193        if ((new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0 ||
 194             new_plane_state->crtc_x + new_plane_state->crtc_w > hdisplay ||
 195             new_plane_state->crtc_y + new_plane_state->crtc_h > vdisplay) &&
 196            !dcss_plane_fb_is_linear(fb)) {
 197                DRM_DEBUG_KMS("requested cropping operation is not allowed!\n");
 198                return -EINVAL;
 199        }
 200
 201        if ((fb->flags & DRM_MODE_FB_MODIFIERS) &&
 202            !plane->funcs->format_mod_supported(plane,
 203                                fb->format->format,
 204                                fb->modifier)) {
 205                DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier);
 206                return -EINVAL;
 207        }
 208
 209        return 0;
 210}
 211
 212static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane)
 213{
 214        struct drm_plane *plane = &dcss_plane->base;
 215        struct drm_plane_state *state = plane->state;
 216        struct dcss_dev *dcss = plane->dev->dev_private;
 217        struct drm_framebuffer *fb = state->fb;
 218        const struct drm_format_info *format = fb->format;
 219        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 220        unsigned long p1_ba = 0, p2_ba = 0;
 221
 222        if (!format->is_yuv ||
 223            format->format == DRM_FORMAT_NV12 ||
 224            format->format == DRM_FORMAT_NV21)
 225                p1_ba = cma_obj->paddr + fb->offsets[0] +
 226                        fb->pitches[0] * (state->src.y1 >> 16) +
 227                        format->char_per_block[0] * (state->src.x1 >> 16);
 228        else if (format->format == DRM_FORMAT_UYVY ||
 229                 format->format == DRM_FORMAT_VYUY ||
 230                 format->format == DRM_FORMAT_YUYV ||
 231                 format->format == DRM_FORMAT_YVYU)
 232                p1_ba = cma_obj->paddr + fb->offsets[0] +
 233                        fb->pitches[0] * (state->src.y1 >> 16) +
 234                        2 * format->char_per_block[0] * (state->src.x1 >> 17);
 235
 236        if (format->format == DRM_FORMAT_NV12 ||
 237            format->format == DRM_FORMAT_NV21)
 238                p2_ba = cma_obj->paddr + fb->offsets[1] +
 239                        (((fb->pitches[1] >> 1) * (state->src.y1 >> 17) +
 240                        (state->src.x1 >> 17)) << 1);
 241
 242        dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba,
 243                          fb->pitches[0]);
 244}
 245
 246static bool dcss_plane_needs_setup(struct drm_plane_state *state,
 247                                   struct drm_plane_state *old_state)
 248{
 249        struct drm_framebuffer *fb = state->fb;
 250        struct drm_framebuffer *old_fb = old_state->fb;
 251
 252        return state->crtc_x != old_state->crtc_x ||
 253               state->crtc_y != old_state->crtc_y ||
 254               state->crtc_w != old_state->crtc_w ||
 255               state->crtc_h != old_state->crtc_h ||
 256               state->src_x  != old_state->src_x  ||
 257               state->src_y  != old_state->src_y  ||
 258               state->src_w  != old_state->src_w  ||
 259               state->src_h  != old_state->src_h  ||
 260               fb->format->format != old_fb->format->format ||
 261               fb->modifier  != old_fb->modifier ||
 262               state->rotation != old_state->rotation ||
 263               state->scaling_filter != old_state->scaling_filter;
 264}
 265
 266static void dcss_plane_atomic_update(struct drm_plane *plane,
 267                                     struct drm_atomic_state *state)
 268{
 269        struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
 270                                                                           plane);
 271        struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
 272                                                                           plane);
 273        struct dcss_plane *dcss_plane = to_dcss_plane(plane);
 274        struct dcss_dev *dcss = plane->dev->dev_private;
 275        struct drm_framebuffer *fb = new_state->fb;
 276        struct drm_crtc_state *crtc_state;
 277        bool modifiers_present;
 278        u32 src_w, src_h, dst_w, dst_h;
 279        struct drm_rect src, dst;
 280        bool enable = true;
 281        bool is_rotation_90_or_270;
 282
 283        if (!fb || !new_state->crtc || !new_state->visible)
 284                return;
 285
 286        crtc_state = new_state->crtc->state;
 287        modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS);
 288
 289        if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) &&
 290            !dcss_plane_needs_setup(new_state, old_state)) {
 291                dcss_plane_atomic_set_base(dcss_plane);
 292                return;
 293        }
 294
 295        src = plane->state->src;
 296        dst = plane->state->dst;
 297
 298        /*
 299         * The width and height after clipping.
 300         */
 301        src_w = drm_rect_width(&src) >> 16;
 302        src_h = drm_rect_height(&src) >> 16;
 303        dst_w = drm_rect_width(&dst);
 304        dst_h = drm_rect_height(&dst);
 305
 306        if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
 307            modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR)
 308                modifiers_present = false;
 309
 310        dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num,
 311                            new_state->fb->format,
 312                            modifiers_present ? fb->modifier :
 313                                                DRM_FORMAT_MOD_LINEAR);
 314        dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h);
 315        dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num,
 316                              new_state->rotation);
 317
 318        dcss_plane_atomic_set_base(dcss_plane);
 319
 320        is_rotation_90_or_270 = new_state->rotation & (DRM_MODE_ROTATE_90 |
 321                                                   DRM_MODE_ROTATE_270);
 322
 323        dcss_scaler_set_filter(dcss->scaler, dcss_plane->ch_num,
 324                               new_state->scaling_filter);
 325
 326        dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num,
 327                          new_state->fb->format,
 328                          is_rotation_90_or_270 ? src_h : src_w,
 329                          is_rotation_90_or_270 ? src_w : src_h,
 330                          dst_w, dst_h,
 331                          drm_mode_vrefresh(&crtc_state->mode));
 332
 333        dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
 334                               dst.x1, dst.y1, dst_w, dst_h);
 335        dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num,
 336                                 fb->format, new_state->alpha >> 8);
 337
 338        if (!dcss_plane->ch_num && (new_state->alpha >> 8) == 0)
 339                enable = false;
 340
 341        dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable);
 342        dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable);
 343
 344        if (!enable)
 345                dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
 346                                       0, 0, 0, 0);
 347
 348        dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable);
 349}
 350
 351static void dcss_plane_atomic_disable(struct drm_plane *plane,
 352                                      struct drm_atomic_state *state)
 353{
 354        struct dcss_plane *dcss_plane = to_dcss_plane(plane);
 355        struct dcss_dev *dcss = plane->dev->dev_private;
 356
 357        dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false);
 358        dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false);
 359        dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0);
 360        dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false);
 361}
 362
 363static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = {
 364        .atomic_check = dcss_plane_atomic_check,
 365        .atomic_update = dcss_plane_atomic_update,
 366        .atomic_disable = dcss_plane_atomic_disable,
 367};
 368
 369struct dcss_plane *dcss_plane_init(struct drm_device *drm,
 370                                   unsigned int possible_crtcs,
 371                                   enum drm_plane_type type,
 372                                   unsigned int zpos)
 373{
 374        struct dcss_plane *dcss_plane;
 375        const u64 *format_modifiers = dcss_video_format_modifiers;
 376        int ret;
 377
 378        if (zpos > 2)
 379                return ERR_PTR(-EINVAL);
 380
 381        dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL);
 382        if (!dcss_plane) {
 383                DRM_ERROR("failed to allocate plane\n");
 384                return ERR_PTR(-ENOMEM);
 385        }
 386
 387        if (type == DRM_PLANE_TYPE_PRIMARY)
 388                format_modifiers = dcss_graphics_format_modifiers;
 389
 390        ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs,
 391                                       &dcss_plane_funcs, dcss_common_formats,
 392                                       ARRAY_SIZE(dcss_common_formats),
 393                                       format_modifiers, type, NULL);
 394        if (ret) {
 395                DRM_ERROR("failed to initialize plane\n");
 396                kfree(dcss_plane);
 397                return ERR_PTR(ret);
 398        }
 399
 400        drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs);
 401
 402        ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos);
 403        if (ret)
 404                return ERR_PTR(ret);
 405
 406        drm_plane_create_scaling_filter_property(&dcss_plane->base,
 407                                        BIT(DRM_SCALING_FILTER_DEFAULT) |
 408                                        BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR));
 409
 410        drm_plane_create_rotation_property(&dcss_plane->base,
 411                                           DRM_MODE_ROTATE_0,
 412                                           DRM_MODE_ROTATE_0   |
 413                                           DRM_MODE_ROTATE_90  |
 414                                           DRM_MODE_ROTATE_180 |
 415                                           DRM_MODE_ROTATE_270 |
 416                                           DRM_MODE_REFLECT_X  |
 417                                           DRM_MODE_REFLECT_Y);
 418
 419        dcss_plane->ch_num = zpos;
 420
 421        return dcss_plane;
 422}
 423