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-2014 Renesas Electronics 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_atomic_helper.h>
  16#include <drm/drm_crtc.h>
  17#include <drm/drm_crtc_helper.h>
  18#include <drm/drm_fb_cma_helper.h>
  19#include <drm/drm_gem_cma_helper.h>
  20#include <drm/drm_plane_helper.h>
  21
  22#include "rcar_du_drv.h"
  23#include "rcar_du_kms.h"
  24#include "rcar_du_plane.h"
  25#include "rcar_du_regs.h"
  26
  27#define RCAR_DU_COLORKEY_NONE           (0 << 24)
  28#define RCAR_DU_COLORKEY_SOURCE         (1 << 24)
  29#define RCAR_DU_COLORKEY_MASK           (1 << 24)
  30
  31static u32 rcar_du_plane_read(struct rcar_du_group *rgrp,
  32                              unsigned int index, u32 reg)
  33{
  34        return rcar_du_read(rgrp->dev,
  35                            rgrp->mmio_offset + index * PLANE_OFF + reg);
  36}
  37
  38static void rcar_du_plane_write(struct rcar_du_group *rgrp,
  39                                unsigned int index, u32 reg, u32 data)
  40{
  41        rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
  42                      data);
  43}
  44
  45static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane)
  46{
  47        struct rcar_du_plane_state *state =
  48                to_rcar_du_plane_state(plane->plane.state);
  49        struct drm_framebuffer *fb = plane->plane.state->fb;
  50        struct rcar_du_group *rgrp = plane->group;
  51        unsigned int src_x = state->state.src_x >> 16;
  52        unsigned int src_y = state->state.src_y >> 16;
  53        unsigned int index = state->hwindex;
  54        struct drm_gem_cma_object *gem;
  55        bool interlaced;
  56        u32 mwr;
  57
  58        interlaced = state->state.crtc->state->adjusted_mode.flags
  59                   & DRM_MODE_FLAG_INTERLACE;
  60
  61        /* Memory pitch (expressed in pixels). Must be doubled for interlaced
  62         * operation with 32bpp formats.
  63         */
  64        if (state->format->planes == 2)
  65                mwr = fb->pitches[0];
  66        else
  67                mwr = fb->pitches[0] * 8 / state->format->bpp;
  68
  69        if (interlaced && state->format->bpp == 32)
  70                mwr *= 2;
  71
  72        rcar_du_plane_write(rgrp, index, PnMWR, mwr);
  73
  74        /* The Y position is expressed in raster line units and must be doubled
  75         * for 32bpp formats, according to the R8A7790 datasheet. No mention of
  76         * doubling the Y position is found in the R8A7779 datasheet, but the
  77         * rule seems to apply there as well.
  78         *
  79         * Despite not being documented, doubling seem not to be needed when
  80         * operating in interlaced mode.
  81         *
  82         * Similarly, for the second plane, NV12 and NV21 formats seem to
  83         * require a halved Y position value, in both progressive and interlaced
  84         * modes.
  85         */
  86        rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
  87        rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
  88                            (!interlaced && state->format->bpp == 32 ? 2 : 1));
  89
  90        gem = drm_fb_cma_get_gem_obj(fb, 0);
  91        rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]);
  92
  93        if (state->format->planes == 2) {
  94                index = (index + 1) % 8;
  95
  96                rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]);
  97
  98                rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
  99                rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
 100                                    (state->format->bpp == 16 ? 2 : 1) / 2);
 101
 102                gem = drm_fb_cma_get_gem_obj(fb, 1);
 103                rcar_du_plane_write(rgrp, index, PnDSA0R,
 104                                    gem->paddr + fb->offsets[1]);
 105        }
 106}
 107
 108static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
 109                                     unsigned int index)
 110{
 111        struct rcar_du_plane_state *state =
 112                to_rcar_du_plane_state(plane->plane.state);
 113        struct rcar_du_group *rgrp = plane->group;
 114        u32 colorkey;
 115        u32 pnmr;
 116
 117        /* The PnALPHAR register controls alpha-blending in 16bpp formats
 118         * (ARGB1555 and XRGB1555).
 119         *
 120         * For ARGB, set the alpha value to 0, and enable alpha-blending when
 121         * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
 122         *
 123         * For XRGB, set the alpha value to the plane-wide alpha value and
 124         * enable alpha-blending regardless of the X bit value.
 125         */
 126        if (state->format->fourcc != DRM_FORMAT_XRGB1555)
 127                rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
 128        else
 129                rcar_du_plane_write(rgrp, index, PnALPHAR,
 130                                    PnALPHAR_ABIT_X | state->alpha);
 131
 132        pnmr = PnMR_BM_MD | state->format->pnmr;
 133
 134        /* Disable color keying when requested. YUV formats have the
 135         * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
 136         * automatically.
 137         */
 138        if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
 139                pnmr |= PnMR_SPIM_TP_OFF;
 140
 141        /* For packed YUV formats we need to select the U/V order. */
 142        if (state->format->fourcc == DRM_FORMAT_YUYV)
 143                pnmr |= PnMR_YCDF_YUYV;
 144
 145        rcar_du_plane_write(rgrp, index, PnMR, pnmr);
 146
 147        switch (state->format->fourcc) {
 148        case DRM_FORMAT_RGB565:
 149                colorkey = ((state->colorkey & 0xf80000) >> 8)
 150                         | ((state->colorkey & 0x00fc00) >> 5)
 151                         | ((state->colorkey & 0x0000f8) >> 3);
 152                rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
 153                break;
 154
 155        case DRM_FORMAT_ARGB1555:
 156        case DRM_FORMAT_XRGB1555:
 157                colorkey = ((state->colorkey & 0xf80000) >> 9)
 158                         | ((state->colorkey & 0x00f800) >> 6)
 159                         | ((state->colorkey & 0x0000f8) >> 3);
 160                rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
 161                break;
 162
 163        case DRM_FORMAT_XRGB8888:
 164        case DRM_FORMAT_ARGB8888:
 165                rcar_du_plane_write(rgrp, index, PnTC3R,
 166                                    PnTC3R_CODE | (state->colorkey & 0xffffff));
 167                break;
 168        }
 169}
 170
 171static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 172                                  unsigned int index)
 173{
 174        struct rcar_du_plane_state *state =
 175                to_rcar_du_plane_state(plane->plane.state);
 176        struct rcar_du_group *rgrp = plane->group;
 177        u32 ddcr2 = PnDDCR2_CODE;
 178        u32 ddcr4;
 179
 180        /* Data format
 181         *
 182         * The data format is selected by the DDDF field in PnMR and the EDF
 183         * field in DDCR4.
 184         */
 185        ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4);
 186        ddcr4 &= ~PnDDCR4_EDF_MASK;
 187        ddcr4 |= state->format->edf | PnDDCR4_CODE;
 188
 189        rcar_du_plane_setup_mode(plane, index);
 190
 191        if (state->format->planes == 2) {
 192                if (state->hwindex != index) {
 193                        if (state->format->fourcc == DRM_FORMAT_NV12 ||
 194                            state->format->fourcc == DRM_FORMAT_NV21)
 195                                ddcr2 |= PnDDCR2_Y420;
 196
 197                        if (state->format->fourcc == DRM_FORMAT_NV21)
 198                                ddcr2 |= PnDDCR2_NV21;
 199
 200                        ddcr2 |= PnDDCR2_DIVU;
 201                } else {
 202                        ddcr2 |= PnDDCR2_DIVY;
 203                }
 204        }
 205
 206        rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
 207        rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
 208
 209        /* Destination position and size */
 210        rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w);
 211        rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h);
 212        rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x);
 213        rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y);
 214
 215        /* Wrap-around and blinking, disabled */
 216        rcar_du_plane_write(rgrp, index, PnWASPR, 0);
 217        rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
 218        rcar_du_plane_write(rgrp, index, PnBTR, 0);
 219        rcar_du_plane_write(rgrp, index, PnMLR, 0);
 220}
 221
 222void rcar_du_plane_setup(struct rcar_du_plane *plane)
 223{
 224        struct rcar_du_plane_state *state =
 225                to_rcar_du_plane_state(plane->plane.state);
 226
 227        __rcar_du_plane_setup(plane, state->hwindex);
 228        if (state->format->planes == 2)
 229                __rcar_du_plane_setup(plane, (state->hwindex + 1) % 8);
 230
 231        rcar_du_plane_setup_fb(plane);
 232}
 233
 234static int rcar_du_plane_atomic_check(struct drm_plane *plane,
 235                                      struct drm_plane_state *state)
 236{
 237        struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state);
 238        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 239        struct rcar_du_device *rcdu = rplane->group->dev;
 240
 241        if (!state->fb || !state->crtc) {
 242                rstate->format = NULL;
 243                return 0;
 244        }
 245
 246        if (state->src_w >> 16 != state->crtc_w ||
 247            state->src_h >> 16 != state->crtc_h) {
 248                dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
 249                return -EINVAL;
 250        }
 251
 252        rstate->format = rcar_du_format_info(state->fb->pixel_format);
 253        if (rstate->format == NULL) {
 254                dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
 255                        state->fb->pixel_format);
 256                return -EINVAL;
 257        }
 258
 259        return 0;
 260}
 261
 262static void rcar_du_plane_atomic_update(struct drm_plane *plane,
 263                                        struct drm_plane_state *old_state)
 264{
 265        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 266
 267        if (plane->state->crtc)
 268                rcar_du_plane_setup(rplane);
 269}
 270
 271static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
 272        .atomic_check = rcar_du_plane_atomic_check,
 273        .atomic_update = rcar_du_plane_atomic_update,
 274};
 275
 276static void rcar_du_plane_reset(struct drm_plane *plane)
 277{
 278        struct rcar_du_plane_state *state;
 279
 280        if (plane->state && plane->state->fb)
 281                drm_framebuffer_unreference(plane->state->fb);
 282
 283        kfree(plane->state);
 284        plane->state = NULL;
 285
 286        state = kzalloc(sizeof(*state), GFP_KERNEL);
 287        if (state == NULL)
 288                return;
 289
 290        state->hwindex = -1;
 291        state->alpha = 255;
 292        state->colorkey = RCAR_DU_COLORKEY_NONE;
 293        state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
 294
 295        plane->state = &state->state;
 296        plane->state->plane = plane;
 297}
 298
 299static struct drm_plane_state *
 300rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
 301{
 302        struct rcar_du_plane_state *state;
 303        struct rcar_du_plane_state *copy;
 304
 305        state = to_rcar_du_plane_state(plane->state);
 306        copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
 307        if (copy == NULL)
 308                return NULL;
 309
 310        if (copy->state.fb)
 311                drm_framebuffer_reference(copy->state.fb);
 312
 313        return &copy->state;
 314}
 315
 316static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
 317                                               struct drm_plane_state *state)
 318{
 319        if (state->fb)
 320                drm_framebuffer_unreference(state->fb);
 321
 322        kfree(to_rcar_du_plane_state(state));
 323}
 324
 325static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
 326                                             struct drm_plane_state *state,
 327                                             struct drm_property *property,
 328                                             uint64_t val)
 329{
 330        struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state);
 331        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 332        struct rcar_du_group *rgrp = rplane->group;
 333
 334        if (property == rgrp->planes.alpha)
 335                rstate->alpha = val;
 336        else if (property == rgrp->planes.colorkey)
 337                rstate->colorkey = val;
 338        else if (property == rgrp->planes.zpos)
 339                rstate->zpos = val;
 340        else
 341                return -EINVAL;
 342
 343        return 0;
 344}
 345
 346static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
 347        const struct drm_plane_state *state, struct drm_property *property,
 348        uint64_t *val)
 349{
 350        const struct rcar_du_plane_state *rstate =
 351                container_of(state, const struct rcar_du_plane_state, state);
 352        struct rcar_du_plane *rplane = to_rcar_plane(plane);
 353        struct rcar_du_group *rgrp = rplane->group;
 354
 355        if (property == rgrp->planes.alpha)
 356                *val = rstate->alpha;
 357        else if (property == rgrp->planes.colorkey)
 358                *val = rstate->colorkey;
 359        else if (property == rgrp->planes.zpos)
 360                *val = rstate->zpos;
 361        else
 362                return -EINVAL;
 363
 364        return 0;
 365}
 366
 367static const struct drm_plane_funcs rcar_du_plane_funcs = {
 368        .update_plane = drm_atomic_helper_update_plane,
 369        .disable_plane = drm_atomic_helper_disable_plane,
 370        .reset = rcar_du_plane_reset,
 371        .set_property = drm_atomic_helper_plane_set_property,
 372        .destroy = drm_plane_cleanup,
 373        .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
 374        .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
 375        .atomic_set_property = rcar_du_plane_atomic_set_property,
 376        .atomic_get_property = rcar_du_plane_atomic_get_property,
 377};
 378
 379static const uint32_t formats[] = {
 380        DRM_FORMAT_RGB565,
 381        DRM_FORMAT_ARGB1555,
 382        DRM_FORMAT_XRGB1555,
 383        DRM_FORMAT_XRGB8888,
 384        DRM_FORMAT_ARGB8888,
 385        DRM_FORMAT_UYVY,
 386        DRM_FORMAT_YUYV,
 387        DRM_FORMAT_NV12,
 388        DRM_FORMAT_NV21,
 389        DRM_FORMAT_NV16,
 390};
 391
 392int rcar_du_planes_init(struct rcar_du_group *rgrp)
 393{
 394        struct rcar_du_planes *planes = &rgrp->planes;
 395        struct rcar_du_device *rcdu = rgrp->dev;
 396        unsigned int num_planes;
 397        unsigned int num_crtcs;
 398        unsigned int crtcs;
 399        unsigned int i;
 400        int ret;
 401
 402        planes->alpha =
 403                drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
 404        if (planes->alpha == NULL)
 405                return -ENOMEM;
 406
 407        /* The color key is expressed as an RGB888 triplet stored in a 32-bit
 408         * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
 409         * or enable source color keying (1).
 410         */
 411        planes->colorkey =
 412                drm_property_create_range(rcdu->ddev, 0, "colorkey",
 413                                          0, 0x01ffffff);
 414        if (planes->colorkey == NULL)
 415                return -ENOMEM;
 416
 417        planes->zpos =
 418                drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
 419        if (planes->zpos == NULL)
 420                return -ENOMEM;
 421
 422         /* Create one primary plane per in this group CRTC and seven overlay
 423          * planes.
 424          */
 425        num_crtcs = min(rcdu->num_crtcs - 2 * rgrp->index, 2U);
 426        num_planes = num_crtcs + 7;
 427
 428        crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
 429
 430        for (i = 0; i < num_planes; ++i) {
 431                enum drm_plane_type type = i < num_crtcs
 432                                         ? DRM_PLANE_TYPE_PRIMARY
 433                                         : DRM_PLANE_TYPE_OVERLAY;
 434                struct rcar_du_plane *plane = &planes->planes[i];
 435
 436                plane->group = rgrp;
 437
 438                ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
 439                                               &rcar_du_plane_funcs, formats,
 440                                               ARRAY_SIZE(formats), type);
 441                if (ret < 0)
 442                        return ret;
 443
 444                drm_plane_helper_add(&plane->plane,
 445                                     &rcar_du_plane_helper_funcs);
 446
 447                if (type == DRM_PLANE_TYPE_PRIMARY)
 448                        continue;
 449
 450                drm_object_attach_property(&plane->plane.base,
 451                                           planes->alpha, 255);
 452                drm_object_attach_property(&plane->plane.base,
 453                                           planes->colorkey,
 454                                           RCAR_DU_COLORKEY_NONE);
 455                drm_object_attach_property(&plane->plane.base,
 456                                           planes->zpos, 1);
 457        }
 458
 459        return 0;
 460}
 461