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