linux/drivers/gpu/drm/rcar-du/rcar_du_plane.c
<<
>>
Prefs
   1/*
   2 * rcar_du_plane.c  --  R-Car Display Unit Planes
   3 *
   4 * Copyright (C) 2013 Renesas Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <drm/drmP.h>
  15#include <drm/drm_crtc.h>
  16#include <drm/drm_crtc_helper.h>
  17#include <drm/drm_fb_cma_helper.h>
  18#include <drm/drm_gem_cma_helper.h>
  19
  20#include "rcar_du_drv.h"
  21#include "rcar_du_kms.h"
  22#include "rcar_du_plane.h"
  23#include "rcar_du_regs.h"
  24
  25#define RCAR_DU_COLORKEY_NONE           (0 << 24)
  26#define RCAR_DU_COLORKEY_SOURCE         (1 << 24)
  27#define RCAR_DU_COLORKEY_MASK           (1 << 24)
  28
  29struct rcar_du_kms_plane {
  30        struct drm_plane plane;
  31        struct rcar_du_plane *hwplane;
  32};
  33
  34static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
  35{
  36        return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
  37}
  38
  39static u32 rcar_du_plane_read(struct rcar_du_device *rcdu,
  40                              unsigned int index, u32 reg)
  41{
  42        return rcar_du_read(rcdu, index * PLANE_OFF + reg);
  43}
  44
  45static void rcar_du_plane_write(struct rcar_du_device *rcdu,
  46                                unsigned int index, u32 reg, u32 data)
  47{
  48        rcar_du_write(rcdu, index * PLANE_OFF + reg, data);
  49}
  50
  51int rcar_du_plane_reserve(struct rcar_du_plane *plane,
  52                          const struct rcar_du_format_info *format)
  53{
  54        struct rcar_du_device *rcdu = plane->dev;
  55        unsigned int i;
  56        int ret = -EBUSY;
  57
  58        mutex_lock(&rcdu->planes.lock);
  59
  60        for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
  61                if (!(rcdu->planes.free & (1 << i)))
  62                        continue;
  63
  64                if (format->planes == 1 ||
  65                    rcdu->planes.free & (1 << ((i + 1) % 8)))
  66                        break;
  67        }
  68
  69        if (i == ARRAY_SIZE(rcdu->planes.planes))
  70                goto done;
  71
  72        rcdu->planes.free &= ~(1 << i);
  73        if (format->planes == 2)
  74                rcdu->planes.free &= ~(1 << ((i + 1) % 8));
  75
  76        plane->hwindex = i;
  77
  78        ret = 0;
  79
  80done:
  81        mutex_unlock(&rcdu->planes.lock);
  82        return ret;
  83}
  84
  85void rcar_du_plane_release(struct rcar_du_plane *plane)
  86{
  87        struct rcar_du_device *rcdu = plane->dev;
  88
  89        if (plane->hwindex == -1)
  90                return;
  91
  92        mutex_lock(&rcdu->planes.lock);
  93        rcdu->planes.free |= 1 << plane->hwindex;
  94        if (plane->format->planes == 2)
  95                rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8);
  96        mutex_unlock(&rcdu->planes.lock);
  97
  98        plane->hwindex = -1;
  99}
 100
 101void rcar_du_plane_update_base(struct rcar_du_plane *plane)
 102{
 103        struct rcar_du_device *rcdu = plane->dev;
 104        unsigned int index = plane->hwindex;
 105
 106        /* According to the datasheet the Y position is expressed in raster line
 107         * units. However, 32bpp formats seem to require a doubled Y position
 108         * value. Similarly, for the second plane, NV12 and NV21 formats seem to
 109         * require a halved Y position value.
 110         */
 111        rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
 112        rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
 113                            (plane->format->bpp == 32 ? 2 : 1));
 114        rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]);
 115
 116        if (plane->format->planes == 2) {
 117                index = (index + 1) % 8;
 118
 119                rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
 120                rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
 121                                    (plane->format->bpp == 16 ? 2 : 1) / 2);
 122                rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]);
 123        }
 124}
 125
 126void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
 127                                struct drm_framebuffer *fb)
 128{
 129        struct drm_gem_cma_object *gem;
 130
 131        gem = drm_fb_cma_get_gem_obj(fb, 0);
 132        plane->dma[0] = gem->paddr + fb->offsets[0];
 133
 134        if (plane->format->planes == 2) {
 135                gem = drm_fb_cma_get_gem_obj(fb, 1);
 136                plane->dma[1] = gem->paddr + fb->offsets[1];
 137        }
 138}
 139
 140static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
 141                                     unsigned int index)
 142{
 143        struct rcar_du_device *rcdu = plane->dev;
 144        u32 colorkey;
 145        u32 pnmr;
 146
 147        /* The PnALPHAR register controls alpha-blending in 16bpp formats
 148         * (ARGB1555 and XRGB1555).
 149         *
 150         * For ARGB, set the alpha value to 0, and enable alpha-blending when
 151         * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
 152         *
 153         * For XRGB, set the alpha value to the plane-wide alpha value and
 154         * enable alpha-blending regardless of the X bit value.
 155         */
 156        if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
 157                rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0);
 158        else
 159                rcar_du_plane_write(rcdu, index, PnALPHAR,
 160                                    PnALPHAR_ABIT_X | plane->alpha);
 161
 162        pnmr = PnMR_BM_MD | plane->format->pnmr;
 163
 164        /* Disable color keying when requested. YUV formats have the
 165         * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
 166         * automatically.
 167         */
 168        if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
 169                pnmr |= PnMR_SPIM_TP_OFF;
 170
 171        /* For packed YUV formats we need to select the U/V order. */
 172        if (plane->format->fourcc == DRM_FORMAT_YUYV)
 173                pnmr |= PnMR_YCDF_YUYV;
 174
 175        rcar_du_plane_write(rcdu, index, PnMR, pnmr);
 176
 177        switch (plane->format->fourcc) {
 178        case DRM_FORMAT_RGB565:
 179                colorkey = ((plane->colorkey & 0xf80000) >> 8)
 180                         | ((plane->colorkey & 0x00fc00) >> 5)
 181                         | ((plane->colorkey & 0x0000f8) >> 3);
 182                rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
 183                break;
 184
 185        case DRM_FORMAT_ARGB1555:
 186        case DRM_FORMAT_XRGB1555:
 187                colorkey = ((plane->colorkey & 0xf80000) >> 9)
 188                         | ((plane->colorkey & 0x00f800) >> 6)
 189                         | ((plane->colorkey & 0x0000f8) >> 3);
 190                rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
 191                break;
 192
 193        case DRM_FORMAT_XRGB8888:
 194        case DRM_FORMAT_ARGB8888:
 195                rcar_du_plane_write(rcdu, index, PnTC3R,
 196                                    PnTC3R_CODE | (plane->colorkey & 0xffffff));
 197                break;
 198        }
 199}
 200
 201static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 202                                  unsigned int index)
 203{
 204        struct rcar_du_device *rcdu = plane->dev;
 205        u32 ddcr2 = PnDDCR2_CODE;
 206        u32 ddcr4;
 207        u32 mwr;
 208
 209        /* Data format
 210         *
 211         * The data format is selected by the DDDF field in PnMR and the EDF
 212         * field in DDCR4.
 213         */
 214        ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4);
 215        ddcr4 &= ~PnDDCR4_EDF_MASK;
 216        ddcr4 |= plane->format->edf | PnDDCR4_CODE;
 217
 218        rcar_du_plane_setup_mode(plane, index);
 219
 220        if (plane->format->planes == 2) {
 221                if (plane->hwindex != index) {
 222                        if (plane->format->fourcc == DRM_FORMAT_NV12 ||
 223                            plane->format->fourcc == DRM_FORMAT_NV21)
 224                                ddcr2 |= PnDDCR2_Y420;
 225
 226                        if (plane->format->fourcc == DRM_FORMAT_NV21)
 227                                ddcr2 |= PnDDCR2_NV21;
 228
 229                        ddcr2 |= PnDDCR2_DIVU;
 230                } else {
 231                        ddcr2 |= PnDDCR2_DIVY;
 232                }
 233        }
 234
 235        rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2);
 236        rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4);
 237
 238        /* Memory pitch (expressed in pixels) */
 239        if (plane->format->planes == 2)
 240                mwr = plane->pitch;
 241        else
 242                mwr = plane->pitch * 8 / plane->format->bpp;
 243
 244        rcar_du_plane_write(rcdu, index, PnMWR, mwr);
 245
 246        /* Destination position and size */
 247        rcar_du_plane_write(rcdu, index, PnDSXR, plane->width);
 248        rcar_du_plane_write(rcdu, index, PnDSYR, plane->height);
 249        rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x);
 250        rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y);
 251
 252        /* Wrap-around and blinking, disabled */
 253        rcar_du_plane_write(rcdu, index, PnWASPR, 0);
 254        rcar_du_plane_write(rcdu, index, PnWAMWR, 4095);
 255        rcar_du_plane_write(rcdu, index, PnBTR, 0);
 256        rcar_du_plane_write(rcdu, index, PnMLR, 0);
 257}
 258
 259void rcar_du_plane_setup(struct rcar_du_plane *plane)
 260{
 261        __rcar_du_plane_setup(plane, plane->hwindex);
 262        if (plane->format->planes == 2)
 263                __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
 264
 265        rcar_du_plane_update_base(plane);
 266}
 267
 268static int
 269rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 270                       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 271                       unsigned int crtc_w, unsigned int crtc_h,
 272                       uint32_t src_x, uint32_t src_y,
 273                       uint32_t src_w, uint32_t src_h)
 274{
 275        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 276        struct rcar_du_device *rcdu = plane->dev->dev_private;
 277        const struct rcar_du_format_info *format;
 278        unsigned int nplanes;
 279        int ret;
 280
 281        format = rcar_du_format_info(fb->pixel_format);
 282        if (format == NULL) {
 283                dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
 284                        fb->pixel_format);
 285                return -EINVAL;
 286        }
 287
 288        if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
 289                dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
 290                return -EINVAL;
 291        }
 292
 293        nplanes = rplane->format ? rplane->format->planes : 0;
 294
 295        /* Reallocate hardware planes if the number of required planes has
 296         * changed.
 297         */
 298        if (format->planes != nplanes) {
 299                rcar_du_plane_release(rplane);
 300                ret = rcar_du_plane_reserve(rplane, format);
 301                if (ret < 0)
 302                        return ret;
 303        }
 304
 305        rplane->crtc = crtc;
 306        rplane->format = format;
 307        rplane->pitch = fb->pitches[0];
 308
 309        rplane->src_x = src_x >> 16;
 310        rplane->src_y = src_y >> 16;
 311        rplane->dst_x = crtc_x;
 312        rplane->dst_y = crtc_y;
 313        rplane->width = crtc_w;
 314        rplane->height = crtc_h;
 315
 316        rcar_du_plane_compute_base(rplane, fb);
 317        rcar_du_plane_setup(rplane);
 318
 319        mutex_lock(&rcdu->planes.lock);
 320        rplane->enabled = true;
 321        rcar_du_crtc_update_planes(rplane->crtc);
 322        mutex_unlock(&rcdu->planes.lock);
 323
 324        return 0;
 325}
 326
 327static int rcar_du_plane_disable(struct drm_plane *plane)
 328{
 329        struct rcar_du_device *rcdu = plane->dev->dev_private;
 330        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 331
 332        if (!rplane->enabled)
 333                return 0;
 334
 335        mutex_lock(&rcdu->planes.lock);
 336        rplane->enabled = false;
 337        rcar_du_crtc_update_planes(rplane->crtc);
 338        mutex_unlock(&rcdu->planes.lock);
 339
 340        rcar_du_plane_release(rplane);
 341
 342        rplane->crtc = NULL;
 343        rplane->format = NULL;
 344
 345        return 0;
 346}
 347
 348/* Both the .set_property and the .update_plane operations are called with the
 349 * mode_config lock held. There is this no need to explicitly protect access to
 350 * the alpha and colorkey fields and the mode register.
 351 */
 352static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
 353{
 354        if (plane->alpha == alpha)
 355                return;
 356
 357        plane->alpha = alpha;
 358        if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
 359                return;
 360
 361        rcar_du_plane_setup_mode(plane, plane->hwindex);
 362}
 363
 364static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
 365                                       u32 colorkey)
 366{
 367        if (plane->colorkey == colorkey)
 368                return;
 369
 370        plane->colorkey = colorkey;
 371        if (!plane->enabled)
 372                return;
 373
 374        rcar_du_plane_setup_mode(plane, plane->hwindex);
 375}
 376
 377static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
 378                                   unsigned int zpos)
 379{
 380        struct rcar_du_device *rcdu = plane->dev;
 381
 382        mutex_lock(&rcdu->planes.lock);
 383        if (plane->zpos == zpos)
 384                goto done;
 385
 386        plane->zpos = zpos;
 387        if (!plane->enabled)
 388                goto done;
 389
 390        rcar_du_crtc_update_planes(plane->crtc);
 391
 392done:
 393        mutex_unlock(&rcdu->planes.lock);
 394}
 395
 396static int rcar_du_plane_set_property(struct drm_plane *plane,
 397                                      struct drm_property *property,
 398                                      uint64_t value)
 399{
 400        struct rcar_du_device *rcdu = plane->dev->dev_private;
 401        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 402
 403        if (property == rcdu->planes.alpha)
 404                rcar_du_plane_set_alpha(rplane, value);
 405        else if (property == rcdu->planes.colorkey)
 406                rcar_du_plane_set_colorkey(rplane, value);
 407        else if (property == rcdu->planes.zpos)
 408                rcar_du_plane_set_zpos(rplane, value);
 409        else
 410                return -EINVAL;
 411
 412        return 0;
 413}
 414
 415static const struct drm_plane_funcs rcar_du_plane_funcs = {
 416        .update_plane = rcar_du_plane_update,
 417        .disable_plane = rcar_du_plane_disable,
 418        .set_property = rcar_du_plane_set_property,
 419        .destroy = drm_plane_cleanup,
 420};
 421
 422static const uint32_t formats[] = {
 423        DRM_FORMAT_RGB565,
 424        DRM_FORMAT_ARGB1555,
 425        DRM_FORMAT_XRGB1555,
 426        DRM_FORMAT_XRGB8888,
 427        DRM_FORMAT_ARGB8888,
 428        DRM_FORMAT_UYVY,
 429        DRM_FORMAT_YUYV,
 430        DRM_FORMAT_NV12,
 431        DRM_FORMAT_NV21,
 432        DRM_FORMAT_NV16,
 433};
 434
 435int rcar_du_plane_init(struct rcar_du_device *rcdu)
 436{
 437        unsigned int i;
 438
 439        mutex_init(&rcdu->planes.lock);
 440        rcdu->planes.free = 0xff;
 441
 442        rcdu->planes.alpha =
 443                drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
 444        if (rcdu->planes.alpha == NULL)
 445                return -ENOMEM;
 446
 447        /* The color key is expressed as an RGB888 triplet stored in a 32-bit
 448         * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
 449         * or enable source color keying (1).
 450         */
 451        rcdu->planes.colorkey =
 452                drm_property_create_range(rcdu->ddev, 0, "colorkey",
 453                                          0, 0x01ffffff);
 454        if (rcdu->planes.colorkey == NULL)
 455                return -ENOMEM;
 456
 457        rcdu->planes.zpos =
 458                drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
 459        if (rcdu->planes.zpos == NULL)
 460                return -ENOMEM;
 461
 462        for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
 463                struct rcar_du_plane *plane = &rcdu->planes.planes[i];
 464
 465                plane->dev = rcdu;
 466                plane->hwindex = -1;
 467                plane->alpha = 255;
 468                plane->colorkey = RCAR_DU_COLORKEY_NONE;
 469                plane->zpos = 0;
 470        }
 471
 472        return 0;
 473}
 474
 475int rcar_du_plane_register(struct rcar_du_device *rcdu)
 476{
 477        unsigned int i;
 478        int ret;
 479
 480        for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
 481                struct rcar_du_kms_plane *plane;
 482
 483                plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
 484                if (plane == NULL)
 485                        return -ENOMEM;
 486
 487                plane->hwplane = &rcdu->planes.planes[i + 2];
 488                plane->hwplane->zpos = 1;
 489
 490                ret = drm_plane_init(rcdu->ddev, &plane->plane,
 491                                     (1 << rcdu->num_crtcs) - 1,
 492                                     &rcar_du_plane_funcs, formats,
 493                                     ARRAY_SIZE(formats), false);
 494                if (ret < 0)
 495                        return ret;
 496
 497                drm_object_attach_property(&plane->plane.base,
 498                                           rcdu->planes.alpha, 255);
 499                drm_object_attach_property(&plane->plane.base,
 500                                           rcdu->planes.colorkey,
 501                                           RCAR_DU_COLORKEY_NONE);
 502                drm_object_attach_property(&plane->plane.base,
 503                                           rcdu->planes.zpos, 1);
 504        }
 505
 506        return 0;
 507}
 508