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_XRGB1555,
  27        DRM_FORMAT_XBGR1555,
  28        DRM_FORMAT_ARGB8888,
  29        DRM_FORMAT_XRGB8888,
  30        DRM_FORMAT_ABGR8888,
  31        DRM_FORMAT_XBGR8888,
  32        DRM_FORMAT_YUYV,
  33        DRM_FORMAT_YVYU,
  34        DRM_FORMAT_YUV420,
  35        DRM_FORMAT_YVU420,
  36};
  37
  38int ipu_plane_irq(struct ipu_plane *ipu_plane)
  39{
  40        return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
  41                                     IPU_IRQ_EOF);
  42}
  43
  44static int calc_vref(struct drm_display_mode *mode)
  45{
  46        unsigned long htotal, vtotal;
  47
  48        htotal = mode->htotal;
  49        vtotal = mode->vtotal;
  50
  51        if (!htotal || !vtotal)
  52                return 60;
  53
  54        return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
  55}
  56
  57static inline int calc_bandwidth(int width, int height, unsigned int vref)
  58{
  59        return width * height * vref;
  60}
  61
  62int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
  63                       int x, int y)
  64{
  65        struct drm_gem_cma_object *cma_obj;
  66        unsigned long eba;
  67        int active;
  68
  69        cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
  70        if (!cma_obj) {
  71                DRM_DEBUG_KMS("entry is null.\n");
  72                return -EFAULT;
  73        }
  74
  75        dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
  76                &cma_obj->paddr, x, y);
  77
  78        eba = cma_obj->paddr + fb->offsets[0] +
  79              fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
  80
  81        if (ipu_plane->enabled) {
  82                active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
  83                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
  84                ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
  85        } else {
  86                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
  87                ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
  88        }
  89
  90        /* cache offsets for subsequent pageflips */
  91        ipu_plane->x = x;
  92        ipu_plane->y = y;
  93
  94        return 0;
  95}
  96
  97int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
  98                       struct drm_display_mode *mode,
  99                       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 100                       unsigned int crtc_w, unsigned int crtc_h,
 101                       uint32_t src_x, uint32_t src_y,
 102                       uint32_t src_w, uint32_t src_h)
 103{
 104        struct device *dev = ipu_plane->base.dev->dev;
 105        int ret;
 106
 107        /* no scaling */
 108        if (src_w != crtc_w || src_h != crtc_h)
 109                return -EINVAL;
 110
 111        /* clip to crtc bounds */
 112        if (crtc_x < 0) {
 113                if (-crtc_x > crtc_w)
 114                        return -EINVAL;
 115                src_x += -crtc_x;
 116                src_w -= -crtc_x;
 117                crtc_w -= -crtc_x;
 118                crtc_x = 0;
 119        }
 120        if (crtc_y < 0) {
 121                if (-crtc_y > crtc_h)
 122                        return -EINVAL;
 123                src_y += -crtc_y;
 124                src_h -= -crtc_y;
 125                crtc_h -= -crtc_y;
 126                crtc_y = 0;
 127        }
 128        if (crtc_x + crtc_w > mode->hdisplay) {
 129                if (crtc_x > mode->hdisplay)
 130                        return -EINVAL;
 131                crtc_w = mode->hdisplay - crtc_x;
 132                src_w = crtc_w;
 133        }
 134        if (crtc_y + crtc_h > mode->vdisplay) {
 135                if (crtc_y > mode->vdisplay)
 136                        return -EINVAL;
 137                crtc_h = mode->vdisplay - crtc_y;
 138                src_h = crtc_h;
 139        }
 140        /* full plane minimum width is 13 pixels */
 141        if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
 142                return -EINVAL;
 143        if (crtc_h < 2)
 144                return -EINVAL;
 145
 146        /*
 147         * since we cannot touch active IDMAC channels, we do not support
 148         * resizing the enabled plane or changing its format
 149         */
 150        if (ipu_plane->enabled) {
 151                if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
 152                    fb->pixel_format != ipu_plane->base.fb->pixel_format)
 153                        return -EINVAL;
 154
 155                return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
 156        }
 157
 158        switch (ipu_plane->dp_flow) {
 159        case IPU_DP_FLOW_SYNC_BG:
 160                ret = ipu_dp_setup_channel(ipu_plane->dp,
 161                                IPUV3_COLORSPACE_RGB,
 162                                IPUV3_COLORSPACE_RGB);
 163                if (ret) {
 164                        dev_err(dev,
 165                                "initializing display processor failed with %d\n",
 166                                ret);
 167                        return ret;
 168                }
 169                ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
 170                break;
 171        case IPU_DP_FLOW_SYNC_FG:
 172                ipu_dp_setup_channel(ipu_plane->dp,
 173                                ipu_drm_fourcc_to_colorspace(fb->pixel_format),
 174                                IPUV3_COLORSPACE_UNKNOWN);
 175                ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
 176                /* Enable local alpha on partial plane */
 177                switch (fb->pixel_format) {
 178                case DRM_FORMAT_ARGB8888:
 179                case DRM_FORMAT_ABGR8888:
 180                        ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
 181                        break;
 182                default:
 183                        break;
 184                }
 185        }
 186
 187        ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
 188        if (ret) {
 189                dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
 190                return ret;
 191        }
 192
 193        ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
 194                        calc_bandwidth(crtc_w, crtc_h,
 195                                       calc_vref(mode)), 64);
 196        if (ret) {
 197                dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
 198                return ret;
 199        }
 200
 201        ipu_cpmem_zero(ipu_plane->ipu_ch);
 202        ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
 203        ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
 204        if (ret < 0) {
 205                dev_err(dev, "unsupported pixel format 0x%08x\n",
 206                        fb->pixel_format);
 207                return ret;
 208        }
 209        ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
 210        ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
 211        ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
 212
 213        ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
 214        if (ret < 0)
 215                return ret;
 216
 217        ipu_plane->w = src_w;
 218        ipu_plane->h = src_h;
 219
 220        return 0;
 221}
 222
 223void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
 224{
 225        if (!IS_ERR_OR_NULL(ipu_plane->dp))
 226                ipu_dp_put(ipu_plane->dp);
 227        if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
 228                ipu_dmfc_put(ipu_plane->dmfc);
 229        if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
 230                ipu_idmac_put(ipu_plane->ipu_ch);
 231}
 232
 233int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
 234{
 235        int ret;
 236
 237        ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
 238        if (IS_ERR(ipu_plane->ipu_ch)) {
 239                ret = PTR_ERR(ipu_plane->ipu_ch);
 240                DRM_ERROR("failed to get idmac channel: %d\n", ret);
 241                return ret;
 242        }
 243
 244        ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
 245        if (IS_ERR(ipu_plane->dmfc)) {
 246                ret = PTR_ERR(ipu_plane->dmfc);
 247                DRM_ERROR("failed to get dmfc: ret %d\n", ret);
 248                goto err_out;
 249        }
 250
 251        if (ipu_plane->dp_flow >= 0) {
 252                ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
 253                if (IS_ERR(ipu_plane->dp)) {
 254                        ret = PTR_ERR(ipu_plane->dp);
 255                        DRM_ERROR("failed to get dp flow: %d\n", ret);
 256                        goto err_out;
 257                }
 258        }
 259
 260        return 0;
 261err_out:
 262        ipu_plane_put_resources(ipu_plane);
 263
 264        return ret;
 265}
 266
 267void ipu_plane_enable(struct ipu_plane *ipu_plane)
 268{
 269        if (ipu_plane->dp)
 270                ipu_dp_enable(ipu_plane->ipu);
 271        ipu_dmfc_enable_channel(ipu_plane->dmfc);
 272        ipu_idmac_enable_channel(ipu_plane->ipu_ch);
 273        if (ipu_plane->dp)
 274                ipu_dp_enable_channel(ipu_plane->dp);
 275
 276        ipu_plane->enabled = true;
 277}
 278
 279void ipu_plane_disable(struct ipu_plane *ipu_plane)
 280{
 281        ipu_plane->enabled = false;
 282
 283        ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
 284
 285        if (ipu_plane->dp)
 286                ipu_dp_disable_channel(ipu_plane->dp);
 287        ipu_idmac_disable_channel(ipu_plane->ipu_ch);
 288        ipu_dmfc_disable_channel(ipu_plane->dmfc);
 289        if (ipu_plane->dp)
 290                ipu_dp_disable(ipu_plane->ipu);
 291}
 292
 293/*
 294 * drm_plane API
 295 */
 296
 297static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 298                            struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 299                            unsigned int crtc_w, unsigned int crtc_h,
 300                            uint32_t src_x, uint32_t src_y,
 301                            uint32_t src_w, uint32_t src_h)
 302{
 303        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 304        int ret = 0;
 305
 306        DRM_DEBUG_KMS("plane - %p\n", plane);
 307
 308        if (!ipu_plane->enabled)
 309                ret = ipu_plane_get_resources(ipu_plane);
 310        if (ret < 0)
 311                return ret;
 312
 313        ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
 314                        crtc_x, crtc_y, crtc_w, crtc_h,
 315                        src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16);
 316        if (ret < 0) {
 317                ipu_plane_put_resources(ipu_plane);
 318                return ret;
 319        }
 320
 321        if (crtc != plane->crtc)
 322                dev_info(plane->dev->dev, "crtc change: %p -> %p\n",
 323                                plane->crtc, crtc);
 324        plane->crtc = crtc;
 325
 326        if (!ipu_plane->enabled)
 327                ipu_plane_enable(ipu_plane);
 328
 329        return 0;
 330}
 331
 332static int ipu_disable_plane(struct drm_plane *plane)
 333{
 334        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 335
 336        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 337
 338        if (ipu_plane->enabled)
 339                ipu_plane_disable(ipu_plane);
 340
 341        ipu_plane_put_resources(ipu_plane);
 342
 343        return 0;
 344}
 345
 346static void ipu_plane_destroy(struct drm_plane *plane)
 347{
 348        struct ipu_plane *ipu_plane = to_ipu_plane(plane);
 349
 350        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 351
 352        ipu_disable_plane(plane);
 353        drm_plane_cleanup(plane);
 354        kfree(ipu_plane);
 355}
 356
 357static struct drm_plane_funcs ipu_plane_funcs = {
 358        .update_plane   = ipu_update_plane,
 359        .disable_plane  = ipu_disable_plane,
 360        .destroy        = ipu_plane_destroy,
 361};
 362
 363struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
 364                                 int dma, int dp, unsigned int possible_crtcs,
 365                                 bool priv)
 366{
 367        struct ipu_plane *ipu_plane;
 368        int ret;
 369
 370        DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
 371                      dma, dp, possible_crtcs);
 372
 373        ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
 374        if (!ipu_plane) {
 375                DRM_ERROR("failed to allocate plane\n");
 376                return ERR_PTR(-ENOMEM);
 377        }
 378
 379        ipu_plane->ipu = ipu;
 380        ipu_plane->dma = dma;
 381        ipu_plane->dp_flow = dp;
 382
 383        ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs,
 384                             &ipu_plane_funcs, ipu_plane_formats,
 385                             ARRAY_SIZE(ipu_plane_formats),
 386                             priv);
 387        if (ret) {
 388                DRM_ERROR("failed to initialize plane\n");
 389                kfree(ipu_plane);
 390                return ERR_PTR(ret);
 391        }
 392
 393        return ipu_plane;
 394}
 395