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