linux/drivers/gpu/drm/exynos/exynos_drm_plane.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
   3 * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
   4 *
   5 * This program is free software; you can redistribute  it and/or modify it
   6 * under  the terms of  the GNU General  Public License as published by the
   7 * Free Software Foundation;  either version 2 of the  License, or (at your
   8 * option) any later version.
   9 *
  10 */
  11
  12#include <drm/drmP.h>
  13
  14#include <drm/exynos_drm.h>
  15#include "exynos_drm_drv.h"
  16#include "exynos_drm_encoder.h"
  17#include "exynos_drm_fb.h"
  18#include "exynos_drm_gem.h"
  19
  20#define to_exynos_plane(x)      container_of(x, struct exynos_plane, base)
  21
  22struct exynos_plane {
  23        struct drm_plane                base;
  24        struct exynos_drm_overlay       overlay;
  25        bool                            enabled;
  26};
  27
  28static const uint32_t formats[] = {
  29        DRM_FORMAT_XRGB8888,
  30        DRM_FORMAT_ARGB8888,
  31        DRM_FORMAT_NV12,
  32        DRM_FORMAT_NV12MT,
  33};
  34
  35/*
  36 * This function is to get X or Y size shown via screen. This needs length and
  37 * start position of CRTC.
  38 *
  39 *      <--- length --->
  40 * CRTC ----------------
  41 *      ^ start        ^ end
  42 *
  43 * There are six cases from a to f.
  44 *
  45 *             <----- SCREEN ----->
  46 *             0                 last
  47 *   ----------|------------------|----------
  48 * CRTCs
  49 * a -------
  50 *        b -------
  51 *        c --------------------------
  52 *                 d --------
  53 *                           e -------
  54 *                                  f -------
  55 */
  56static int exynos_plane_get_size(int start, unsigned length, unsigned last)
  57{
  58        int end = start + length;
  59        int size = 0;
  60
  61        if (start <= 0) {
  62                if (end > 0)
  63                        size = min_t(unsigned, end, last);
  64        } else if (start <= last) {
  65                size = min_t(unsigned, last - start, length);
  66        }
  67
  68        return size;
  69}
  70
  71int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
  72                          struct drm_framebuffer *fb, int crtc_x, int crtc_y,
  73                          unsigned int crtc_w, unsigned int crtc_h,
  74                          uint32_t src_x, uint32_t src_y,
  75                          uint32_t src_w, uint32_t src_h)
  76{
  77        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
  78        struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
  79        unsigned int actual_w;
  80        unsigned int actual_h;
  81        int nr;
  82        int i;
  83
  84        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
  85
  86        nr = exynos_drm_fb_get_buf_cnt(fb);
  87        for (i = 0; i < nr; i++) {
  88                struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
  89
  90                if (!buffer) {
  91                        DRM_LOG_KMS("buffer is null\n");
  92                        return -EFAULT;
  93                }
  94
  95                overlay->dma_addr[i] = buffer->dma_addr;
  96
  97                DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
  98                                i, (unsigned long)overlay->dma_addr[i]);
  99        }
 100
 101        actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
 102        actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
 103
 104        if (crtc_x < 0) {
 105                if (actual_w)
 106                        src_x -= crtc_x;
 107                crtc_x = 0;
 108        }
 109
 110        if (crtc_y < 0) {
 111                if (actual_h)
 112                        src_y -= crtc_y;
 113                crtc_y = 0;
 114        }
 115
 116        /* set drm framebuffer data. */
 117        overlay->fb_x = src_x;
 118        overlay->fb_y = src_y;
 119        overlay->fb_width = fb->width;
 120        overlay->fb_height = fb->height;
 121        overlay->src_width = src_w;
 122        overlay->src_height = src_h;
 123        overlay->bpp = fb->bits_per_pixel;
 124        overlay->pitch = fb->pitches[0];
 125        overlay->pixel_format = fb->pixel_format;
 126
 127        /* set overlay range to be displayed. */
 128        overlay->crtc_x = crtc_x;
 129        overlay->crtc_y = crtc_y;
 130        overlay->crtc_width = actual_w;
 131        overlay->crtc_height = actual_h;
 132
 133        /* set drm mode data. */
 134        overlay->mode_width = crtc->mode.hdisplay;
 135        overlay->mode_height = crtc->mode.vdisplay;
 136        overlay->refresh = crtc->mode.vrefresh;
 137        overlay->scan_flag = crtc->mode.flags;
 138
 139        DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
 140                        overlay->crtc_x, overlay->crtc_y,
 141                        overlay->crtc_width, overlay->crtc_height);
 142
 143        exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
 144
 145        return 0;
 146}
 147
 148void exynos_plane_commit(struct drm_plane *plane)
 149{
 150        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 151        struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
 152
 153        exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
 154                        exynos_drm_encoder_plane_commit);
 155}
 156
 157void exynos_plane_dpms(struct drm_plane *plane, int mode)
 158{
 159        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 160        struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
 161
 162        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 163
 164        if (mode == DRM_MODE_DPMS_ON) {
 165                if (exynos_plane->enabled)
 166                        return;
 167
 168                exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
 169                                exynos_drm_encoder_plane_enable);
 170
 171                exynos_plane->enabled = true;
 172        } else {
 173                if (!exynos_plane->enabled)
 174                        return;
 175
 176                exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
 177                                exynos_drm_encoder_plane_disable);
 178
 179                exynos_plane->enabled = false;
 180        }
 181}
 182
 183static int
 184exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 185                     struct drm_framebuffer *fb, int crtc_x, int crtc_y,
 186                     unsigned int crtc_w, unsigned int crtc_h,
 187                     uint32_t src_x, uint32_t src_y,
 188                     uint32_t src_w, uint32_t src_h)
 189{
 190        int ret;
 191
 192        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 193
 194        ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
 195                        crtc_w, crtc_h, src_x >> 16, src_y >> 16,
 196                        src_w >> 16, src_h >> 16);
 197        if (ret < 0)
 198                return ret;
 199
 200        plane->crtc = crtc;
 201
 202        exynos_plane_commit(plane);
 203        exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
 204
 205        return 0;
 206}
 207
 208static int exynos_disable_plane(struct drm_plane *plane)
 209{
 210        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 211
 212        exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 213
 214        return 0;
 215}
 216
 217static void exynos_plane_destroy(struct drm_plane *plane)
 218{
 219        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 220
 221        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 222
 223        exynos_disable_plane(plane);
 224        drm_plane_cleanup(plane);
 225        kfree(exynos_plane);
 226}
 227
 228static int exynos_plane_set_property(struct drm_plane *plane,
 229                                     struct drm_property *property,
 230                                     uint64_t val)
 231{
 232        struct drm_device *dev = plane->dev;
 233        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 234        struct exynos_drm_private *dev_priv = dev->dev_private;
 235
 236        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 237
 238        if (property == dev_priv->plane_zpos_property) {
 239                exynos_plane->overlay.zpos = val;
 240                return 0;
 241        }
 242
 243        return -EINVAL;
 244}
 245
 246static struct drm_plane_funcs exynos_plane_funcs = {
 247        .update_plane   = exynos_update_plane,
 248        .disable_plane  = exynos_disable_plane,
 249        .destroy        = exynos_plane_destroy,
 250        .set_property   = exynos_plane_set_property,
 251};
 252
 253static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
 254{
 255        struct drm_device *dev = plane->dev;
 256        struct exynos_drm_private *dev_priv = dev->dev_private;
 257        struct drm_property *prop;
 258
 259        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 260
 261        prop = dev_priv->plane_zpos_property;
 262        if (!prop) {
 263                prop = drm_property_create_range(dev, 0, "zpos", 0,
 264                                                 MAX_PLANE - 1);
 265                if (!prop)
 266                        return;
 267
 268                dev_priv->plane_zpos_property = prop;
 269        }
 270
 271        drm_object_attach_property(&plane->base, prop, 0);
 272}
 273
 274struct drm_plane *exynos_plane_init(struct drm_device *dev,
 275                                    unsigned int possible_crtcs, bool priv)
 276{
 277        struct exynos_plane *exynos_plane;
 278        int err;
 279
 280        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 281
 282        exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
 283        if (!exynos_plane) {
 284                DRM_ERROR("failed to allocate plane\n");
 285                return NULL;
 286        }
 287
 288        err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
 289                              &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
 290                              priv);
 291        if (err) {
 292                DRM_ERROR("failed to initialize plane\n");
 293                kfree(exynos_plane);
 294                return NULL;
 295        }
 296
 297        if (priv)
 298                exynos_plane->overlay.zpos = DEFAULT_ZPOS;
 299        else
 300                exynos_plane_attach_zpos_property(&exynos_plane->base);
 301
 302        return &exynos_plane->base;
 303}
 304