linux/drivers/gpu/drm/imx/ipuv3-plane.c
<<
>>
Prefs
   1/*
   2 * i.MX IPUv3 DP Overlay Planes
   3 *
   4 * Copyright (C) 2013 Philipp Zabel, Pengutronix
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version 2
   9 * of the License, or (at your option) any later version.
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <drm/drmP.h>
  17#include <drm/drm_atomic.h>
  18#include <drm/drm_atomic_helper.h>
  19#include <drm/drm_fb_cma_helper.h>
  20#include <drm/drm_gem_cma_helper.h>
  21#include <drm/drm_plane_helper.h>
  22
  23#include "video/imx-ipu-v3.h"
  24#include "ipuv3-plane.h"
  25
  26static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
  27{
  28        return container_of(p, struct ipu_plane, base);
  29}
  30
  31static const uint32_t ipu_plane_formats[] = {
  32        DRM_FORMAT_ARGB1555,
  33        DRM_FORMAT_XRGB1555,
  34        DRM_FORMAT_ABGR1555,
  35        DRM_FORMAT_XBGR1555,
  36        DRM_FORMAT_RGBA5551,
  37        DRM_FORMAT_BGRA5551,
  38        DRM_FORMAT_ARGB4444,
  39        DRM_FORMAT_ARGB8888,
  40        DRM_FORMAT_XRGB8888,
  41        DRM_FORMAT_ABGR8888,
  42        DRM_FORMAT_XBGR8888,
  43        DRM_FORMAT_RGBA8888,
  44        DRM_FORMAT_RGBX8888,
  45        DRM_FORMAT_BGRA8888,
  46        DRM_FORMAT_BGRA8888,
  47        DRM_FORMAT_UYVY,
  48        DRM_FORMAT_VYUY,
  49        DRM_FORMAT_YUYV,
  50        DRM_FORMAT_YVYU,
  51        DRM_FORMAT_YUV420,
  52        DRM_FORMAT_YVU420,
  53        DRM_FORMAT_YUV422,
  54        DRM_FORMAT_YVU422,
  55        DRM_FORMAT_YUV444,
  56        DRM_FORMAT_YVU444,
  57        DRM_FORMAT_NV12,
  58        DRM_FORMAT_NV16,
  59        DRM_FORMAT_RGB565,
  60};
  61
  62int ipu_plane_irq(struct ipu_plane *ipu_plane)
  63{
  64        return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
  65                                     IPU_IRQ_EOF);
  66}
  67
  68static inline unsigned long
  69drm_plane_state_to_eba(struct drm_plane_state *state)
  70{
  71        struct drm_framebuffer *fb = state->fb;
  72        struct drm_gem_cma_object *cma_obj;
  73        int x = state->src_x >> 16;
  74        int y = state->src_y >> 16;
  75
  76        cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
  77        BUG_ON(!cma_obj);
  78
  79        return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y +
  80               fb->format->cpp[0] * x;
  81}
  82
  83static inline unsigned long
  84drm_plane_state_to_ubo(struct drm_plane_state *state)
  85{
  86        struct drm_framebuffer *fb = state->fb;
  87        struct drm_gem_cma_object *cma_obj;
  88        unsigned long eba = drm_plane_state_to_eba(state);
  89        int x = state->src_x >> 16;
  90        int y = state->src_y >> 16;
  91
  92        cma_obj = drm_fb_cma_get_gem_obj(fb, 1);
  93        BUG_ON(!cma_obj);
  94
  95        x /= drm_format_horz_chroma_subsampling(fb->format->format);
  96        y /= drm_format_vert_chroma_subsampling(fb->format->format);
  97
  98        return cma_obj->paddr + fb->offsets[1] + fb->pitches[1] * y +
  99               fb->format->cpp[1] * x - eba;
 100}
 101
 102static inline unsigned long
 103drm_plane_state_to_vbo(struct drm_plane_state *state)
 104{
 105        struct drm_framebuffer *fb = state->fb;
 106        struct drm_gem_cma_object *cma_obj;
 107        unsigned long eba = drm_plane_state_to_eba(state);
 108        int x = state->src_x >> 16;
 109        int y = state->src_y >> 16;
 110
 111        cma_obj = drm_fb_cma_get_gem_obj(fb, 2);
 112        BUG_ON(!cma_obj);
 113
 114        x /= drm_format_horz_chroma_subsampling(fb->format->format);
 115        y /= drm_format_vert_chroma_subsampling(fb->format->format);
 116
 117        return cma_obj->paddr + fb->offsets[2] + fb->pitches[2] * y +
 118               fb->format->cpp[2] * x - eba;
 119}
 120
 121void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
 122{
 123        if (!IS_ERR_OR_NULL(ipu_plane->dp))
 124                ipu_dp_put(ipu_plane->dp);
 125        if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
 126                ipu_dmfc_put(ipu_plane->dmfc);
 127        if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
 128                ipu_idmac_put(ipu_plane->ipu_ch);
 129}
 130
 131int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
 132{
 133        int ret;
 134
 135        ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
 136        if (IS_ERR(ipu_plane->ipu_ch)) {
 137                ret = PTR_ERR(ipu_plane->ipu_ch);
 138                DRM_ERROR("failed to get idmac channel: %d\n", ret);
 139                return ret;
 140        }
 141
 142        ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
 143        if (IS_ERR(ipu_plane->dmfc)) {
 144                ret = PTR_ERR(ipu_plane->dmfc);
 145                DRM_ERROR("failed to get dmfc: ret %d\n", ret);
 146                goto err_out;
 147        }
 148
 149        if (ipu_plane->dp_flow >= 0) {
 150                ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
 151                if (IS_ERR(ipu_plane->dp)) {
 152                        ret = PTR_ERR(ipu_plane->dp);
 153                        DRM_ERROR("failed to get dp flow: %d\n", ret);
 154                        goto err_out;
 155                }
 156        }
 157
 158        return 0;
 159err_out:
 160        ipu_plane_put_resources(ipu_plane);
 161
 162        return ret;
 163}
 164
 165static void ipu_plane_enable(struct ipu_plane *ipu_plane)
 166{
 167        if (ipu_plane->dp)
 168                ipu_dp_enable(ipu_plane->ipu);
 169        ipu_dmfc_enable_channel(ipu_plane->dmfc);
 170        ipu_idmac_enable_channel(ipu_plane->ipu_ch);
 171        if (ipu_plane->dp)
 172                ipu_dp_enable_channel(ipu_plane->dp);
 173}
 174
 175static int ipu_disable_plane(struct drm_plane *plane)
 176{
 177        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 178
 179        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 180
 181        ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
 182
 183        if (ipu_plane->dp)
 184                ipu_dp_disable_channel(ipu_plane->dp);
 185        ipu_idmac_disable_channel(ipu_plane->ipu_ch);
 186        ipu_dmfc_disable_channel(ipu_plane->dmfc);
 187        if (ipu_plane->dp)
 188                ipu_dp_disable(ipu_plane->ipu);
 189
 190        return 0;
 191}
 192
 193static void ipu_plane_destroy(struct drm_plane *plane)
 194{
 195        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 196
 197        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 198
 199        drm_plane_cleanup(plane);
 200        kfree(ipu_plane);
 201}
 202
 203static const struct drm_plane_funcs ipu_plane_funcs = {
 204        .update_plane   = drm_atomic_helper_update_plane,
 205        .disable_plane  = drm_atomic_helper_disable_plane,
 206        .destroy        = ipu_plane_destroy,
 207        .reset          = drm_atomic_helper_plane_reset,
 208        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 209        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 210};
 211
 212static int ipu_plane_atomic_check(struct drm_plane *plane,
 213                                  struct drm_plane_state *state)
 214{
 215        struct drm_plane_state *old_state = plane->state;
 216        struct drm_crtc_state *crtc_state;
 217        struct device *dev = plane->dev->dev;
 218        struct drm_framebuffer *fb = state->fb;
 219        struct drm_framebuffer *old_fb = old_state->fb;
 220        unsigned long eba, ubo, vbo, old_ubo, old_vbo;
 221        int hsub, vsub;
 222
 223        /* Ok to disable */
 224        if (!fb)
 225                return 0;
 226
 227        if (!state->crtc)
 228                return -EINVAL;
 229
 230        crtc_state =
 231                drm_atomic_get_existing_crtc_state(state->state, state->crtc);
 232        if (WARN_ON(!crtc_state))
 233                return -EINVAL;
 234
 235        /* CRTC should be enabled */
 236        if (!crtc_state->enable)
 237                return -EINVAL;
 238
 239        /* no scaling */
 240        if (state->src_w >> 16 != state->crtc_w ||
 241            state->src_h >> 16 != state->crtc_h)
 242                return -EINVAL;
 243
 244        switch (plane->type) {
 245        case DRM_PLANE_TYPE_PRIMARY:
 246                /* full plane doesn't support partial off screen */
 247                if (state->crtc_x || state->crtc_y ||
 248                    state->crtc_w != crtc_state->adjusted_mode.hdisplay ||
 249                    state->crtc_h != crtc_state->adjusted_mode.vdisplay)
 250                        return -EINVAL;
 251
 252                /* full plane minimum width is 13 pixels */
 253                if (state->crtc_w < 13)
 254                        return -EINVAL;
 255                break;
 256        case DRM_PLANE_TYPE_OVERLAY:
 257                if (state->crtc_x < 0 || state->crtc_y < 0)
 258                        return -EINVAL;
 259
 260                if (state->crtc_x + state->crtc_w >
 261                    crtc_state->adjusted_mode.hdisplay)
 262                        return -EINVAL;
 263                if (state->crtc_y + state->crtc_h >
 264                    crtc_state->adjusted_mode.vdisplay)
 265                        return -EINVAL;
 266                break;
 267        default:
 268                dev_warn(dev, "Unsupported plane type\n");
 269                return -EINVAL;
 270        }
 271
 272        if (state->crtc_h < 2)
 273                return -EINVAL;
 274
 275        /*
 276         * We support resizing active plane or changing its format by
 277         * forcing CRTC mode change in plane's ->atomic_check callback
 278         * and disabling all affected active planes in CRTC's ->atomic_disable
 279         * callback.  The planes will be reenabled in plane's ->atomic_update
 280         * callback.
 281         */
 282        if (old_fb && (state->src_w != old_state->src_w ||
 283                              state->src_h != old_state->src_h ||
 284                              fb->format != old_fb->format))
 285                crtc_state->mode_changed = true;
 286
 287        eba = drm_plane_state_to_eba(state);
 288
 289        if (eba & 0x7)
 290                return -EINVAL;
 291
 292        if (fb->pitches[0] < 1 || fb->pitches[0] > 16384)
 293                return -EINVAL;
 294
 295        if (old_fb && fb->pitches[0] != old_fb->pitches[0])
 296                crtc_state->mode_changed = true;
 297
 298        switch (fb->format->format) {
 299        case DRM_FORMAT_YUV420:
 300        case DRM_FORMAT_YVU420:
 301        case DRM_FORMAT_YUV422:
 302        case DRM_FORMAT_YVU422:
 303        case DRM_FORMAT_YUV444:
 304        case DRM_FORMAT_YVU444:
 305                /*
 306                 * Multiplanar formats have to meet the following restrictions:
 307                 * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
 308                 * - EBA, UBO and VBO are a multiple of 8
 309                 * - UBO and VBO are unsigned and not larger than 0xfffff8
 310                 * - Only EBA may be changed while scanout is active
 311                 * - The strides of U and V planes must be identical.
 312                 */
 313                vbo = drm_plane_state_to_vbo(state);
 314
 315                if (vbo & 0x7 || vbo > 0xfffff8)
 316                        return -EINVAL;
 317
 318                if (old_fb && (fb->format == old_fb->format)) {
 319                        old_vbo = drm_plane_state_to_vbo(old_state);
 320                        if (vbo != old_vbo)
 321                                crtc_state->mode_changed = true;
 322                }
 323
 324                if (fb->pitches[1] != fb->pitches[2])
 325                        return -EINVAL;
 326
 327                /* fall-through */
 328        case DRM_FORMAT_NV12:
 329        case DRM_FORMAT_NV16:
 330                ubo = drm_plane_state_to_ubo(state);
 331
 332                if (ubo & 0x7 || ubo > 0xfffff8)
 333                        return -EINVAL;
 334
 335                if (old_fb && (fb->format == old_fb->format)) {
 336                        old_ubo = drm_plane_state_to_ubo(old_state);
 337                        if (ubo != old_ubo)
 338                                crtc_state->mode_changed = true;
 339                }
 340
 341                if (fb->pitches[1] < 1 || fb->pitches[1] > 16384)
 342                        return -EINVAL;
 343
 344                if (old_fb && old_fb->pitches[1] != fb->pitches[1])
 345                        crtc_state->mode_changed = true;
 346
 347                /*
 348                 * The x/y offsets must be even in case of horizontal/vertical
 349                 * chroma subsampling.
 350                 */
 351                hsub = drm_format_horz_chroma_subsampling(fb->format->format);
 352                vsub = drm_format_vert_chroma_subsampling(fb->format->format);
 353                if (((state->src_x >> 16) & (hsub - 1)) ||
 354                    ((state->src_y >> 16) & (vsub - 1)))
 355                        return -EINVAL;
 356        }
 357
 358        return 0;
 359}
 360
 361static void ipu_plane_atomic_disable(struct drm_plane *plane,
 362                                     struct drm_plane_state *old_state)
 363{
 364        ipu_disable_plane(plane);
 365}
 366
 367static void ipu_plane_atomic_update(struct drm_plane *plane,
 368                                    struct drm_plane_state *old_state)
 369{
 370        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 371        struct drm_plane_state *state = plane->state;
 372        struct drm_crtc_state *crtc_state = state->crtc->state;
 373        struct drm_framebuffer *fb = state->fb;
 374        unsigned long eba, ubo, vbo;
 375        enum ipu_color_space ics;
 376        int active;
 377
 378        eba = drm_plane_state_to_eba(state);
 379
 380        if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) {
 381                active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
 382                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
 383                ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
 384                return;
 385        }
 386
 387        switch (ipu_plane->dp_flow) {
 388        case IPU_DP_FLOW_SYNC_BG:
 389                ipu_dp_setup_channel(ipu_plane->dp,
 390                                        IPUV3_COLORSPACE_RGB,
 391                                        IPUV3_COLORSPACE_RGB);
 392                ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
 393                break;
 394        case IPU_DP_FLOW_SYNC_FG:
 395                ics = ipu_drm_fourcc_to_colorspace(state->fb->format->format);
 396                ipu_dp_setup_channel(ipu_plane->dp, ics,
 397                                        IPUV3_COLORSPACE_UNKNOWN);
 398                ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x,
 399                                        state->crtc_y);
 400                /* Enable local alpha on partial plane */
 401                switch (state->fb->format->format) {
 402                case DRM_FORMAT_ARGB1555:
 403                case DRM_FORMAT_ABGR1555:
 404                case DRM_FORMAT_RGBA5551:
 405                case DRM_FORMAT_BGRA5551:
 406                case DRM_FORMAT_ARGB4444:
 407                case DRM_FORMAT_ARGB8888:
 408                case DRM_FORMAT_ABGR8888:
 409                case DRM_FORMAT_RGBA8888:
 410                case DRM_FORMAT_BGRA8888:
 411                        ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
 412                        break;
 413                default:
 414                        ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
 415                        break;
 416                }
 417        }
 418
 419        ipu_dmfc_config_wait4eot(ipu_plane->dmfc, state->crtc_w);
 420
 421        ipu_cpmem_zero(ipu_plane->ipu_ch);
 422        ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16,
 423                                        state->src_h >> 16);
 424        ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->format->format);
 425        ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
 426        ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
 427        ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]);
 428        switch (fb->format->format) {
 429        case DRM_FORMAT_YUV420:
 430        case DRM_FORMAT_YVU420:
 431        case DRM_FORMAT_YUV422:
 432        case DRM_FORMAT_YVU422:
 433        case DRM_FORMAT_YUV444:
 434        case DRM_FORMAT_YVU444:
 435                ubo = drm_plane_state_to_ubo(state);
 436                vbo = drm_plane_state_to_vbo(state);
 437                if (fb->format->format == DRM_FORMAT_YVU420 ||
 438                    fb->format->format == DRM_FORMAT_YVU422 ||
 439                    fb->format->format == DRM_FORMAT_YVU444)
 440                        swap(ubo, vbo);
 441
 442                ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
 443                                              fb->pitches[1], ubo, vbo);
 444
 445                dev_dbg(ipu_plane->base.dev->dev,
 446                        "phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo,
 447                        state->src_x >> 16, state->src_y >> 16);
 448                break;
 449        case DRM_FORMAT_NV12:
 450        case DRM_FORMAT_NV16:
 451                ubo = drm_plane_state_to_ubo(state);
 452
 453                ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
 454                                              fb->pitches[1], ubo, ubo);
 455
 456                dev_dbg(ipu_plane->base.dev->dev,
 457                        "phy = %lu %lu, x = %d, y = %d", eba, ubo,
 458                        state->src_x >> 16, state->src_y >> 16);
 459                break;
 460        default:
 461                dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d",
 462                        eba, state->src_x >> 16, state->src_y >> 16);
 463                break;
 464        }
 465        ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
 466        ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
 467        ipu_plane_enable(ipu_plane);
 468}
 469
 470static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
 471        .prepare_fb = drm_fb_cma_prepare_fb,
 472        .atomic_check = ipu_plane_atomic_check,
 473        .atomic_disable = ipu_plane_atomic_disable,
 474        .atomic_update = ipu_plane_atomic_update,
 475};
 476
 477struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
 478                                 int dma, int dp, unsigned int possible_crtcs,
 479                                 enum drm_plane_type type)
 480{
 481        struct ipu_plane *ipu_plane;
 482        int ret;
 483
 484        DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
 485                      dma, dp, possible_crtcs);
 486
 487        ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
 488        if (!ipu_plane) {
 489                DRM_ERROR("failed to allocate plane\n");
 490                return ERR_PTR(-ENOMEM);
 491        }
 492
 493        ipu_plane->ipu = ipu;
 494        ipu_plane->dma = dma;
 495        ipu_plane->dp_flow = dp;
 496
 497        ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
 498                                       &ipu_plane_funcs, ipu_plane_formats,
 499                                       ARRAY_SIZE(ipu_plane_formats), type,
 500                                       NULL);
 501        if (ret) {
 502                DRM_ERROR("failed to initialize plane\n");
 503                kfree(ipu_plane);
 504                return ERR_PTR(ret);
 505        }
 506
 507        drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
 508
 509        return ipu_plane;
 510}
 511