linux/drivers/gpu/drm/zte/zx_plane.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2016 Linaro Ltd.
   4 * Copyright 2016 ZTE Corporation.
   5 */
   6
   7#include <drm/drm_atomic.h>
   8#include <drm/drm_atomic_helper.h>
   9#include <drm/drm_fb_cma_helper.h>
  10#include <drm/drm_fourcc.h>
  11#include <drm/drm_gem_cma_helper.h>
  12#include <drm/drm_modeset_helper_vtables.h>
  13#include <drm/drm_plane_helper.h>
  14
  15#include "zx_common_regs.h"
  16#include "zx_drm_drv.h"
  17#include "zx_plane.h"
  18#include "zx_plane_regs.h"
  19#include "zx_vou.h"
  20
  21static const uint32_t gl_formats[] = {
  22        DRM_FORMAT_ARGB8888,
  23        DRM_FORMAT_XRGB8888,
  24        DRM_FORMAT_RGB888,
  25        DRM_FORMAT_RGB565,
  26        DRM_FORMAT_ARGB1555,
  27        DRM_FORMAT_ARGB4444,
  28};
  29
  30static const uint32_t vl_formats[] = {
  31        DRM_FORMAT_NV12,        /* Semi-planar YUV420 */
  32        DRM_FORMAT_YUV420,      /* Planar YUV420 */
  33        DRM_FORMAT_YUYV,        /* Packed YUV422 */
  34        DRM_FORMAT_YVYU,
  35        DRM_FORMAT_UYVY,
  36        DRM_FORMAT_VYUY,
  37        DRM_FORMAT_YUV444,      /* YUV444 8bit */
  38        /*
  39         * TODO: add formats below that HW supports:
  40         *  - YUV420 P010
  41         *  - YUV420 Hantro
  42         *  - YUV444 10bit
  43         */
  44};
  45
  46#define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
  47
  48static int zx_vl_plane_atomic_check(struct drm_plane *plane,
  49                                    struct drm_plane_state *plane_state)
  50{
  51        struct drm_framebuffer *fb = plane_state->fb;
  52        struct drm_crtc *crtc = plane_state->crtc;
  53        struct drm_crtc_state *crtc_state;
  54        int min_scale = FRAC_16_16(1, 8);
  55        int max_scale = FRAC_16_16(8, 1);
  56
  57        if (!crtc || WARN_ON(!fb))
  58                return 0;
  59
  60        crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
  61                                                        crtc);
  62        if (WARN_ON(!crtc_state))
  63                return -EINVAL;
  64
  65        /* nothing to check when disabling or disabled */
  66        if (!crtc_state->enable)
  67                return 0;
  68
  69        /* plane must be enabled */
  70        if (!plane_state->crtc)
  71                return -EINVAL;
  72
  73        return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
  74                                                   min_scale, max_scale,
  75                                                   true, true);
  76}
  77
  78static int zx_vl_get_fmt(uint32_t format)
  79{
  80        switch (format) {
  81        case DRM_FORMAT_NV12:
  82                return VL_FMT_YUV420;
  83        case DRM_FORMAT_YUV420:
  84                return VL_YUV420_PLANAR | VL_FMT_YUV420;
  85        case DRM_FORMAT_YUYV:
  86                return VL_YUV422_YUYV | VL_FMT_YUV422;
  87        case DRM_FORMAT_YVYU:
  88                return VL_YUV422_YVYU | VL_FMT_YUV422;
  89        case DRM_FORMAT_UYVY:
  90                return VL_YUV422_UYVY | VL_FMT_YUV422;
  91        case DRM_FORMAT_VYUY:
  92                return VL_YUV422_VYUY | VL_FMT_YUV422;
  93        case DRM_FORMAT_YUV444:
  94                return VL_FMT_YUV444_8BIT;
  95        default:
  96                WARN_ONCE(1, "invalid pixel format %d\n", format);
  97                return -EINVAL;
  98        }
  99}
 100
 101static inline void zx_vl_set_update(struct zx_plane *zplane)
 102{
 103        void __iomem *layer = zplane->layer;
 104
 105        zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
 106}
 107
 108static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
 109{
 110        zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
 111}
 112
 113static int zx_vl_rsz_get_fmt(uint32_t format)
 114{
 115        switch (format) {
 116        case DRM_FORMAT_NV12:
 117        case DRM_FORMAT_YUV420:
 118                return RSZ_VL_FMT_YCBCR420;
 119        case DRM_FORMAT_YUYV:
 120        case DRM_FORMAT_YVYU:
 121        case DRM_FORMAT_UYVY:
 122        case DRM_FORMAT_VYUY:
 123                return RSZ_VL_FMT_YCBCR422;
 124        case DRM_FORMAT_YUV444:
 125                return RSZ_VL_FMT_YCBCR444;
 126        default:
 127                WARN_ONCE(1, "invalid pixel format %d\n", format);
 128                return -EINVAL;
 129        }
 130}
 131
 132static inline u32 rsz_step_value(u32 src, u32 dst)
 133{
 134        u32 val = 0;
 135
 136        if (src == dst)
 137                val = 0;
 138        else if (src < dst)
 139                val = RSZ_PARA_STEP((src << 16) / dst);
 140        else if (src > dst)
 141                val = RSZ_DATA_STEP(src / dst) |
 142                      RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
 143
 144        return val;
 145}
 146
 147static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
 148                            u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
 149{
 150        void __iomem *rsz = zplane->rsz;
 151        u32 src_chroma_w = src_w;
 152        u32 src_chroma_h = src_h;
 153        int fmt;
 154
 155        /* Set up source and destination resolution */
 156        zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
 157        zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
 158
 159        /* Configure data format for VL RSZ */
 160        fmt = zx_vl_rsz_get_fmt(format);
 161        if (fmt >= 0)
 162                zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
 163
 164        /* Calculate Chroma height and width */
 165        if (fmt == RSZ_VL_FMT_YCBCR420) {
 166                src_chroma_w = src_w >> 1;
 167                src_chroma_h = src_h >> 1;
 168        } else if (fmt == RSZ_VL_FMT_YCBCR422) {
 169                src_chroma_w = src_w >> 1;
 170        }
 171
 172        /* Set up Luma and Chroma step registers */
 173        zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
 174        zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
 175        zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
 176        zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
 177
 178        zx_vl_rsz_set_update(zplane);
 179}
 180
 181static void zx_vl_plane_atomic_update(struct drm_plane *plane,
 182                                      struct drm_plane_state *old_state)
 183{
 184        struct zx_plane *zplane = to_zx_plane(plane);
 185        struct drm_plane_state *state = plane->state;
 186        struct drm_framebuffer *fb = state->fb;
 187        struct drm_rect *src = &state->src;
 188        struct drm_rect *dst = &state->dst;
 189        struct drm_gem_cma_object *cma_obj;
 190        void __iomem *layer = zplane->layer;
 191        void __iomem *hbsc = zplane->hbsc;
 192        void __iomem *paddr_reg;
 193        dma_addr_t paddr;
 194        u32 src_x, src_y, src_w, src_h;
 195        u32 dst_x, dst_y, dst_w, dst_h;
 196        uint32_t format;
 197        int fmt;
 198        int i;
 199
 200        if (!fb)
 201                return;
 202
 203        format = fb->format->format;
 204
 205        src_x = src->x1 >> 16;
 206        src_y = src->y1 >> 16;
 207        src_w = drm_rect_width(src) >> 16;
 208        src_h = drm_rect_height(src) >> 16;
 209
 210        dst_x = dst->x1;
 211        dst_y = dst->y1;
 212        dst_w = drm_rect_width(dst);
 213        dst_h = drm_rect_height(dst);
 214
 215        /* Set up data address registers for Y, Cb and Cr planes */
 216        paddr_reg = layer + VL_Y;
 217        for (i = 0; i < fb->format->num_planes; i++) {
 218                cma_obj = drm_fb_cma_get_gem_obj(fb, i);
 219                paddr = cma_obj->paddr + fb->offsets[i];
 220                paddr += src_y * fb->pitches[i];
 221                paddr += src_x * fb->format->cpp[i];
 222                zx_writel(paddr_reg, paddr);
 223                paddr_reg += 4;
 224        }
 225
 226        /* Set up source height/width register */
 227        zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
 228
 229        /* Set up start position register */
 230        zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
 231
 232        /* Set up end position register */
 233        zx_writel(layer + VL_POS_END,
 234                  GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
 235
 236        /* Strides of Cb and Cr planes should be identical */
 237        zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
 238                  CHROMA_STRIDE(fb->pitches[1]));
 239
 240        /* Set up video layer data format */
 241        fmt = zx_vl_get_fmt(format);
 242        if (fmt >= 0)
 243                zx_writel(layer + VL_CTRL1, fmt);
 244
 245        /* Always use scaler since it exists (set for not bypass) */
 246        zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
 247                       VL_SCALER_BYPASS_MODE);
 248
 249        zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
 250
 251        /* Enable HBSC block */
 252        zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
 253
 254        zx_vou_layer_enable(plane);
 255
 256        zx_vl_set_update(zplane);
 257}
 258
 259static void zx_plane_atomic_disable(struct drm_plane *plane,
 260                                    struct drm_plane_state *old_state)
 261{
 262        struct zx_plane *zplane = to_zx_plane(plane);
 263        void __iomem *hbsc = zplane->hbsc;
 264
 265        zx_vou_layer_disable(plane, old_state);
 266
 267        /* Disable HBSC block */
 268        zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
 269}
 270
 271static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
 272        .atomic_check = zx_vl_plane_atomic_check,
 273        .atomic_update = zx_vl_plane_atomic_update,
 274        .atomic_disable = zx_plane_atomic_disable,
 275};
 276
 277static int zx_gl_plane_atomic_check(struct drm_plane *plane,
 278                                    struct drm_plane_state *plane_state)
 279{
 280        struct drm_framebuffer *fb = plane_state->fb;
 281        struct drm_crtc *crtc = plane_state->crtc;
 282        struct drm_crtc_state *crtc_state;
 283
 284        if (!crtc || WARN_ON(!fb))
 285                return 0;
 286
 287        crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
 288                                                        crtc);
 289        if (WARN_ON(!crtc_state))
 290                return -EINVAL;
 291
 292        /* nothing to check when disabling or disabled */
 293        if (!crtc_state->enable)
 294                return 0;
 295
 296        /* plane must be enabled */
 297        if (!plane_state->crtc)
 298                return -EINVAL;
 299
 300        return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
 301                                                   DRM_PLANE_HELPER_NO_SCALING,
 302                                                   DRM_PLANE_HELPER_NO_SCALING,
 303                                                   false, true);
 304}
 305
 306static int zx_gl_get_fmt(uint32_t format)
 307{
 308        switch (format) {
 309        case DRM_FORMAT_ARGB8888:
 310        case DRM_FORMAT_XRGB8888:
 311                return GL_FMT_ARGB8888;
 312        case DRM_FORMAT_RGB888:
 313                return GL_FMT_RGB888;
 314        case DRM_FORMAT_RGB565:
 315                return GL_FMT_RGB565;
 316        case DRM_FORMAT_ARGB1555:
 317                return GL_FMT_ARGB1555;
 318        case DRM_FORMAT_ARGB4444:
 319                return GL_FMT_ARGB4444;
 320        default:
 321                WARN_ONCE(1, "invalid pixel format %d\n", format);
 322                return -EINVAL;
 323        }
 324}
 325
 326static inline void zx_gl_set_update(struct zx_plane *zplane)
 327{
 328        void __iomem *layer = zplane->layer;
 329
 330        zx_writel_mask(layer + GL_CTRL0, GL_UPDATE, GL_UPDATE);
 331}
 332
 333static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
 334{
 335        zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
 336}
 337
 338static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
 339                            u32 dst_w, u32 dst_h)
 340{
 341        void __iomem *rsz = zplane->rsz;
 342
 343        zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
 344        zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
 345
 346        zx_gl_rsz_set_update(zplane);
 347}
 348
 349static void zx_gl_plane_atomic_update(struct drm_plane *plane,
 350                                      struct drm_plane_state *old_state)
 351{
 352        struct zx_plane *zplane = to_zx_plane(plane);
 353        struct drm_framebuffer *fb = plane->state->fb;
 354        struct drm_gem_cma_object *cma_obj;
 355        void __iomem *layer = zplane->layer;
 356        void __iomem *csc = zplane->csc;
 357        void __iomem *hbsc = zplane->hbsc;
 358        u32 src_x, src_y, src_w, src_h;
 359        u32 dst_x, dst_y, dst_w, dst_h;
 360        unsigned int bpp;
 361        uint32_t format;
 362        dma_addr_t paddr;
 363        u32 stride;
 364        int fmt;
 365
 366        if (!fb)
 367                return;
 368
 369        format = fb->format->format;
 370        stride = fb->pitches[0];
 371
 372        src_x = plane->state->src_x >> 16;
 373        src_y = plane->state->src_y >> 16;
 374        src_w = plane->state->src_w >> 16;
 375        src_h = plane->state->src_h >> 16;
 376
 377        dst_x = plane->state->crtc_x;
 378        dst_y = plane->state->crtc_y;
 379        dst_w = plane->state->crtc_w;
 380        dst_h = plane->state->crtc_h;
 381
 382        bpp = fb->format->cpp[0];
 383
 384        cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 385        paddr = cma_obj->paddr + fb->offsets[0];
 386        paddr += src_y * stride + src_x * bpp / 8;
 387        zx_writel(layer + GL_ADDR, paddr);
 388
 389        /* Set up source height/width register */
 390        zx_writel(layer + GL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
 391
 392        /* Set up start position register */
 393        zx_writel(layer + GL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
 394
 395        /* Set up end position register */
 396        zx_writel(layer + GL_POS_END,
 397                  GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
 398
 399        /* Set up stride register */
 400        zx_writel(layer + GL_STRIDE, stride & 0xffff);
 401
 402        /* Set up graphic layer data format */
 403        fmt = zx_gl_get_fmt(format);
 404        if (fmt >= 0)
 405                zx_writel_mask(layer + GL_CTRL1, GL_DATA_FMT_MASK,
 406                               fmt << GL_DATA_FMT_SHIFT);
 407
 408        /* Initialize global alpha with a sane value */
 409        zx_writel_mask(layer + GL_CTRL2, GL_GLOBAL_ALPHA_MASK,
 410                       0xff << GL_GLOBAL_ALPHA_SHIFT);
 411
 412        /* Setup CSC for the GL */
 413        if (dst_h > 720)
 414                zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
 415                               CSC_BT709_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT);
 416        else
 417                zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
 418                               CSC_BT601_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT);
 419        zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, CSC_WORK_ENABLE);
 420
 421        /* Always use scaler since it exists (set for not bypass) */
 422        zx_writel_mask(layer + GL_CTRL3, GL_SCALER_BYPASS_MODE,
 423                       GL_SCALER_BYPASS_MODE);
 424
 425        zx_gl_rsz_setup(zplane, src_w, src_h, dst_w, dst_h);
 426
 427        /* Enable HBSC block */
 428        zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
 429
 430        zx_vou_layer_enable(plane);
 431
 432        zx_gl_set_update(zplane);
 433}
 434
 435static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
 436        .atomic_check = zx_gl_plane_atomic_check,
 437        .atomic_update = zx_gl_plane_atomic_update,
 438        .atomic_disable = zx_plane_atomic_disable,
 439};
 440
 441static void zx_plane_destroy(struct drm_plane *plane)
 442{
 443        drm_plane_cleanup(plane);
 444}
 445
 446static const struct drm_plane_funcs zx_plane_funcs = {
 447        .update_plane = drm_atomic_helper_update_plane,
 448        .disable_plane = drm_atomic_helper_disable_plane,
 449        .destroy = zx_plane_destroy,
 450        .reset = drm_atomic_helper_plane_reset,
 451        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 452        .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 453};
 454
 455void zx_plane_set_update(struct drm_plane *plane)
 456{
 457        struct zx_plane *zplane = to_zx_plane(plane);
 458
 459        /* Do nothing if the plane is not enabled */
 460        if (!plane->state->crtc)
 461                return;
 462
 463        switch (plane->type) {
 464        case DRM_PLANE_TYPE_PRIMARY:
 465                zx_gl_rsz_set_update(zplane);
 466                zx_gl_set_update(zplane);
 467                break;
 468        case DRM_PLANE_TYPE_OVERLAY:
 469                zx_vl_rsz_set_update(zplane);
 470                zx_vl_set_update(zplane);
 471                break;
 472        default:
 473                WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
 474        }
 475}
 476
 477static void zx_plane_hbsc_init(struct zx_plane *zplane)
 478{
 479        void __iomem *hbsc = zplane->hbsc;
 480
 481        /*
 482         *  Initialize HBSC block with a sane configuration per recommedation
 483         *  from ZTE BSP code.
 484         */
 485        zx_writel(hbsc + HBSC_SATURATION, 0x200);
 486        zx_writel(hbsc + HBSC_HUE, 0x0);
 487        zx_writel(hbsc + HBSC_BRIGHT, 0x0);
 488        zx_writel(hbsc + HBSC_CONTRAST, 0x200);
 489
 490        zx_writel(hbsc + HBSC_THRESHOLD_COL1, (0x3ac << 16) | 0x40);
 491        zx_writel(hbsc + HBSC_THRESHOLD_COL2, (0x3c0 << 16) | 0x40);
 492        zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40);
 493}
 494
 495int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
 496                  enum drm_plane_type type)
 497{
 498        const struct drm_plane_helper_funcs *helper;
 499        struct drm_plane *plane = &zplane->plane;
 500        struct device *dev = zplane->dev;
 501        const uint32_t *formats;
 502        unsigned int format_count;
 503        int ret;
 504
 505        zx_plane_hbsc_init(zplane);
 506
 507        switch (type) {
 508        case DRM_PLANE_TYPE_PRIMARY:
 509                helper = &zx_gl_plane_helper_funcs;
 510                formats = gl_formats;
 511                format_count = ARRAY_SIZE(gl_formats);
 512                break;
 513        case DRM_PLANE_TYPE_OVERLAY:
 514                helper = &zx_vl_plane_helper_funcs;
 515                formats = vl_formats;
 516                format_count = ARRAY_SIZE(vl_formats);
 517                break;
 518        default:
 519                return -ENODEV;
 520        }
 521
 522        ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
 523                                       &zx_plane_funcs, formats, format_count,
 524                                       NULL, type, NULL);
 525        if (ret) {
 526                DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret);
 527                return ret;
 528        }
 529
 530        drm_plane_helper_add(plane, helper);
 531
 532        return 0;
 533}
 534