linux/drivers/gpu/drm/vc4/vc4_plane.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Broadcom
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9/**
  10 * DOC: VC4 plane module
  11 *
  12 * Each DRM plane is a layer of pixels being scanned out by the HVS.
  13 *
  14 * At atomic modeset check time, we compute the HVS display element
  15 * state that would be necessary for displaying the plane (giving us a
  16 * chance to figure out if a plane configuration is invalid), then at
  17 * atomic flush time the CRTC will ask us to write our element state
  18 * into the region of the HVS that it has allocated for us.
  19 */
  20
  21#include "vc4_drv.h"
  22#include "vc4_regs.h"
  23#include "drm_atomic_helper.h"
  24#include "drm_fb_cma_helper.h"
  25#include "drm_plane_helper.h"
  26
  27enum vc4_scaling_mode {
  28        VC4_SCALING_NONE,
  29        VC4_SCALING_TPZ,
  30        VC4_SCALING_PPF,
  31};
  32
  33struct vc4_plane_state {
  34        struct drm_plane_state base;
  35        /* System memory copy of the display list for this element, computed
  36         * at atomic_check time.
  37         */
  38        u32 *dlist;
  39        u32 dlist_size; /* Number of dwords allocated for the display list */
  40        u32 dlist_count; /* Number of used dwords in the display list. */
  41
  42        /* Offset in the dlist to various words, for pageflip or
  43         * cursor updates.
  44         */
  45        u32 pos0_offset;
  46        u32 pos2_offset;
  47        u32 ptr0_offset;
  48
  49        /* Offset where the plane's dlist was last stored in the
  50         * hardware at vc4_crtc_atomic_flush() time.
  51         */
  52        u32 __iomem *hw_dlist;
  53
  54        /* Clipped coordinates of the plane on the display. */
  55        int crtc_x, crtc_y, crtc_w, crtc_h;
  56        /* Clipped area being scanned from in the FB. */
  57        u32 src_x, src_y;
  58
  59        u32 src_w[2], src_h[2];
  60
  61        /* Scaling selection for the RGB/Y plane and the Cb/Cr planes. */
  62        enum vc4_scaling_mode x_scaling[2], y_scaling[2];
  63        bool is_unity;
  64        bool is_yuv;
  65
  66        /* Offset to start scanning out from the start of the plane's
  67         * BO.
  68         */
  69        u32 offsets[3];
  70
  71        /* Our allocation in LBM for temporary storage during scaling. */
  72        struct drm_mm_node lbm;
  73};
  74
  75static inline struct vc4_plane_state *
  76to_vc4_plane_state(struct drm_plane_state *state)
  77{
  78        return (struct vc4_plane_state *)state;
  79}
  80
  81static const struct hvs_format {
  82        u32 drm; /* DRM_FORMAT_* */
  83        u32 hvs; /* HVS_FORMAT_* */
  84        u32 pixel_order;
  85        bool has_alpha;
  86        bool flip_cbcr;
  87} hvs_formats[] = {
  88        {
  89                .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
  90                .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
  91        },
  92        {
  93                .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
  94                .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
  95        },
  96        {
  97                .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
  98                .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
  99        },
 100        {
 101                .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
 102                .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
 103        },
 104        {
 105                .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
 106                .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
 107        },
 108        {
 109                .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
 110                .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
 111        },
 112        {
 113                .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
 114                .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
 115        },
 116        {
 117                .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
 118                .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
 119        },
 120        {
 121                .drm = DRM_FORMAT_YUV422,
 122                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
 123        },
 124        {
 125                .drm = DRM_FORMAT_YVU422,
 126                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
 127                .flip_cbcr = true,
 128        },
 129        {
 130                .drm = DRM_FORMAT_YUV420,
 131                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
 132        },
 133        {
 134                .drm = DRM_FORMAT_YVU420,
 135                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
 136                .flip_cbcr = true,
 137        },
 138        {
 139                .drm = DRM_FORMAT_NV12,
 140                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
 141        },
 142        {
 143                .drm = DRM_FORMAT_NV16,
 144                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
 145        },
 146};
 147
 148static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
 149{
 150        unsigned i;
 151
 152        for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
 153                if (hvs_formats[i].drm == drm_format)
 154                        return &hvs_formats[i];
 155        }
 156
 157        return NULL;
 158}
 159
 160static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst)
 161{
 162        if (dst > src)
 163                return VC4_SCALING_PPF;
 164        else if (dst < src)
 165                return VC4_SCALING_TPZ;
 166        else
 167                return VC4_SCALING_NONE;
 168}
 169
 170static bool plane_enabled(struct drm_plane_state *state)
 171{
 172        return state->fb && state->crtc;
 173}
 174
 175static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
 176{
 177        struct vc4_plane_state *vc4_state;
 178
 179        if (WARN_ON(!plane->state))
 180                return NULL;
 181
 182        vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
 183        if (!vc4_state)
 184                return NULL;
 185
 186        memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
 187
 188        __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
 189
 190        if (vc4_state->dlist) {
 191                vc4_state->dlist = kmemdup(vc4_state->dlist,
 192                                           vc4_state->dlist_count * 4,
 193                                           GFP_KERNEL);
 194                if (!vc4_state->dlist) {
 195                        kfree(vc4_state);
 196                        return NULL;
 197                }
 198                vc4_state->dlist_size = vc4_state->dlist_count;
 199        }
 200
 201        return &vc4_state->base;
 202}
 203
 204static void vc4_plane_destroy_state(struct drm_plane *plane,
 205                                    struct drm_plane_state *state)
 206{
 207        struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
 208        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 209
 210        if (vc4_state->lbm.allocated) {
 211                unsigned long irqflags;
 212
 213                spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
 214                drm_mm_remove_node(&vc4_state->lbm);
 215                spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
 216        }
 217
 218        kfree(vc4_state->dlist);
 219        __drm_atomic_helper_plane_destroy_state(&vc4_state->base);
 220        kfree(state);
 221}
 222
 223/* Called during init to allocate the plane's atomic state. */
 224static void vc4_plane_reset(struct drm_plane *plane)
 225{
 226        struct vc4_plane_state *vc4_state;
 227
 228        WARN_ON(plane->state);
 229
 230        vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
 231        if (!vc4_state)
 232                return;
 233
 234        plane->state = &vc4_state->base;
 235        vc4_state->base.plane = plane;
 236}
 237
 238static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
 239{
 240        if (vc4_state->dlist_count == vc4_state->dlist_size) {
 241                u32 new_size = max(4u, vc4_state->dlist_count * 2);
 242                u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
 243
 244                if (!new_dlist)
 245                        return;
 246                memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
 247
 248                kfree(vc4_state->dlist);
 249                vc4_state->dlist = new_dlist;
 250                vc4_state->dlist_size = new_size;
 251        }
 252
 253        vc4_state->dlist[vc4_state->dlist_count++] = val;
 254}
 255
 256/* Returns the scl0/scl1 field based on whether the dimensions need to
 257 * be up/down/non-scaled.
 258 *
 259 * This is a replication of a table from the spec.
 260 */
 261static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
 262{
 263        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 264
 265        switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) {
 266        case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF:
 267                return SCALER_CTL0_SCL_H_PPF_V_PPF;
 268        case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF:
 269                return SCALER_CTL0_SCL_H_TPZ_V_PPF;
 270        case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ:
 271                return SCALER_CTL0_SCL_H_PPF_V_TPZ;
 272        case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ:
 273                return SCALER_CTL0_SCL_H_TPZ_V_TPZ;
 274        case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE:
 275                return SCALER_CTL0_SCL_H_PPF_V_NONE;
 276        case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF:
 277                return SCALER_CTL0_SCL_H_NONE_V_PPF;
 278        case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ:
 279                return SCALER_CTL0_SCL_H_NONE_V_TPZ;
 280        case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE:
 281                return SCALER_CTL0_SCL_H_TPZ_V_NONE;
 282        default:
 283        case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE:
 284                /* The unity case is independently handled by
 285                 * SCALER_CTL0_UNITY.
 286                 */
 287                return 0;
 288        }
 289}
 290
 291static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
 292{
 293        struct drm_plane *plane = state->plane;
 294        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 295        struct drm_framebuffer *fb = state->fb;
 296        struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
 297        u32 subpixel_src_mask = (1 << 16) - 1;
 298        u32 format = fb->pixel_format;
 299        int num_planes = drm_format_num_planes(format);
 300        u32 h_subsample = 1;
 301        u32 v_subsample = 1;
 302        int i;
 303
 304        for (i = 0; i < num_planes; i++)
 305                vc4_state->offsets[i] = bo->paddr + fb->offsets[i];
 306
 307        /* We don't support subpixel source positioning for scaling. */
 308        if ((state->src_x & subpixel_src_mask) ||
 309            (state->src_y & subpixel_src_mask) ||
 310            (state->src_w & subpixel_src_mask) ||
 311            (state->src_h & subpixel_src_mask)) {
 312                return -EINVAL;
 313        }
 314
 315        vc4_state->src_x = state->src_x >> 16;
 316        vc4_state->src_y = state->src_y >> 16;
 317        vc4_state->src_w[0] = state->src_w >> 16;
 318        vc4_state->src_h[0] = state->src_h >> 16;
 319
 320        vc4_state->crtc_x = state->crtc_x;
 321        vc4_state->crtc_y = state->crtc_y;
 322        vc4_state->crtc_w = state->crtc_w;
 323        vc4_state->crtc_h = state->crtc_h;
 324
 325        vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
 326                                                       vc4_state->crtc_w);
 327        vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
 328                                                       vc4_state->crtc_h);
 329
 330        if (num_planes > 1) {
 331                vc4_state->is_yuv = true;
 332
 333                h_subsample = drm_format_horz_chroma_subsampling(format);
 334                v_subsample = drm_format_vert_chroma_subsampling(format);
 335                vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample;
 336                vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample;
 337
 338                vc4_state->x_scaling[1] =
 339                        vc4_get_scaling_mode(vc4_state->src_w[1],
 340                                             vc4_state->crtc_w);
 341                vc4_state->y_scaling[1] =
 342                        vc4_get_scaling_mode(vc4_state->src_h[1],
 343                                             vc4_state->crtc_h);
 344
 345                /* YUV conversion requires that scaling be enabled,
 346                 * even on a plane that's otherwise 1:1.  Choose TPZ
 347                 * for simplicity.
 348                 */
 349                if (vc4_state->x_scaling[0] == VC4_SCALING_NONE)
 350                        vc4_state->x_scaling[0] = VC4_SCALING_TPZ;
 351                if (vc4_state->y_scaling[0] == VC4_SCALING_NONE)
 352                        vc4_state->y_scaling[0] = VC4_SCALING_TPZ;
 353        }
 354
 355        vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE &&
 356                               vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
 357                               vc4_state->x_scaling[1] == VC4_SCALING_NONE &&
 358                               vc4_state->y_scaling[1] == VC4_SCALING_NONE);
 359
 360        /* No configuring scaling on the cursor plane, since it gets
 361           non-vblank-synced updates, and scaling requires requires
 362           LBM changes which have to be vblank-synced.
 363         */
 364        if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity)
 365                return -EINVAL;
 366
 367        /* Clamp the on-screen start x/y to 0.  The hardware doesn't
 368         * support negative y, and negative x wastes bandwidth.
 369         */
 370        if (vc4_state->crtc_x < 0) {
 371                for (i = 0; i < num_planes; i++) {
 372                        u32 cpp = drm_format_plane_cpp(fb->pixel_format, i);
 373                        u32 subs = ((i == 0) ? 1 : h_subsample);
 374
 375                        vc4_state->offsets[i] += (cpp *
 376                                                  (-vc4_state->crtc_x) / subs);
 377                }
 378                vc4_state->src_w[0] += vc4_state->crtc_x;
 379                vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample;
 380                vc4_state->crtc_x = 0;
 381        }
 382
 383        if (vc4_state->crtc_y < 0) {
 384                for (i = 0; i < num_planes; i++) {
 385                        u32 subs = ((i == 0) ? 1 : v_subsample);
 386
 387                        vc4_state->offsets[i] += (fb->pitches[i] *
 388                                                  (-vc4_state->crtc_y) / subs);
 389                }
 390                vc4_state->src_h[0] += vc4_state->crtc_y;
 391                vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample;
 392                vc4_state->crtc_y = 0;
 393        }
 394
 395        return 0;
 396}
 397
 398static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
 399{
 400        u32 scale, recip;
 401
 402        scale = (1 << 16) * src / dst;
 403
 404        /* The specs note that while the reciprocal would be defined
 405         * as (1<<32)/scale, ~0 is close enough.
 406         */
 407        recip = ~0 / scale;
 408
 409        vc4_dlist_write(vc4_state,
 410                        VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
 411                        VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
 412        vc4_dlist_write(vc4_state,
 413                        VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP));
 414}
 415
 416static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
 417{
 418        u32 scale = (1 << 16) * src / dst;
 419
 420        vc4_dlist_write(vc4_state,
 421                        SCALER_PPF_AGC |
 422                        VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
 423                        VC4_SET_FIELD(0, SCALER_PPF_IPHASE));
 424}
 425
 426static u32 vc4_lbm_size(struct drm_plane_state *state)
 427{
 428        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 429        /* This is the worst case number.  One of the two sizes will
 430         * be used depending on the scaling configuration.
 431         */
 432        u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w);
 433        u32 lbm;
 434
 435        if (!vc4_state->is_yuv) {
 436                if (vc4_state->is_unity)
 437                        return 0;
 438                else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
 439                        lbm = pix_per_line * 8;
 440                else {
 441                        /* In special cases, this multiplier might be 12. */
 442                        lbm = pix_per_line * 16;
 443                }
 444        } else {
 445                /* There are cases for this going down to a multiplier
 446                 * of 2, but according to the firmware source, the
 447                 * table in the docs is somewhat wrong.
 448                 */
 449                lbm = pix_per_line * 16;
 450        }
 451
 452        lbm = roundup(lbm, 32);
 453
 454        return lbm;
 455}
 456
 457static void vc4_write_scaling_parameters(struct drm_plane_state *state,
 458                                         int channel)
 459{
 460        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 461
 462        /* Ch0 H-PPF Word 0: Scaling Parameters */
 463        if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
 464                vc4_write_ppf(vc4_state,
 465                              vc4_state->src_w[channel], vc4_state->crtc_w);
 466        }
 467
 468        /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
 469        if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) {
 470                vc4_write_ppf(vc4_state,
 471                              vc4_state->src_h[channel], vc4_state->crtc_h);
 472                vc4_dlist_write(vc4_state, 0xc0c0c0c0);
 473        }
 474
 475        /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
 476        if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) {
 477                vc4_write_tpz(vc4_state,
 478                              vc4_state->src_w[channel], vc4_state->crtc_w);
 479        }
 480
 481        /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
 482        if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) {
 483                vc4_write_tpz(vc4_state,
 484                              vc4_state->src_h[channel], vc4_state->crtc_h);
 485                vc4_dlist_write(vc4_state, 0xc0c0c0c0);
 486        }
 487}
 488
 489/* Writes out a full display list for an active plane to the plane's
 490 * private dlist state.
 491 */
 492static int vc4_plane_mode_set(struct drm_plane *plane,
 493                              struct drm_plane_state *state)
 494{
 495        struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
 496        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 497        struct drm_framebuffer *fb = state->fb;
 498        u32 ctl0_offset = vc4_state->dlist_count;
 499        const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
 500        int num_planes = drm_format_num_planes(format->drm);
 501        u32 scl0, scl1;
 502        u32 lbm_size;
 503        unsigned long irqflags;
 504        int ret, i;
 505
 506        ret = vc4_plane_setup_clipping_and_scaling(state);
 507        if (ret)
 508                return ret;
 509
 510        /* Allocate the LBM memory that the HVS will use for temporary
 511         * storage due to our scaling/format conversion.
 512         */
 513        lbm_size = vc4_lbm_size(state);
 514        if (lbm_size) {
 515                if (!vc4_state->lbm.allocated) {
 516                        spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
 517                        ret = drm_mm_insert_node(&vc4->hvs->lbm_mm,
 518                                                 &vc4_state->lbm,
 519                                                 lbm_size, 32, 0);
 520                        spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
 521                } else {
 522                        WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
 523                }
 524        }
 525
 526        if (ret)
 527                return ret;
 528
 529        /* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
 530         * and 4:4:4, scl1 should be set to scl0 so both channels of
 531         * the scaler do the same thing.  For YUV, the Y plane needs
 532         * to be put in channel 1 and Cb/Cr in channel 0, so we swap
 533         * the scl fields here.
 534         */
 535        if (num_planes == 1) {
 536                scl0 = vc4_get_scl_field(state, 1);
 537                scl1 = scl0;
 538        } else {
 539                scl0 = vc4_get_scl_field(state, 1);
 540                scl1 = vc4_get_scl_field(state, 0);
 541        }
 542
 543        /* Control word */
 544        vc4_dlist_write(vc4_state,
 545                        SCALER_CTL0_VALID |
 546                        (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
 547                        (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
 548                        (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
 549                        VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
 550                        VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
 551
 552        /* Position Word 0: Image Positions and Alpha Value */
 553        vc4_state->pos0_offset = vc4_state->dlist_count;
 554        vc4_dlist_write(vc4_state,
 555                        VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
 556                        VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
 557                        VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
 558
 559        /* Position Word 1: Scaled Image Dimensions. */
 560        if (!vc4_state->is_unity) {
 561                vc4_dlist_write(vc4_state,
 562                                VC4_SET_FIELD(vc4_state->crtc_w,
 563                                              SCALER_POS1_SCL_WIDTH) |
 564                                VC4_SET_FIELD(vc4_state->crtc_h,
 565                                              SCALER_POS1_SCL_HEIGHT));
 566        }
 567
 568        /* Position Word 2: Source Image Size, Alpha Mode */
 569        vc4_state->pos2_offset = vc4_state->dlist_count;
 570        vc4_dlist_write(vc4_state,
 571                        VC4_SET_FIELD(format->has_alpha ?
 572                                      SCALER_POS2_ALPHA_MODE_PIPELINE :
 573                                      SCALER_POS2_ALPHA_MODE_FIXED,
 574                                      SCALER_POS2_ALPHA_MODE) |
 575                        VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) |
 576                        VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT));
 577
 578        /* Position Word 3: Context.  Written by the HVS. */
 579        vc4_dlist_write(vc4_state, 0xc0c0c0c0);
 580
 581
 582        /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
 583         *
 584         * The pointers may be any byte address.
 585         */
 586        vc4_state->ptr0_offset = vc4_state->dlist_count;
 587        if (!format->flip_cbcr) {
 588                for (i = 0; i < num_planes; i++)
 589                        vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
 590        } else {
 591                WARN_ON_ONCE(num_planes != 3);
 592                vc4_dlist_write(vc4_state, vc4_state->offsets[0]);
 593                vc4_dlist_write(vc4_state, vc4_state->offsets[2]);
 594                vc4_dlist_write(vc4_state, vc4_state->offsets[1]);
 595        }
 596
 597        /* Pointer Context Word 0/1/2: Written by the HVS */
 598        for (i = 0; i < num_planes; i++)
 599                vc4_dlist_write(vc4_state, 0xc0c0c0c0);
 600
 601        /* Pitch word 0/1/2 */
 602        for (i = 0; i < num_planes; i++) {
 603                vc4_dlist_write(vc4_state,
 604                                VC4_SET_FIELD(fb->pitches[i], SCALER_SRC_PITCH));
 605        }
 606
 607        /* Colorspace conversion words */
 608        if (vc4_state->is_yuv) {
 609                vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5);
 610                vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5);
 611                vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5);
 612        }
 613
 614        if (!vc4_state->is_unity) {
 615                /* LBM Base Address. */
 616                if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
 617                    vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
 618                        vc4_dlist_write(vc4_state, vc4_state->lbm.start);
 619                }
 620
 621                if (num_planes > 1) {
 622                        /* Emit Cb/Cr as channel 0 and Y as channel
 623                         * 1. This matches how we set up scl0/scl1
 624                         * above.
 625                         */
 626                        vc4_write_scaling_parameters(state, 1);
 627                }
 628                vc4_write_scaling_parameters(state, 0);
 629
 630                /* If any PPF setup was done, then all the kernel
 631                 * pointers get uploaded.
 632                 */
 633                if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
 634                    vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
 635                    vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
 636                    vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
 637                        u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
 638                                                   SCALER_PPF_KERNEL_OFFSET);
 639
 640                        /* HPPF plane 0 */
 641                        vc4_dlist_write(vc4_state, kernel);
 642                        /* VPPF plane 0 */
 643                        vc4_dlist_write(vc4_state, kernel);
 644                        /* HPPF plane 1 */
 645                        vc4_dlist_write(vc4_state, kernel);
 646                        /* VPPF plane 1 */
 647                        vc4_dlist_write(vc4_state, kernel);
 648                }
 649        }
 650
 651        vc4_state->dlist[ctl0_offset] |=
 652                VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
 653
 654        return 0;
 655}
 656
 657/* If a modeset involves changing the setup of a plane, the atomic
 658 * infrastructure will call this to validate a proposed plane setup.
 659 * However, if a plane isn't getting updated, this (and the
 660 * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
 661 * compute the dlist here and have all active plane dlists get updated
 662 * in the CRTC's flush.
 663 */
 664static int vc4_plane_atomic_check(struct drm_plane *plane,
 665                                  struct drm_plane_state *state)
 666{
 667        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 668
 669        vc4_state->dlist_count = 0;
 670
 671        if (plane_enabled(state))
 672                return vc4_plane_mode_set(plane, state);
 673        else
 674                return 0;
 675}
 676
 677static void vc4_plane_atomic_update(struct drm_plane *plane,
 678                                    struct drm_plane_state *old_state)
 679{
 680        /* No contents here.  Since we don't know where in the CRTC's
 681         * dlist we should be stored, our dlist is uploaded to the
 682         * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
 683         * time.
 684         */
 685}
 686
 687u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
 688{
 689        struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
 690        int i;
 691
 692        vc4_state->hw_dlist = dlist;
 693
 694        /* Can't memcpy_toio() because it needs to be 32-bit writes. */
 695        for (i = 0; i < vc4_state->dlist_count; i++)
 696                writel(vc4_state->dlist[i], &dlist[i]);
 697
 698        return vc4_state->dlist_count;
 699}
 700
 701u32 vc4_plane_dlist_size(const struct drm_plane_state *state)
 702{
 703        const struct vc4_plane_state *vc4_state =
 704                container_of(state, typeof(*vc4_state), base);
 705
 706        return vc4_state->dlist_count;
 707}
 708
 709/* Updates the plane to immediately (well, once the FIFO needs
 710 * refilling) scan out from at a new framebuffer.
 711 */
 712void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
 713{
 714        struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
 715        struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
 716        uint32_t addr;
 717
 718        /* We're skipping the address adjustment for negative origin,
 719         * because this is only called on the primary plane.
 720         */
 721        WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
 722        addr = bo->paddr + fb->offsets[0];
 723
 724        /* Write the new address into the hardware immediately.  The
 725         * scanout will start from this address as soon as the FIFO
 726         * needs to refill with pixels.
 727         */
 728        writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
 729
 730        /* Also update the CPU-side dlist copy, so that any later
 731         * atomic updates that don't do a new modeset on our plane
 732         * also use our updated address.
 733         */
 734        vc4_state->dlist[vc4_state->ptr0_offset] = addr;
 735}
 736
 737static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
 738        .atomic_check = vc4_plane_atomic_check,
 739        .atomic_update = vc4_plane_atomic_update,
 740};
 741
 742static void vc4_plane_destroy(struct drm_plane *plane)
 743{
 744        drm_plane_helper_disable(plane);
 745        drm_plane_cleanup(plane);
 746}
 747
 748/* Implements immediate (non-vblank-synced) updates of the cursor
 749 * position, or falls back to the atomic helper otherwise.
 750 */
 751static int
 752vc4_update_plane(struct drm_plane *plane,
 753                 struct drm_crtc *crtc,
 754                 struct drm_framebuffer *fb,
 755                 int crtc_x, int crtc_y,
 756                 unsigned int crtc_w, unsigned int crtc_h,
 757                 uint32_t src_x, uint32_t src_y,
 758                 uint32_t src_w, uint32_t src_h)
 759{
 760        struct drm_plane_state *plane_state;
 761        struct vc4_plane_state *vc4_state;
 762
 763        if (plane != crtc->cursor)
 764                goto out;
 765
 766        plane_state = plane->state;
 767        vc4_state = to_vc4_plane_state(plane_state);
 768
 769        if (!plane_state)
 770                goto out;
 771
 772        /* If we're changing the cursor contents, do that in the
 773         * normal vblank-synced atomic path.
 774         */
 775        if (fb != plane_state->fb)
 776                goto out;
 777
 778        /* No configuring new scaling in the fast path. */
 779        if (crtc_w != plane_state->crtc_w ||
 780            crtc_h != plane_state->crtc_h ||
 781            src_w != plane_state->src_w ||
 782            src_h != plane_state->src_h) {
 783                goto out;
 784        }
 785
 786        /* Set the cursor's position on the screen.  This is the
 787         * expected change from the drm_mode_cursor_universal()
 788         * helper.
 789         */
 790        plane_state->crtc_x = crtc_x;
 791        plane_state->crtc_y = crtc_y;
 792
 793        /* Allow changing the start position within the cursor BO, if
 794         * that matters.
 795         */
 796        plane_state->src_x = src_x;
 797        plane_state->src_y = src_y;
 798
 799        /* Update the display list based on the new crtc_x/y. */
 800        vc4_plane_atomic_check(plane, plane_state);
 801
 802        /* Note that we can't just call vc4_plane_write_dlist()
 803         * because that would smash the context data that the HVS is
 804         * currently using.
 805         */
 806        writel(vc4_state->dlist[vc4_state->pos0_offset],
 807               &vc4_state->hw_dlist[vc4_state->pos0_offset]);
 808        writel(vc4_state->dlist[vc4_state->pos2_offset],
 809               &vc4_state->hw_dlist[vc4_state->pos2_offset]);
 810        writel(vc4_state->dlist[vc4_state->ptr0_offset],
 811               &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
 812
 813        return 0;
 814
 815out:
 816        return drm_atomic_helper_update_plane(plane, crtc, fb,
 817                                              crtc_x, crtc_y,
 818                                              crtc_w, crtc_h,
 819                                              src_x, src_y,
 820                                              src_w, src_h);
 821}
 822
 823static const struct drm_plane_funcs vc4_plane_funcs = {
 824        .update_plane = vc4_update_plane,
 825        .disable_plane = drm_atomic_helper_disable_plane,
 826        .destroy = vc4_plane_destroy,
 827        .set_property = NULL,
 828        .reset = vc4_plane_reset,
 829        .atomic_duplicate_state = vc4_plane_duplicate_state,
 830        .atomic_destroy_state = vc4_plane_destroy_state,
 831};
 832
 833struct drm_plane *vc4_plane_init(struct drm_device *dev,
 834                                 enum drm_plane_type type)
 835{
 836        struct drm_plane *plane = NULL;
 837        struct vc4_plane *vc4_plane;
 838        u32 formats[ARRAY_SIZE(hvs_formats)];
 839        u32 num_formats = 0;
 840        int ret = 0;
 841        unsigned i;
 842
 843        vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
 844                                 GFP_KERNEL);
 845        if (!vc4_plane) {
 846                ret = -ENOMEM;
 847                goto fail;
 848        }
 849
 850        for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
 851                /* Don't allow YUV in cursor planes, since that means
 852                 * tuning on the scaler, which we don't allow for the
 853                 * cursor.
 854                 */
 855                if (type != DRM_PLANE_TYPE_CURSOR ||
 856                    hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) {
 857                        formats[num_formats++] = hvs_formats[i].drm;
 858                }
 859        }
 860        plane = &vc4_plane->base;
 861        ret = drm_universal_plane_init(dev, plane, 0,
 862                                       &vc4_plane_funcs,
 863                                       formats, num_formats,
 864                                       type, NULL);
 865
 866        drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
 867
 868        return plane;
 869fail:
 870        if (plane)
 871                vc4_plane_destroy(plane);
 872
 873        return ERR_PTR(ret);
 874}
 875