linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2014 Free Electrons
   3 * Copyright (C) 2014 Atmel
   4 *
   5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "atmel_hlcdc_dc.h"
  21
  22/**
  23 * Atmel HLCDC Plane state structure.
  24 *
  25 * @base: DRM plane state
  26 * @crtc_x: x position of the plane relative to the CRTC
  27 * @crtc_y: y position of the plane relative to the CRTC
  28 * @crtc_w: visible width of the plane
  29 * @crtc_h: visible height of the plane
  30 * @src_x: x buffer position
  31 * @src_y: y buffer position
  32 * @src_w: buffer width
  33 * @src_h: buffer height
  34 * @alpha: alpha blending of the plane
  35 * @disc_x: x discard position
  36 * @disc_y: y discard position
  37 * @disc_w: discard width
  38 * @disc_h: discard height
  39 * @bpp: bytes per pixel deduced from pixel_format
  40 * @offsets: offsets to apply to the GEM buffers
  41 * @xstride: value to add to the pixel pointer between each line
  42 * @pstride: value to add to the pixel pointer between each pixel
  43 * @nplanes: number of planes (deduced from pixel_format)
  44 * @dscrs: DMA descriptors
  45 */
  46struct atmel_hlcdc_plane_state {
  47        struct drm_plane_state base;
  48        int crtc_x;
  49        int crtc_y;
  50        unsigned int crtc_w;
  51        unsigned int crtc_h;
  52        uint32_t src_x;
  53        uint32_t src_y;
  54        uint32_t src_w;
  55        uint32_t src_h;
  56
  57        u8 alpha;
  58
  59        int disc_x;
  60        int disc_y;
  61        int disc_w;
  62        int disc_h;
  63
  64        int ahb_id;
  65
  66        /* These fields are private and should not be touched */
  67        int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
  68        unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
  69        int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  70        int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  71        int nplanes;
  72
  73        /* DMA descriptors. */
  74        struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
  75};
  76
  77static inline struct atmel_hlcdc_plane_state *
  78drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
  79{
  80        return container_of(s, struct atmel_hlcdc_plane_state, base);
  81}
  82
  83#define SUBPIXEL_MASK                   0xffff
  84
  85static uint32_t rgb_formats[] = {
  86        DRM_FORMAT_C8,
  87        DRM_FORMAT_XRGB4444,
  88        DRM_FORMAT_ARGB4444,
  89        DRM_FORMAT_RGBA4444,
  90        DRM_FORMAT_ARGB1555,
  91        DRM_FORMAT_RGB565,
  92        DRM_FORMAT_RGB888,
  93        DRM_FORMAT_XRGB8888,
  94        DRM_FORMAT_ARGB8888,
  95        DRM_FORMAT_RGBA8888,
  96};
  97
  98struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
  99        .formats = rgb_formats,
 100        .nformats = ARRAY_SIZE(rgb_formats),
 101};
 102
 103static uint32_t rgb_and_yuv_formats[] = {
 104        DRM_FORMAT_C8,
 105        DRM_FORMAT_XRGB4444,
 106        DRM_FORMAT_ARGB4444,
 107        DRM_FORMAT_RGBA4444,
 108        DRM_FORMAT_ARGB1555,
 109        DRM_FORMAT_RGB565,
 110        DRM_FORMAT_RGB888,
 111        DRM_FORMAT_XRGB8888,
 112        DRM_FORMAT_ARGB8888,
 113        DRM_FORMAT_RGBA8888,
 114        DRM_FORMAT_AYUV,
 115        DRM_FORMAT_YUYV,
 116        DRM_FORMAT_UYVY,
 117        DRM_FORMAT_YVYU,
 118        DRM_FORMAT_VYUY,
 119        DRM_FORMAT_NV21,
 120        DRM_FORMAT_NV61,
 121        DRM_FORMAT_YUV422,
 122        DRM_FORMAT_YUV420,
 123};
 124
 125struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
 126        .formats = rgb_and_yuv_formats,
 127        .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
 128};
 129
 130static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
 131{
 132        switch (format) {
 133        case DRM_FORMAT_C8:
 134                *mode = ATMEL_HLCDC_C8_MODE;
 135                break;
 136        case DRM_FORMAT_XRGB4444:
 137                *mode = ATMEL_HLCDC_XRGB4444_MODE;
 138                break;
 139        case DRM_FORMAT_ARGB4444:
 140                *mode = ATMEL_HLCDC_ARGB4444_MODE;
 141                break;
 142        case DRM_FORMAT_RGBA4444:
 143                *mode = ATMEL_HLCDC_RGBA4444_MODE;
 144                break;
 145        case DRM_FORMAT_RGB565:
 146                *mode = ATMEL_HLCDC_RGB565_MODE;
 147                break;
 148        case DRM_FORMAT_RGB888:
 149                *mode = ATMEL_HLCDC_RGB888_MODE;
 150                break;
 151        case DRM_FORMAT_ARGB1555:
 152                *mode = ATMEL_HLCDC_ARGB1555_MODE;
 153                break;
 154        case DRM_FORMAT_XRGB8888:
 155                *mode = ATMEL_HLCDC_XRGB8888_MODE;
 156                break;
 157        case DRM_FORMAT_ARGB8888:
 158                *mode = ATMEL_HLCDC_ARGB8888_MODE;
 159                break;
 160        case DRM_FORMAT_RGBA8888:
 161                *mode = ATMEL_HLCDC_RGBA8888_MODE;
 162                break;
 163        case DRM_FORMAT_AYUV:
 164                *mode = ATMEL_HLCDC_AYUV_MODE;
 165                break;
 166        case DRM_FORMAT_YUYV:
 167                *mode = ATMEL_HLCDC_YUYV_MODE;
 168                break;
 169        case DRM_FORMAT_UYVY:
 170                *mode = ATMEL_HLCDC_UYVY_MODE;
 171                break;
 172        case DRM_FORMAT_YVYU:
 173                *mode = ATMEL_HLCDC_YVYU_MODE;
 174                break;
 175        case DRM_FORMAT_VYUY:
 176                *mode = ATMEL_HLCDC_VYUY_MODE;
 177                break;
 178        case DRM_FORMAT_NV21:
 179                *mode = ATMEL_HLCDC_NV21_MODE;
 180                break;
 181        case DRM_FORMAT_NV61:
 182                *mode = ATMEL_HLCDC_NV61_MODE;
 183                break;
 184        case DRM_FORMAT_YUV420:
 185                *mode = ATMEL_HLCDC_YUV420_MODE;
 186                break;
 187        case DRM_FORMAT_YUV422:
 188                *mode = ATMEL_HLCDC_YUV422_MODE;
 189                break;
 190        default:
 191                return -ENOTSUPP;
 192        }
 193
 194        return 0;
 195}
 196
 197static bool atmel_hlcdc_format_embeds_alpha(u32 format)
 198{
 199        int i;
 200
 201        for (i = 0; i < sizeof(format); i++) {
 202                char tmp = (format >> (8 * i)) & 0xff;
 203
 204                if (tmp == 'A')
 205                        return true;
 206        }
 207
 208        return false;
 209}
 210
 211static u32 heo_downscaling_xcoef[] = {
 212        0x11343311,
 213        0x000000f7,
 214        0x1635300c,
 215        0x000000f9,
 216        0x1b362c08,
 217        0x000000fb,
 218        0x1f372804,
 219        0x000000fe,
 220        0x24382400,
 221        0x00000000,
 222        0x28371ffe,
 223        0x00000004,
 224        0x2c361bfb,
 225        0x00000008,
 226        0x303516f9,
 227        0x0000000c,
 228};
 229
 230static u32 heo_downscaling_ycoef[] = {
 231        0x00123737,
 232        0x00173732,
 233        0x001b382d,
 234        0x001f3928,
 235        0x00243824,
 236        0x0028391f,
 237        0x002d381b,
 238        0x00323717,
 239};
 240
 241static u32 heo_upscaling_xcoef[] = {
 242        0xf74949f7,
 243        0x00000000,
 244        0xf55f33fb,
 245        0x000000fe,
 246        0xf5701efe,
 247        0x000000ff,
 248        0xf87c0dff,
 249        0x00000000,
 250        0x00800000,
 251        0x00000000,
 252        0x0d7cf800,
 253        0x000000ff,
 254        0x1e70f5ff,
 255        0x000000fe,
 256        0x335ff5fe,
 257        0x000000fb,
 258};
 259
 260static u32 heo_upscaling_ycoef[] = {
 261        0x00004040,
 262        0x00075920,
 263        0x00056f0c,
 264        0x00027b03,
 265        0x00008000,
 266        0x00037b02,
 267        0x000c6f05,
 268        0x00205907,
 269};
 270
 271#define ATMEL_HLCDC_XPHIDEF     4
 272#define ATMEL_HLCDC_YPHIDEF     4
 273
 274static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
 275                                                  u32 dstsize,
 276                                                  u32 phidef)
 277{
 278        u32 factor, max_memsize;
 279
 280        factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
 281        max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
 282
 283        if (max_memsize > srcsize - 1)
 284                factor--;
 285
 286        return factor;
 287}
 288
 289static void
 290atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
 291                                      const u32 *coeff_tab, int size,
 292                                      unsigned int cfg_offs)
 293{
 294        int i;
 295
 296        for (i = 0; i < size; i++)
 297                atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
 298                                            coeff_tab[i]);
 299}
 300
 301void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
 302                                    struct atmel_hlcdc_plane_state *state)
 303{
 304        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 305        u32 xfactor, yfactor;
 306
 307        if (!desc->layout.scaler_config)
 308                return;
 309
 310        if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
 311                atmel_hlcdc_layer_write_cfg(&plane->layer,
 312                                            desc->layout.scaler_config, 0);
 313                return;
 314        }
 315
 316        if (desc->layout.phicoeffs.x) {
 317                xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
 318                                                        state->crtc_w,
 319                                                        ATMEL_HLCDC_XPHIDEF);
 320
 321                yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
 322                                                        state->crtc_h,
 323                                                        ATMEL_HLCDC_YPHIDEF);
 324
 325                atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 326                                state->crtc_w < state->src_w ?
 327                                heo_downscaling_xcoef :
 328                                heo_upscaling_xcoef,
 329                                ARRAY_SIZE(heo_upscaling_xcoef),
 330                                desc->layout.phicoeffs.x);
 331
 332                atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 333                                state->crtc_h < state->src_h ?
 334                                heo_downscaling_ycoef :
 335                                heo_upscaling_ycoef,
 336                                ARRAY_SIZE(heo_upscaling_ycoef),
 337                                desc->layout.phicoeffs.y);
 338        } else {
 339                xfactor = (1024 * state->src_w) / state->crtc_w;
 340                yfactor = (1024 * state->src_h) / state->crtc_h;
 341        }
 342
 343        atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
 344                                    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
 345                                    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
 346                                                                     yfactor));
 347}
 348
 349static void
 350atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
 351                                      struct atmel_hlcdc_plane_state *state)
 352{
 353        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 354
 355        if (desc->layout.size)
 356                atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
 357                                        ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
 358                                                               state->crtc_h));
 359
 360        if (desc->layout.memsize)
 361                atmel_hlcdc_layer_write_cfg(&plane->layer,
 362                                        desc->layout.memsize,
 363                                        ATMEL_HLCDC_LAYER_SIZE(state->src_w,
 364                                                               state->src_h));
 365
 366        if (desc->layout.pos)
 367                atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
 368                                        ATMEL_HLCDC_LAYER_POS(state->crtc_x,
 369                                                              state->crtc_y));
 370
 371        atmel_hlcdc_plane_setup_scaler(plane, state);
 372}
 373
 374static void
 375atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
 376                                        struct atmel_hlcdc_plane_state *state)
 377{
 378        unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
 379        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 380        u32 format = state->base.fb->format->format;
 381
 382        /*
 383         * Rotation optimization is not working on RGB888 (rotation is still
 384         * working but without any optimization).
 385         */
 386        if (format == DRM_FORMAT_RGB888)
 387                cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
 388
 389        atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
 390                                    cfg);
 391
 392        cfg = ATMEL_HLCDC_LAYER_DMA;
 393
 394        if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
 395                cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
 396                       ATMEL_HLCDC_LAYER_ITER;
 397
 398                if (atmel_hlcdc_format_embeds_alpha(format))
 399                        cfg |= ATMEL_HLCDC_LAYER_LAEN;
 400                else
 401                        cfg |= ATMEL_HLCDC_LAYER_GAEN |
 402                               ATMEL_HLCDC_LAYER_GA(state->alpha);
 403        }
 404
 405        if (state->disc_h && state->disc_w)
 406                cfg |= ATMEL_HLCDC_LAYER_DISCEN;
 407
 408        atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
 409                                    cfg);
 410}
 411
 412static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
 413                                        struct atmel_hlcdc_plane_state *state)
 414{
 415        u32 cfg;
 416        int ret;
 417
 418        ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,
 419                                               &cfg);
 420        if (ret)
 421                return;
 422
 423        if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||
 424             state->base.fb->format->format == DRM_FORMAT_NV61) &&
 425            drm_rotation_90_or_270(state->base.rotation))
 426                cfg |= ATMEL_HLCDC_YUV422ROT;
 427
 428        atmel_hlcdc_layer_write_cfg(&plane->layer,
 429                                    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
 430}
 431
 432static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane)
 433{
 434        struct drm_crtc *crtc = plane->base.crtc;
 435        struct drm_color_lut *lut;
 436        int idx;
 437
 438        if (!crtc || !crtc->state)
 439                return;
 440
 441        if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
 442                return;
 443
 444        lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
 445
 446        for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {
 447                u32 val = ((lut->red << 8) & 0xff0000) |
 448                        (lut->green & 0xff00) |
 449                        (lut->blue >> 8);
 450
 451                atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);
 452        }
 453}
 454
 455static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
 456                                        struct atmel_hlcdc_plane_state *state)
 457{
 458        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 459        struct drm_framebuffer *fb = state->base.fb;
 460        u32 sr;
 461        int i;
 462
 463        sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 464
 465        for (i = 0; i < state->nplanes; i++) {
 466                struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
 467
 468                state->dscrs[i]->addr = gem->paddr + state->offsets[i];
 469
 470                atmel_hlcdc_layer_write_reg(&plane->layer,
 471                                            ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
 472                                            state->dscrs[i]->self);
 473
 474                if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
 475                        atmel_hlcdc_layer_write_reg(&plane->layer,
 476                                        ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
 477                                        state->dscrs[i]->addr);
 478                        atmel_hlcdc_layer_write_reg(&plane->layer,
 479                                        ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
 480                                        state->dscrs[i]->ctrl);
 481                        atmel_hlcdc_layer_write_reg(&plane->layer,
 482                                        ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
 483                                        state->dscrs[i]->self);
 484                }
 485
 486                if (desc->layout.xstride[i])
 487                        atmel_hlcdc_layer_write_cfg(&plane->layer,
 488                                                    desc->layout.xstride[i],
 489                                                    state->xstride[i]);
 490
 491                if (desc->layout.pstride[i])
 492                        atmel_hlcdc_layer_write_cfg(&plane->layer,
 493                                                    desc->layout.pstride[i],
 494                                                    state->pstride[i]);
 495        }
 496}
 497
 498int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)
 499{
 500        unsigned int ahb_load[2] = { };
 501        struct drm_plane *plane;
 502
 503        drm_atomic_crtc_state_for_each_plane(plane, c_state) {
 504                struct atmel_hlcdc_plane_state *plane_state;
 505                struct drm_plane_state *plane_s;
 506                unsigned int pixels, load = 0;
 507                int i;
 508
 509                plane_s = drm_atomic_get_plane_state(c_state->state, plane);
 510                if (IS_ERR(plane_s))
 511                        return PTR_ERR(plane_s);
 512
 513                plane_state =
 514                        drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);
 515
 516                pixels = (plane_state->src_w * plane_state->src_h) -
 517                         (plane_state->disc_w * plane_state->disc_h);
 518
 519                for (i = 0; i < plane_state->nplanes; i++)
 520                        load += pixels * plane_state->bpp[i];
 521
 522                if (ahb_load[0] <= ahb_load[1])
 523                        plane_state->ahb_id = 0;
 524                else
 525                        plane_state->ahb_id = 1;
 526
 527                ahb_load[plane_state->ahb_id] += load;
 528        }
 529
 530        return 0;
 531}
 532
 533int
 534atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
 535{
 536        int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
 537        const struct atmel_hlcdc_layer_cfg_layout *layout;
 538        struct atmel_hlcdc_plane_state *primary_state;
 539        struct drm_plane_state *primary_s;
 540        struct atmel_hlcdc_plane *primary;
 541        struct drm_plane *ovl;
 542
 543        primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
 544        layout = &primary->layer.desc->layout;
 545        if (!layout->disc_pos || !layout->disc_size)
 546                return 0;
 547
 548        primary_s = drm_atomic_get_plane_state(c_state->state,
 549                                               &primary->base);
 550        if (IS_ERR(primary_s))
 551                return PTR_ERR(primary_s);
 552
 553        primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
 554
 555        drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
 556                struct atmel_hlcdc_plane_state *ovl_state;
 557                struct drm_plane_state *ovl_s;
 558
 559                if (ovl == c_state->crtc->primary)
 560                        continue;
 561
 562                ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
 563                if (IS_ERR(ovl_s))
 564                        return PTR_ERR(ovl_s);
 565
 566                ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
 567
 568                if (!ovl_s->fb ||
 569                    atmel_hlcdc_format_embeds_alpha(ovl_s->fb->format->format) ||
 570                    ovl_state->alpha != 255)
 571                        continue;
 572
 573                /* TODO: implement a smarter hidden area detection */
 574                if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
 575                        continue;
 576
 577                disc_x = ovl_state->crtc_x;
 578                disc_y = ovl_state->crtc_y;
 579                disc_h = ovl_state->crtc_h;
 580                disc_w = ovl_state->crtc_w;
 581        }
 582
 583        primary_state->disc_x = disc_x;
 584        primary_state->disc_y = disc_y;
 585        primary_state->disc_w = disc_w;
 586        primary_state->disc_h = disc_h;
 587
 588        return 0;
 589}
 590
 591static void
 592atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
 593                                   struct atmel_hlcdc_plane_state *state)
 594{
 595        const struct atmel_hlcdc_layer_cfg_layout *layout;
 596
 597        layout = &plane->layer.desc->layout;
 598        if (!layout->disc_pos || !layout->disc_size)
 599                return;
 600
 601        atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
 602                                ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
 603                                                           state->disc_y));
 604
 605        atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
 606                                ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
 607                                                            state->disc_h));
 608}
 609
 610static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 611                                          struct drm_plane_state *s)
 612{
 613        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 614        struct atmel_hlcdc_plane_state *state =
 615                                drm_plane_state_to_atmel_hlcdc_plane_state(s);
 616        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 617        struct drm_framebuffer *fb = state->base.fb;
 618        const struct drm_display_mode *mode;
 619        struct drm_crtc_state *crtc_state;
 620        unsigned int patched_crtc_w;
 621        unsigned int patched_crtc_h;
 622        unsigned int patched_src_w;
 623        unsigned int patched_src_h;
 624        unsigned int tmp;
 625        int x_offset = 0;
 626        int y_offset = 0;
 627        int hsub = 1;
 628        int vsub = 1;
 629        int i;
 630
 631        if (!state->base.crtc || !fb)
 632                return 0;
 633
 634        crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc);
 635        mode = &crtc_state->adjusted_mode;
 636
 637        state->src_x = s->src_x;
 638        state->src_y = s->src_y;
 639        state->src_h = s->src_h;
 640        state->src_w = s->src_w;
 641        state->crtc_x = s->crtc_x;
 642        state->crtc_y = s->crtc_y;
 643        state->crtc_h = s->crtc_h;
 644        state->crtc_w = s->crtc_w;
 645        if ((state->src_x | state->src_y | state->src_w | state->src_h) &
 646            SUBPIXEL_MASK)
 647                return -EINVAL;
 648
 649        state->src_x >>= 16;
 650        state->src_y >>= 16;
 651        state->src_w >>= 16;
 652        state->src_h >>= 16;
 653
 654        state->nplanes = fb->format->num_planes;
 655        if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
 656                return -EINVAL;
 657
 658        /*
 659         * Swap width and size in case of 90 or 270 degrees rotation
 660         */
 661        if (drm_rotation_90_or_270(state->base.rotation)) {
 662                tmp = state->crtc_w;
 663                state->crtc_w = state->crtc_h;
 664                state->crtc_h = tmp;
 665                tmp = state->src_w;
 666                state->src_w = state->src_h;
 667                state->src_h = tmp;
 668        }
 669
 670        if (state->crtc_x + state->crtc_w > mode->hdisplay)
 671                patched_crtc_w = mode->hdisplay - state->crtc_x;
 672        else
 673                patched_crtc_w = state->crtc_w;
 674
 675        if (state->crtc_x < 0) {
 676                patched_crtc_w += state->crtc_x;
 677                x_offset = -state->crtc_x;
 678                state->crtc_x = 0;
 679        }
 680
 681        if (state->crtc_y + state->crtc_h > mode->vdisplay)
 682                patched_crtc_h = mode->vdisplay - state->crtc_y;
 683        else
 684                patched_crtc_h = state->crtc_h;
 685
 686        if (state->crtc_y < 0) {
 687                patched_crtc_h += state->crtc_y;
 688                y_offset = -state->crtc_y;
 689                state->crtc_y = 0;
 690        }
 691
 692        patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w,
 693                                          state->crtc_w);
 694        patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h,
 695                                          state->crtc_h);
 696
 697        hsub = drm_format_horz_chroma_subsampling(fb->format->format);
 698        vsub = drm_format_vert_chroma_subsampling(fb->format->format);
 699
 700        for (i = 0; i < state->nplanes; i++) {
 701                unsigned int offset = 0;
 702                int xdiv = i ? hsub : 1;
 703                int ydiv = i ? vsub : 1;
 704
 705                state->bpp[i] = fb->format->cpp[i];
 706                if (!state->bpp[i])
 707                        return -EINVAL;
 708
 709                switch (state->base.rotation & DRM_MODE_ROTATE_MASK) {
 710                case DRM_MODE_ROTATE_90:
 711                        offset = ((y_offset + state->src_y + patched_src_w - 1) /
 712                                  ydiv) * fb->pitches[i];
 713                        offset += ((x_offset + state->src_x) / xdiv) *
 714                                  state->bpp[i];
 715                        state->xstride[i] = ((patched_src_w - 1) / ydiv) *
 716                                          fb->pitches[i];
 717                        state->pstride[i] = -fb->pitches[i] - state->bpp[i];
 718                        break;
 719                case DRM_MODE_ROTATE_180:
 720                        offset = ((y_offset + state->src_y + patched_src_h - 1) /
 721                                  ydiv) * fb->pitches[i];
 722                        offset += ((x_offset + state->src_x + patched_src_w - 1) /
 723                                   xdiv) * state->bpp[i];
 724                        state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
 725                                           state->bpp[i]) - fb->pitches[i];
 726                        state->pstride[i] = -2 * state->bpp[i];
 727                        break;
 728                case DRM_MODE_ROTATE_270:
 729                        offset = ((y_offset + state->src_y) / ydiv) *
 730                                 fb->pitches[i];
 731                        offset += ((x_offset + state->src_x + patched_src_h - 1) /
 732                                   xdiv) * state->bpp[i];
 733                        state->xstride[i] = -(((patched_src_w - 1) / ydiv) *
 734                                            fb->pitches[i]) -
 735                                          (2 * state->bpp[i]);
 736                        state->pstride[i] = fb->pitches[i] - state->bpp[i];
 737                        break;
 738                case DRM_MODE_ROTATE_0:
 739                default:
 740                        offset = ((y_offset + state->src_y) / ydiv) *
 741                                 fb->pitches[i];
 742                        offset += ((x_offset + state->src_x) / xdiv) *
 743                                  state->bpp[i];
 744                        state->xstride[i] = fb->pitches[i] -
 745                                          ((patched_src_w / xdiv) *
 746                                           state->bpp[i]);
 747                        state->pstride[i] = 0;
 748                        break;
 749                }
 750
 751                state->offsets[i] = offset + fb->offsets[i];
 752        }
 753
 754        state->src_w = patched_src_w;
 755        state->src_h = patched_src_h;
 756        state->crtc_w = patched_crtc_w;
 757        state->crtc_h = patched_crtc_h;
 758
 759        if (!desc->layout.size &&
 760            (mode->hdisplay != state->crtc_w ||
 761             mode->vdisplay != state->crtc_h))
 762                return -EINVAL;
 763
 764        if (desc->max_height && state->crtc_h > desc->max_height)
 765                return -EINVAL;
 766
 767        if (desc->max_width && state->crtc_w > desc->max_width)
 768                return -EINVAL;
 769
 770        if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
 771            (!desc->layout.memsize ||
 772             atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)))
 773                return -EINVAL;
 774
 775        if (state->crtc_x < 0 || state->crtc_y < 0)
 776                return -EINVAL;
 777
 778        if (state->crtc_w + state->crtc_x > mode->hdisplay ||
 779            state->crtc_h + state->crtc_y > mode->vdisplay)
 780                return -EINVAL;
 781
 782        return 0;
 783}
 784
 785static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
 786                                            struct drm_plane_state *old_s)
 787{
 788        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 789        struct atmel_hlcdc_plane_state *state =
 790                        drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
 791        u32 sr;
 792
 793        if (!p->state->crtc || !p->state->fb)
 794                return;
 795
 796        atmel_hlcdc_plane_update_pos_and_size(plane, state);
 797        atmel_hlcdc_plane_update_general_settings(plane, state);
 798        atmel_hlcdc_plane_update_format(plane, state);
 799        atmel_hlcdc_plane_update_clut(plane);
 800        atmel_hlcdc_plane_update_buffers(plane, state);
 801        atmel_hlcdc_plane_update_disc_area(plane, state);
 802
 803        /* Enable the overrun interrupts. */
 804        atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
 805                                    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
 806                                    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
 807                                    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
 808
 809        /* Apply the new config at the next SOF event. */
 810        sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 811        atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
 812                        ATMEL_HLCDC_LAYER_UPDATE |
 813                        (sr & ATMEL_HLCDC_LAYER_EN ?
 814                         ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
 815}
 816
 817static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
 818                                             struct drm_plane_state *old_state)
 819{
 820        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 821
 822        /* Disable interrupts */
 823        atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
 824                                    0xffffffff);
 825
 826        /* Disable the layer */
 827        atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
 828                                    ATMEL_HLCDC_LAYER_RST |
 829                                    ATMEL_HLCDC_LAYER_A2Q |
 830                                    ATMEL_HLCDC_LAYER_UPDATE);
 831
 832        /* Clear all pending interrupts */
 833        atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 834}
 835
 836static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
 837{
 838        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 839
 840        if (plane->base.fb)
 841                drm_framebuffer_put(plane->base.fb);
 842
 843        drm_plane_cleanup(p);
 844}
 845
 846static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
 847                                                 struct drm_plane_state *s,
 848                                                 struct drm_property *property,
 849                                                 uint64_t val)
 850{
 851        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 852        struct atmel_hlcdc_plane_properties *props = plane->properties;
 853        struct atmel_hlcdc_plane_state *state =
 854                        drm_plane_state_to_atmel_hlcdc_plane_state(s);
 855
 856        if (property == props->alpha)
 857                state->alpha = val;
 858        else
 859                return -EINVAL;
 860
 861        return 0;
 862}
 863
 864static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
 865                                        const struct drm_plane_state *s,
 866                                        struct drm_property *property,
 867                                        uint64_t *val)
 868{
 869        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 870        struct atmel_hlcdc_plane_properties *props = plane->properties;
 871        const struct atmel_hlcdc_plane_state *state =
 872                container_of(s, const struct atmel_hlcdc_plane_state, base);
 873
 874        if (property == props->alpha)
 875                *val = state->alpha;
 876        else
 877                return -EINVAL;
 878
 879        return 0;
 880}
 881
 882static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
 883                                struct atmel_hlcdc_plane_properties *props)
 884{
 885        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 886
 887        if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
 888            desc->type == ATMEL_HLCDC_CURSOR_LAYER)
 889                drm_object_attach_property(&plane->base.base,
 890                                           props->alpha, 255);
 891
 892        if (desc->layout.xstride && desc->layout.pstride) {
 893                int ret;
 894
 895                ret = drm_plane_create_rotation_property(&plane->base,
 896                                                         DRM_MODE_ROTATE_0,
 897                                                         DRM_MODE_ROTATE_0 |
 898                                                         DRM_MODE_ROTATE_90 |
 899                                                         DRM_MODE_ROTATE_180 |
 900                                                         DRM_MODE_ROTATE_270);
 901                if (ret)
 902                        return ret;
 903        }
 904
 905        if (desc->layout.csc) {
 906                /*
 907                 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
 908                 * userspace modify these factors (using a BLOB property ?).
 909                 */
 910                atmel_hlcdc_layer_write_cfg(&plane->layer,
 911                                            desc->layout.csc,
 912                                            0x4c900091);
 913                atmel_hlcdc_layer_write_cfg(&plane->layer,
 914                                            desc->layout.csc + 1,
 915                                            0x7a5f5090);
 916                atmel_hlcdc_layer_write_cfg(&plane->layer,
 917                                            desc->layout.csc + 2,
 918                                            0x40040890);
 919        }
 920
 921        return 0;
 922}
 923
 924void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
 925{
 926        const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 927        u32 isr;
 928
 929        isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 930
 931        /*
 932         * There's not much we can do in case of overrun except informing
 933         * the user. However, we are in interrupt context here, hence the
 934         * use of dev_dbg().
 935         */
 936        if (isr &
 937            (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
 938             ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
 939                dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
 940                        desc->name);
 941}
 942
 943static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
 944        .atomic_check = atmel_hlcdc_plane_atomic_check,
 945        .atomic_update = atmel_hlcdc_plane_atomic_update,
 946        .atomic_disable = atmel_hlcdc_plane_atomic_disable,
 947};
 948
 949static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
 950                                         struct atmel_hlcdc_plane_state *state)
 951{
 952        struct atmel_hlcdc_dc *dc = p->dev->dev_private;
 953        int i;
 954
 955        for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
 956                struct atmel_hlcdc_dma_channel_dscr *dscr;
 957                dma_addr_t dscr_dma;
 958
 959                dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
 960                if (!dscr)
 961                        goto err;
 962
 963                dscr->addr = 0;
 964                dscr->next = dscr_dma;
 965                dscr->self = dscr_dma;
 966                dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
 967
 968                state->dscrs[i] = dscr;
 969        }
 970
 971        return 0;
 972
 973err:
 974        for (i--; i >= 0; i--) {
 975                dma_pool_free(dc->dscrpool, state->dscrs[i],
 976                              state->dscrs[i]->self);
 977        }
 978
 979        return -ENOMEM;
 980}
 981
 982static void atmel_hlcdc_plane_reset(struct drm_plane *p)
 983{
 984        struct atmel_hlcdc_plane_state *state;
 985
 986        if (p->state) {
 987                state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
 988
 989                if (state->base.fb)
 990                        drm_framebuffer_put(state->base.fb);
 991
 992                kfree(state);
 993                p->state = NULL;
 994        }
 995
 996        state = kzalloc(sizeof(*state), GFP_KERNEL);
 997        if (state) {
 998                if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
 999                        kfree(state);
1000                        dev_err(p->dev->dev,
1001                                "Failed to allocate initial plane state\n");
1002                        return;
1003                }
1004
1005                state->alpha = 255;
1006                p->state = &state->base;
1007                p->state->plane = p;
1008        }
1009}
1010
1011static struct drm_plane_state *
1012atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
1013{
1014        struct atmel_hlcdc_plane_state *state =
1015                        drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
1016        struct atmel_hlcdc_plane_state *copy;
1017
1018        copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
1019        if (!copy)
1020                return NULL;
1021
1022        if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
1023                kfree(copy);
1024                return NULL;
1025        }
1026
1027        if (copy->base.fb)
1028                drm_framebuffer_get(copy->base.fb);
1029
1030        return &copy->base;
1031}
1032
1033static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
1034                                                   struct drm_plane_state *s)
1035{
1036        struct atmel_hlcdc_plane_state *state =
1037                        drm_plane_state_to_atmel_hlcdc_plane_state(s);
1038        struct atmel_hlcdc_dc *dc = p->dev->dev_private;
1039        int i;
1040
1041        for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
1042                dma_pool_free(dc->dscrpool, state->dscrs[i],
1043                              state->dscrs[i]->self);
1044        }
1045
1046        if (s->fb)
1047                drm_framebuffer_put(s->fb);
1048
1049        kfree(state);
1050}
1051
1052static const struct drm_plane_funcs layer_plane_funcs = {
1053        .update_plane = drm_atomic_helper_update_plane,
1054        .disable_plane = drm_atomic_helper_disable_plane,
1055        .destroy = atmel_hlcdc_plane_destroy,
1056        .reset = atmel_hlcdc_plane_reset,
1057        .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
1058        .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
1059        .atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
1060        .atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
1061};
1062
1063static int atmel_hlcdc_plane_create(struct drm_device *dev,
1064                                    const struct atmel_hlcdc_layer_desc *desc,
1065                                    struct atmel_hlcdc_plane_properties *props)
1066{
1067        struct atmel_hlcdc_dc *dc = dev->dev_private;
1068        struct atmel_hlcdc_plane *plane;
1069        enum drm_plane_type type;
1070        int ret;
1071
1072        plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
1073        if (!plane)
1074                return -ENOMEM;
1075
1076        atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
1077        plane->properties = props;
1078
1079        if (desc->type == ATMEL_HLCDC_BASE_LAYER)
1080                type = DRM_PLANE_TYPE_PRIMARY;
1081        else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
1082                type = DRM_PLANE_TYPE_CURSOR;
1083        else
1084                type = DRM_PLANE_TYPE_OVERLAY;
1085
1086        ret = drm_universal_plane_init(dev, &plane->base, 0,
1087                                       &layer_plane_funcs,
1088                                       desc->formats->formats,
1089                                       desc->formats->nformats,
1090                                       NULL, type, NULL);
1091        if (ret)
1092                return ret;
1093
1094        drm_plane_helper_add(&plane->base,
1095                             &atmel_hlcdc_layer_plane_helper_funcs);
1096
1097        /* Set default property values*/
1098        ret = atmel_hlcdc_plane_init_properties(plane, props);
1099        if (ret)
1100                return ret;
1101
1102        dc->layers[desc->id] = &plane->layer;
1103
1104        return 0;
1105}
1106
1107static struct atmel_hlcdc_plane_properties *
1108atmel_hlcdc_plane_create_properties(struct drm_device *dev)
1109{
1110        struct atmel_hlcdc_plane_properties *props;
1111
1112        props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
1113        if (!props)
1114                return ERR_PTR(-ENOMEM);
1115
1116        props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
1117        if (!props->alpha)
1118                return ERR_PTR(-ENOMEM);
1119
1120        return props;
1121}
1122
1123int atmel_hlcdc_create_planes(struct drm_device *dev)
1124{
1125        struct atmel_hlcdc_dc *dc = dev->dev_private;
1126        struct atmel_hlcdc_plane_properties *props;
1127        const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
1128        int nlayers = dc->desc->nlayers;
1129        int i, ret;
1130
1131        props = atmel_hlcdc_plane_create_properties(dev);
1132        if (IS_ERR(props))
1133                return PTR_ERR(props);
1134
1135        dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
1136                                sizeof(struct atmel_hlcdc_dma_channel_dscr),
1137                                sizeof(u64), 0);
1138        if (!dc->dscrpool)
1139                return -ENOMEM;
1140
1141        for (i = 0; i < nlayers; i++) {
1142                if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
1143                    descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
1144                    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
1145                        continue;
1146
1147                ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
1148                if (ret)
1149                        return ret;
1150        }
1151
1152        return 0;
1153}
1154