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_fb_cma_helper.h>
  18#include <drm/drm_gem_cma_helper.h>
  19
  20#include "video/imx-ipu-v3.h"
  21#include "ipuv3-plane.h"
  22
  23#define to_ipu_plane(x) container_of(x, struct ipu_plane, base)
  24
  25static const uint32_t ipu_plane_formats[] = {
  26        DRM_FORMAT_ARGB1555,
  27        DRM_FORMAT_XRGB1555,
  28        DRM_FORMAT_ABGR1555,
  29        DRM_FORMAT_XBGR1555,
  30        DRM_FORMAT_RGBA5551,
  31        DRM_FORMAT_BGRA5551,
  32        DRM_FORMAT_ARGB4444,
  33        DRM_FORMAT_ARGB8888,
  34        DRM_FORMAT_XRGB8888,
  35        DRM_FORMAT_ABGR8888,
  36        DRM_FORMAT_XBGR8888,
  37        DRM_FORMAT_RGBA8888,
  38        DRM_FORMAT_RGBX8888,
  39        DRM_FORMAT_BGRA8888,
  40        DRM_FORMAT_BGRA8888,
  41        DRM_FORMAT_YUYV,
  42        DRM_FORMAT_YVYU,
  43        DRM_FORMAT_YUV420,
  44        DRM_FORMAT_YVU420,
  45        DRM_FORMAT_RGB565,
  46};
  47
  48int ipu_plane_irq(struct ipu_plane *ipu_plane)
  49{
  50        return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
  51                                     IPU_IRQ_EOF);
  52}
  53
  54static int calc_vref(struct drm_display_mode *mode)
  55{
  56        unsigned long htotal, vtotal;
  57
  58        htotal = mode->htotal;
  59        vtotal = mode->vtotal;
  60
  61        if (!htotal || !vtotal)
  62                return 60;
  63
  64        return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
  65}
  66
  67static inline int calc_bandwidth(int width, int height, unsigned int vref)
  68{
  69        return width * height * vref;
  70}
  71
  72int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
  73                       int x, int y)
  74{
  75        struct drm_gem_cma_object *cma_obj[3];
  76        unsigned long eba, ubo, vbo;
  77        int active, i;
  78
  79        for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
  80                cma_obj[i] = drm_fb_cma_get_gem_obj(fb, i);
  81                if (!cma_obj[i]) {
  82                        DRM_DEBUG_KMS("plane %d entry is null.\n", i);
  83                        return -EFAULT;
  84                }
  85        }
  86
  87        eba = cma_obj[0]->paddr + fb->offsets[0] +
  88              fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
  89
  90        if (eba & 0x7) {
  91                DRM_DEBUG_KMS("base address must be a multiple of 8.\n");
  92                return -EINVAL;
  93        }
  94
  95        if (fb->pitches[0] < 1 || fb->pitches[0] > 16384) {
  96                DRM_DEBUG_KMS("pitches out of range.\n");
  97                return -EINVAL;
  98        }
  99
 100        if (ipu_plane->enabled && fb->pitches[0] != ipu_plane->stride[0]) {
 101                DRM_DEBUG_KMS("pitches must not change while plane is enabled.\n");
 102                return -EINVAL;
 103        }
 104
 105        ipu_plane->stride[0] = fb->pitches[0];
 106
 107        switch (fb->pixel_format) {
 108        case DRM_FORMAT_YUV420:
 109        case DRM_FORMAT_YVU420:
 110                /*
 111                 * Multiplanar formats have to meet the following restrictions:
 112                 * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
 113                 * - EBA, UBO and VBO are a multiple of 8
 114                 * - UBO and VBO are unsigned and not larger than 0xfffff8
 115                 * - Only EBA may be changed while scanout is active
 116                 * - The strides of U and V planes must be identical.
 117                 */
 118                ubo = cma_obj[1]->paddr + fb->offsets[1] +
 119                      fb->pitches[1] * y / 2 + x / 2 - eba;
 120                vbo = cma_obj[2]->paddr + fb->offsets[2] +
 121                      fb->pitches[2] * y / 2 + x / 2 - eba;
 122
 123                if ((ubo & 0x7) || (vbo & 0x7)) {
 124                        DRM_DEBUG_KMS("U/V buffer offsets must be a multiple of 8.\n");
 125                        return -EINVAL;
 126                }
 127
 128                if ((ubo > 0xfffff8) || (vbo > 0xfffff8)) {
 129                        DRM_DEBUG_KMS("U/V buffer offsets must be positive and not larger than 0xfffff8.\n");
 130                        return -EINVAL;
 131                }
 132
 133                if (ipu_plane->enabled && ((ipu_plane->u_offset != ubo) ||
 134                                           (ipu_plane->v_offset != vbo))) {
 135                        DRM_DEBUG_KMS("U/V buffer offsets must not change while plane is enabled.\n");
 136                        return -EINVAL;
 137                }
 138
 139                if (fb->pitches[1] != fb->pitches[2]) {
 140                        DRM_DEBUG_KMS("U/V pitches must be identical.\n");
 141                        return -EINVAL;
 142                }
 143
 144                if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) {
 145                        DRM_DEBUG_KMS("U/V pitches out of range.\n");
 146                        return -EINVAL;
 147                }
 148
 149                if (ipu_plane->enabled &&
 150                    (ipu_plane->stride[1] != fb->pitches[1])) {
 151                        DRM_DEBUG_KMS("U/V pitches must not change while plane is enabled.\n");
 152                        return -EINVAL;
 153                }
 154
 155                ipu_plane->u_offset = ubo;
 156                ipu_plane->v_offset = vbo;
 157                ipu_plane->stride[1] = fb->pitches[1];
 158
 159                dev_dbg(ipu_plane->base.dev->dev,
 160                        "phys = %pad %pad %pad, x = %d, y = %d",
 161                        &cma_obj[0]->paddr, &cma_obj[1]->paddr,
 162                        &cma_obj[2]->paddr, x, y);
 163                break;
 164        default:
 165                dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
 166                        &cma_obj[0]->paddr, x, y);
 167                break;
 168        }
 169
 170        if (ipu_plane->enabled) {
 171                active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
 172                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
 173                ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
 174        } else {
 175                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
 176                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
 177        }
 178
 179        /* cache offsets for subsequent pageflips */
 180        ipu_plane->x = x;
 181        ipu_plane->y = y;
 182
 183        return 0;
 184}
 185
 186int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
 187                       struct drm_display_mode *mode,
 188                       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 189                       unsigned int crtc_w, unsigned int crtc_h,
 190                       uint32_t src_x, uint32_t src_y,
 191                       uint32_t src_w, uint32_t src_h, bool interlaced)
 192{
 193        struct device *dev = ipu_plane->base.dev->dev;
 194        int ret;
 195
 196        /* no scaling */
 197        if (src_w != crtc_w || src_h != crtc_h)
 198                return -EINVAL;
 199
 200        /* clip to crtc bounds */
 201        if (crtc_x < 0) {
 202                if (-crtc_x > crtc_w)
 203                        return -EINVAL;
 204                src_x += -crtc_x;
 205                src_w -= -crtc_x;
 206                crtc_w -= -crtc_x;
 207                crtc_x = 0;
 208        }
 209        if (crtc_y < 0) {
 210                if (-crtc_y > crtc_h)
 211                        return -EINVAL;
 212                src_y += -crtc_y;
 213                src_h -= -crtc_y;
 214                crtc_h -= -crtc_y;
 215                crtc_y = 0;
 216        }
 217        if (crtc_x + crtc_w > mode->hdisplay) {
 218                if (crtc_x > mode->hdisplay)
 219                        return -EINVAL;
 220                crtc_w = mode->hdisplay - crtc_x;
 221                src_w = crtc_w;
 222        }
 223        if (crtc_y + crtc_h > mode->vdisplay) {
 224                if (crtc_y > mode->vdisplay)
 225                        return -EINVAL;
 226                crtc_h = mode->vdisplay - crtc_y;
 227                src_h = crtc_h;
 228        }
 229        /* full plane minimum width is 13 pixels */
 230        if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
 231                return -EINVAL;
 232        if (crtc_h < 2)
 233                return -EINVAL;
 234
 235        /*
 236         * since we cannot touch active IDMAC channels, we do not support
 237         * resizing the enabled plane or changing its format
 238         */
 239        if (ipu_plane->enabled) {
 240                if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
 241                    fb->pixel_format != ipu_plane->base.fb->pixel_format)
 242                        return -EINVAL;
 243
 244                return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
 245        }
 246
 247        switch (ipu_plane->dp_flow) {
 248        case IPU_DP_FLOW_SYNC_BG:
 249                ret = ipu_dp_setup_channel(ipu_plane->dp,
 250                                IPUV3_COLORSPACE_RGB,
 251                                IPUV3_COLORSPACE_RGB);
 252                if (ret) {
 253                        dev_err(dev,
 254                                "initializing display processor failed with %d\n",
 255                                ret);
 256                        return ret;
 257                }
 258                ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
 259                break;
 260        case IPU_DP_FLOW_SYNC_FG:
 261                ipu_dp_setup_channel(ipu_plane->dp,
 262                                ipu_drm_fourcc_to_colorspace(fb->pixel_format),
 263                                IPUV3_COLORSPACE_UNKNOWN);
 264                ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
 265                /* Enable local alpha on partial plane */
 266                switch (fb->pixel_format) {
 267                case DRM_FORMAT_ARGB1555:
 268                case DRM_FORMAT_ABGR1555:
 269                case DRM_FORMAT_RGBA5551:
 270                case DRM_FORMAT_BGRA5551:
 271                case DRM_FORMAT_ARGB4444:
 272                case DRM_FORMAT_ARGB8888:
 273                case DRM_FORMAT_ABGR8888:
 274                case DRM_FORMAT_RGBA8888:
 275                case DRM_FORMAT_BGRA8888:
 276                        ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
 277                        break;
 278                default:
 279                        break;
 280                }
 281        }
 282
 283        ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
 284                        calc_bandwidth(crtc_w, crtc_h,
 285                                       calc_vref(mode)), 64);
 286        if (ret) {
 287                dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
 288                return ret;
 289        }
 290
 291        ipu_dmfc_config_wait4eot(ipu_plane->dmfc, crtc_w);
 292
 293        ipu_cpmem_zero(ipu_plane->ipu_ch);
 294        ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
 295        ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
 296        if (ret < 0) {
 297                dev_err(dev, "unsupported pixel format 0x%08x\n",
 298                        fb->pixel_format);
 299                return ret;
 300        }
 301        ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
 302        ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
 303        ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
 304
 305        ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
 306        if (ret < 0)
 307                return ret;
 308        if (interlaced)
 309                ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]);
 310
 311        if (fb->pixel_format == DRM_FORMAT_YUV420) {
 312                ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
 313                                              ipu_plane->stride[1],
 314                                              ipu_plane->u_offset,
 315                                              ipu_plane->v_offset);
 316        } else if (fb->pixel_format == DRM_FORMAT_YVU420) {
 317                ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
 318                                              ipu_plane->stride[1],
 319                                              ipu_plane->v_offset,
 320                                              ipu_plane->u_offset);
 321        }
 322
 323        ipu_plane->w = src_w;
 324        ipu_plane->h = src_h;
 325
 326        return 0;
 327}
 328
 329void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
 330{
 331        if (!IS_ERR_OR_NULL(ipu_plane->dp))
 332                ipu_dp_put(ipu_plane->dp);
 333        if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
 334                ipu_dmfc_put(ipu_plane->dmfc);
 335        if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
 336                ipu_idmac_put(ipu_plane->ipu_ch);
 337}
 338
 339int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
 340{
 341        int ret;
 342
 343        ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
 344        if (IS_ERR(ipu_plane->ipu_ch)) {
 345                ret = PTR_ERR(ipu_plane->ipu_ch);
 346                DRM_ERROR("failed to get idmac channel: %d\n", ret);
 347                return ret;
 348        }
 349
 350        ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
 351        if (IS_ERR(ipu_plane->dmfc)) {
 352                ret = PTR_ERR(ipu_plane->dmfc);
 353                DRM_ERROR("failed to get dmfc: ret %d\n", ret);
 354                goto err_out;
 355        }
 356
 357        if (ipu_plane->dp_flow >= 0) {
 358                ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
 359                if (IS_ERR(ipu_plane->dp)) {
 360                        ret = PTR_ERR(ipu_plane->dp);
 361                        DRM_ERROR("failed to get dp flow: %d\n", ret);
 362                        goto err_out;
 363                }
 364        }
 365
 366        return 0;
 367err_out:
 368        ipu_plane_put_resources(ipu_plane);
 369
 370        return ret;
 371}
 372
 373void ipu_plane_enable(struct ipu_plane *ipu_plane)
 374{
 375        if (ipu_plane->dp)
 376                ipu_dp_enable(ipu_plane->ipu);
 377        ipu_dmfc_enable_channel(ipu_plane->dmfc);
 378        ipu_idmac_enable_channel(ipu_plane->ipu_ch);
 379        if (ipu_plane->dp)
 380                ipu_dp_enable_channel(ipu_plane->dp);
 381
 382        ipu_plane->enabled = true;
 383}
 384
 385void ipu_plane_disable(struct ipu_plane *ipu_plane)
 386{
 387        ipu_plane->enabled = false;
 388
 389        ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
 390
 391        if (ipu_plane->dp)
 392                ipu_dp_disable_channel(ipu_plane->dp);
 393        ipu_idmac_disable_channel(ipu_plane->ipu_ch);
 394        ipu_dmfc_disable_channel(ipu_plane->dmfc);
 395        if (ipu_plane->dp)
 396                ipu_dp_disable(ipu_plane->ipu);
 397}
 398
 399/*
 400 * drm_plane API
 401 */
 402
 403static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 404                            struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 405                            unsigned int crtc_w, unsigned int crtc_h,
 406                            uint32_t src_x, uint32_t src_y,
 407                            uint32_t src_w, uint32_t src_h)
 408{
 409        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 410        int ret = 0;
 411
 412        DRM_DEBUG_KMS("plane - %p\n", plane);
 413
 414        if (!ipu_plane->enabled)
 415                ret = ipu_plane_get_resources(ipu_plane);
 416        if (ret < 0)
 417                return ret;
 418
 419        ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
 420                        crtc_x, crtc_y, crtc_w, crtc_h,
 421                        src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
 422                        false);
 423        if (ret < 0) {
 424                ipu_plane_put_resources(ipu_plane);
 425                return ret;
 426        }
 427
 428        if (crtc != plane->crtc)
 429                dev_dbg(plane->dev->dev, "crtc change: %p -> %p\n",
 430                                plane->crtc, crtc);
 431        plane->crtc = crtc;
 432
 433        if (!ipu_plane->enabled)
 434                ipu_plane_enable(ipu_plane);
 435
 436        return 0;
 437}
 438
 439static int ipu_disable_plane(struct drm_plane *plane)
 440{
 441        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 442
 443        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 444
 445        if (ipu_plane->enabled)
 446                ipu_plane_disable(ipu_plane);
 447
 448        ipu_plane_put_resources(ipu_plane);
 449
 450        return 0;
 451}
 452
 453static void ipu_plane_destroy(struct drm_plane *plane)
 454{
 455        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 456
 457        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 458
 459        ipu_disable_plane(plane);
 460        drm_plane_cleanup(plane);
 461        kfree(ipu_plane);
 462}
 463
 464static struct drm_plane_funcs ipu_plane_funcs = {
 465        .update_plane   = ipu_update_plane,
 466        .disable_plane  = ipu_disable_plane,
 467        .destroy        = ipu_plane_destroy,
 468};
 469
 470struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
 471                                 int dma, int dp, unsigned int possible_crtcs,
 472                                 enum drm_plane_type type)
 473{
 474        struct ipu_plane *ipu_plane;
 475        int ret;
 476
 477        DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
 478                      dma, dp, possible_crtcs);
 479
 480        ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
 481        if (!ipu_plane) {
 482                DRM_ERROR("failed to allocate plane\n");
 483                return ERR_PTR(-ENOMEM);
 484        }
 485
 486        ipu_plane->ipu = ipu;
 487        ipu_plane->dma = dma;
 488        ipu_plane->dp_flow = dp;
 489
 490        ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
 491                                       &ipu_plane_funcs, ipu_plane_formats,
 492                                       ARRAY_SIZE(ipu_plane_formats), type,
 493                                       NULL);
 494        if (ret) {
 495                DRM_ERROR("failed to initialize plane\n");
 496                kfree(ipu_plane);
 497                return ERR_PTR(ret);
 498        }
 499
 500        return ipu_plane;
 501}
 502