linux/drivers/gpu/drm/armada/armada_overlay.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Russell King
   3 *  Rewritten from the dovefb driver, and Armada510 manuals.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9#include <drm/drmP.h>
  10#include <drm/drm_plane_helper.h>
  11#include "armada_crtc.h"
  12#include "armada_drm.h"
  13#include "armada_fb.h"
  14#include "armada_gem.h"
  15#include "armada_hw.h"
  16#include <drm/armada_drm.h>
  17#include "armada_ioctlP.h"
  18#include "armada_trace.h"
  19
  20struct armada_ovl_plane_properties {
  21        uint32_t colorkey_yr;
  22        uint32_t colorkey_ug;
  23        uint32_t colorkey_vb;
  24#define K2R(val) (((val) >> 0) & 0xff)
  25#define K2G(val) (((val) >> 8) & 0xff)
  26#define K2B(val) (((val) >> 16) & 0xff)
  27        int16_t  brightness;
  28        uint16_t contrast;
  29        uint16_t saturation;
  30        uint32_t colorkey_mode;
  31};
  32
  33struct armada_ovl_plane {
  34        struct armada_plane base;
  35        struct drm_framebuffer *old_fb;
  36        struct {
  37                struct armada_plane_work work;
  38                struct armada_regs regs[13];
  39        } vbl;
  40        struct armada_ovl_plane_properties prop;
  41};
  42#define drm_to_armada_ovl_plane(p) \
  43        container_of(p, struct armada_ovl_plane, base.base)
  44
  45
  46static void
  47armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
  48        struct armada_crtc *dcrtc)
  49{
  50        writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
  51        writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
  52        writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
  53
  54        writel_relaxed(prop->brightness << 16 | prop->contrast,
  55                       dcrtc->base + LCD_SPU_CONTRAST);
  56        /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
  57        writel_relaxed(prop->saturation << 16,
  58                       dcrtc->base + LCD_SPU_SATURATION);
  59        writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
  60
  61        spin_lock_irq(&dcrtc->irq_lock);
  62        armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
  63                     CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
  64                     dcrtc->base + LCD_SPU_DMA_CTRL1);
  65
  66        armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
  67        spin_unlock_irq(&dcrtc->irq_lock);
  68}
  69
  70static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
  71        struct drm_framebuffer *fb)
  72{
  73        struct drm_framebuffer *old_fb;
  74
  75        old_fb = xchg(&dplane->old_fb, fb);
  76
  77        if (old_fb)
  78                armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
  79}
  80
  81/* === Plane support === */
  82static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
  83        struct armada_plane *plane, struct armada_plane_work *work)
  84{
  85        struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base);
  86
  87        trace_armada_ovl_plane_work(&dcrtc->crtc, &plane->base);
  88
  89        armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
  90        armada_ovl_retire_fb(dplane, NULL);
  91}
  92
  93static int
  94armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
  95        struct drm_framebuffer *fb,
  96        int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
  97        uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
  98        struct drm_modeset_acquire_ctx *ctx)
  99{
 100        struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 101        struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 102        const struct drm_format_info *format;
 103        struct drm_rect src = {
 104                .x1 = src_x,
 105                .y1 = src_y,
 106                .x2 = src_x + src_w,
 107                .y2 = src_y + src_h,
 108        };
 109        struct drm_rect dest = {
 110                .x1 = crtc_x,
 111                .y1 = crtc_y,
 112                .x2 = crtc_x + crtc_w,
 113                .y2 = crtc_y + crtc_h,
 114        };
 115        const struct drm_rect clip = {
 116                .x2 = crtc->mode.hdisplay,
 117                .y2 = crtc->mode.vdisplay,
 118        };
 119        uint32_t val, ctrl0;
 120        unsigned idx = 0;
 121        bool visible, fb_changed;
 122        int ret;
 123
 124        trace_armada_ovl_plane_update(plane, crtc, fb,
 125                                 crtc_x, crtc_y, crtc_w, crtc_h,
 126                                 src_x, src_y, src_w, src_h);
 127
 128        ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
 129                                            DRM_MODE_ROTATE_0,
 130                                            0, INT_MAX, true, false, &visible);
 131        if (ret)
 132                return ret;
 133
 134        ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
 135                CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
 136                CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
 137
 138        /* Does the position/size result in nothing to display? */
 139        if (!visible)
 140                ctrl0 &= ~CFG_DMA_ENA;
 141
 142        /*
 143         * Shifting a YUV packed format image by one pixel causes the U/V
 144         * planes to swap.  Compensate for it by also toggling the UV swap.
 145         */
 146        format = fb->format;
 147        if (format->num_planes == 1 && src.x1 >> 16 & (format->hsub - 1))
 148                ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);
 149
 150        fb_changed = plane->fb != fb ||
 151                     dplane->base.state.src_x != src.x1 >> 16 ||
 152                     dplane->base.state.src_y != src.y1 >> 16;
 153
 154        if (!dcrtc->plane) {
 155                dcrtc->plane = plane;
 156                armada_ovl_update_attr(&dplane->prop, dcrtc);
 157        }
 158
 159        /* FIXME: overlay on an interlaced display */
 160        /* Just updating the position/size? */
 161        if (!fb_changed && dplane->base.state.ctrl0 == ctrl0) {
 162                val = (drm_rect_height(&src) & 0xffff0000) |
 163                      drm_rect_width(&src) >> 16;
 164                dplane->base.state.src_hw = val;
 165                writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
 166
 167                val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
 168                dplane->base.state.dst_hw = val;
 169                writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
 170
 171                val = dest.y1 << 16 | dest.x1;
 172                dplane->base.state.dst_yx = val;
 173                writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
 174
 175                return 0;
 176        } else if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) {
 177                /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
 178                armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
 179                               dcrtc->base + LCD_SPU_SRAM_PARA1);
 180        }
 181
 182        if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
 183                armada_drm_plane_work_cancel(dcrtc, &dplane->base);
 184
 185        if (fb_changed) {
 186                u32 addrs[3];
 187
 188                /*
 189                 * Take a reference on the new framebuffer - we want to
 190                 * hold on to it while the hardware is displaying it.
 191                 */
 192                drm_framebuffer_get(fb);
 193
 194                if (plane->fb)
 195                        armada_ovl_retire_fb(dplane, plane->fb);
 196
 197                dplane->base.state.src_y = src_y = src.y1 >> 16;
 198                dplane->base.state.src_x = src_x = src.x1 >> 16;
 199
 200                armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y);
 201
 202                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0],
 203                                     LCD_SPU_DMA_START_ADDR_Y0);
 204                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1],
 205                                     LCD_SPU_DMA_START_ADDR_U0);
 206                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2],
 207                                     LCD_SPU_DMA_START_ADDR_V0);
 208                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0],
 209                                     LCD_SPU_DMA_START_ADDR_Y1);
 210                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1],
 211                                     LCD_SPU_DMA_START_ADDR_U1);
 212                armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2],
 213                                     LCD_SPU_DMA_START_ADDR_V1);
 214
 215                val = fb->pitches[0] << 16 | fb->pitches[0];
 216                armada_reg_queue_set(dplane->vbl.regs, idx, val,
 217                                     LCD_SPU_DMA_PITCH_YC);
 218                val = fb->pitches[1] << 16 | fb->pitches[2];
 219                armada_reg_queue_set(dplane->vbl.regs, idx, val,
 220                                     LCD_SPU_DMA_PITCH_UV);
 221        }
 222
 223        val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16;
 224        if (dplane->base.state.src_hw != val) {
 225                dplane->base.state.src_hw = val;
 226                armada_reg_queue_set(dplane->vbl.regs, idx, val,
 227                                     LCD_SPU_DMA_HPXL_VLN);
 228        }
 229
 230        val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
 231        if (dplane->base.state.dst_hw != val) {
 232                dplane->base.state.dst_hw = val;
 233                armada_reg_queue_set(dplane->vbl.regs, idx, val,
 234                                     LCD_SPU_DZM_HPXL_VLN);
 235        }
 236
 237        val = dest.y1 << 16 | dest.x1;
 238        if (dplane->base.state.dst_yx != val) {
 239                dplane->base.state.dst_yx = val;
 240                armada_reg_queue_set(dplane->vbl.regs, idx, val,
 241                                     LCD_SPU_DMA_OVSA_HPXL_VLN);
 242        }
 243
 244        if (dplane->base.state.ctrl0 != ctrl0) {
 245                dplane->base.state.ctrl0 = ctrl0;
 246                armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
 247                        CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
 248                        CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
 249                        CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
 250                        CFG_YUV2RGB) | CFG_DMA_ENA,
 251                        LCD_SPU_DMA_CTRL0);
 252        }
 253        if (idx) {
 254                armada_reg_queue_end(dplane->vbl.regs, idx);
 255                armada_drm_plane_work_queue(dcrtc, &dplane->base,
 256                                            &dplane->vbl.work);
 257        }
 258        return 0;
 259}
 260
 261static int armada_ovl_plane_disable(struct drm_plane *plane,
 262                                    struct drm_modeset_acquire_ctx *ctx)
 263{
 264        struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 265        struct drm_framebuffer *fb;
 266        struct armada_crtc *dcrtc;
 267
 268        if (!dplane->base.base.crtc)
 269                return 0;
 270
 271        dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
 272
 273        armada_drm_plane_work_cancel(dcrtc, &dplane->base);
 274        armada_drm_crtc_plane_disable(dcrtc, plane);
 275
 276        dcrtc->plane = NULL;
 277        dplane->base.state.ctrl0 = 0;
 278
 279        fb = xchg(&dplane->old_fb, NULL);
 280        if (fb)
 281                drm_framebuffer_put(fb);
 282
 283        return 0;
 284}
 285
 286static void armada_ovl_plane_destroy(struct drm_plane *plane)
 287{
 288        struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 289
 290        drm_plane_cleanup(plane);
 291
 292        kfree(dplane);
 293}
 294
 295static int armada_ovl_plane_set_property(struct drm_plane *plane,
 296        struct drm_property *property, uint64_t val)
 297{
 298        struct armada_private *priv = plane->dev->dev_private;
 299        struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 300        bool update_attr = false;
 301
 302        if (property == priv->colorkey_prop) {
 303#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
 304                dplane->prop.colorkey_yr = CCC(K2R(val));
 305                dplane->prop.colorkey_ug = CCC(K2G(val));
 306                dplane->prop.colorkey_vb = CCC(K2B(val));
 307#undef CCC
 308                update_attr = true;
 309        } else if (property == priv->colorkey_min_prop) {
 310                dplane->prop.colorkey_yr &= ~0x00ff0000;
 311                dplane->prop.colorkey_yr |= K2R(val) << 16;
 312                dplane->prop.colorkey_ug &= ~0x00ff0000;
 313                dplane->prop.colorkey_ug |= K2G(val) << 16;
 314                dplane->prop.colorkey_vb &= ~0x00ff0000;
 315                dplane->prop.colorkey_vb |= K2B(val) << 16;
 316                update_attr = true;
 317        } else if (property == priv->colorkey_max_prop) {
 318                dplane->prop.colorkey_yr &= ~0xff000000;
 319                dplane->prop.colorkey_yr |= K2R(val) << 24;
 320                dplane->prop.colorkey_ug &= ~0xff000000;
 321                dplane->prop.colorkey_ug |= K2G(val) << 24;
 322                dplane->prop.colorkey_vb &= ~0xff000000;
 323                dplane->prop.colorkey_vb |= K2B(val) << 24;
 324                update_attr = true;
 325        } else if (property == priv->colorkey_val_prop) {
 326                dplane->prop.colorkey_yr &= ~0x0000ff00;
 327                dplane->prop.colorkey_yr |= K2R(val) << 8;
 328                dplane->prop.colorkey_ug &= ~0x0000ff00;
 329                dplane->prop.colorkey_ug |= K2G(val) << 8;
 330                dplane->prop.colorkey_vb &= ~0x0000ff00;
 331                dplane->prop.colorkey_vb |= K2B(val) << 8;
 332                update_attr = true;
 333        } else if (property == priv->colorkey_alpha_prop) {
 334                dplane->prop.colorkey_yr &= ~0x000000ff;
 335                dplane->prop.colorkey_yr |= K2R(val);
 336                dplane->prop.colorkey_ug &= ~0x000000ff;
 337                dplane->prop.colorkey_ug |= K2G(val);
 338                dplane->prop.colorkey_vb &= ~0x000000ff;
 339                dplane->prop.colorkey_vb |= K2B(val);
 340                update_attr = true;
 341        } else if (property == priv->colorkey_mode_prop) {
 342                dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
 343                dplane->prop.colorkey_mode |= CFG_CKMODE(val);
 344                update_attr = true;
 345        } else if (property == priv->brightness_prop) {
 346                dplane->prop.brightness = val - 256;
 347                update_attr = true;
 348        } else if (property == priv->contrast_prop) {
 349                dplane->prop.contrast = val;
 350                update_attr = true;
 351        } else if (property == priv->saturation_prop) {
 352                dplane->prop.saturation = val;
 353                update_attr = true;
 354        }
 355
 356        if (update_attr && dplane->base.base.crtc)
 357                armada_ovl_update_attr(&dplane->prop,
 358                                       drm_to_armada_crtc(dplane->base.base.crtc));
 359
 360        return 0;
 361}
 362
 363static const struct drm_plane_funcs armada_ovl_plane_funcs = {
 364        .update_plane   = armada_ovl_plane_update,
 365        .disable_plane  = armada_ovl_plane_disable,
 366        .destroy        = armada_ovl_plane_destroy,
 367        .set_property   = armada_ovl_plane_set_property,
 368};
 369
 370static const uint32_t armada_ovl_formats[] = {
 371        DRM_FORMAT_UYVY,
 372        DRM_FORMAT_YUYV,
 373        DRM_FORMAT_YUV420,
 374        DRM_FORMAT_YVU420,
 375        DRM_FORMAT_YUV422,
 376        DRM_FORMAT_YVU422,
 377        DRM_FORMAT_VYUY,
 378        DRM_FORMAT_YVYU,
 379        DRM_FORMAT_ARGB8888,
 380        DRM_FORMAT_ABGR8888,
 381        DRM_FORMAT_XRGB8888,
 382        DRM_FORMAT_XBGR8888,
 383        DRM_FORMAT_RGB888,
 384        DRM_FORMAT_BGR888,
 385        DRM_FORMAT_ARGB1555,
 386        DRM_FORMAT_ABGR1555,
 387        DRM_FORMAT_RGB565,
 388        DRM_FORMAT_BGR565,
 389};
 390
 391static const struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
 392        { CKMODE_DISABLE, "disabled" },
 393        { CKMODE_Y,       "Y component" },
 394        { CKMODE_U,       "U component" },
 395        { CKMODE_V,       "V component" },
 396        { CKMODE_RGB,     "RGB" },
 397        { CKMODE_R,       "R component" },
 398        { CKMODE_G,       "G component" },
 399        { CKMODE_B,       "B component" },
 400};
 401
 402static int armada_overlay_create_properties(struct drm_device *dev)
 403{
 404        struct armada_private *priv = dev->dev_private;
 405
 406        if (priv->colorkey_prop)
 407                return 0;
 408
 409        priv->colorkey_prop = drm_property_create_range(dev, 0,
 410                                "colorkey", 0, 0xffffff);
 411        priv->colorkey_min_prop = drm_property_create_range(dev, 0,
 412                                "colorkey_min", 0, 0xffffff);
 413        priv->colorkey_max_prop = drm_property_create_range(dev, 0,
 414                                "colorkey_max", 0, 0xffffff);
 415        priv->colorkey_val_prop = drm_property_create_range(dev, 0,
 416                                "colorkey_val", 0, 0xffffff);
 417        priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
 418                                "colorkey_alpha", 0, 0xffffff);
 419        priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
 420                                "colorkey_mode",
 421                                armada_drm_colorkey_enum_list,
 422                                ARRAY_SIZE(armada_drm_colorkey_enum_list));
 423        priv->brightness_prop = drm_property_create_range(dev, 0,
 424                                "brightness", 0, 256 + 255);
 425        priv->contrast_prop = drm_property_create_range(dev, 0,
 426                                "contrast", 0, 0x7fff);
 427        priv->saturation_prop = drm_property_create_range(dev, 0,
 428                                "saturation", 0, 0x7fff);
 429
 430        if (!priv->colorkey_prop)
 431                return -ENOMEM;
 432
 433        return 0;
 434}
 435
 436int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 437{
 438        struct armada_private *priv = dev->dev_private;
 439        struct drm_mode_object *mobj;
 440        struct armada_ovl_plane *dplane;
 441        int ret;
 442
 443        ret = armada_overlay_create_properties(dev);
 444        if (ret)
 445                return ret;
 446
 447        dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
 448        if (!dplane)
 449                return -ENOMEM;
 450
 451        ret = armada_drm_plane_init(&dplane->base);
 452        if (ret) {
 453                kfree(dplane);
 454                return ret;
 455        }
 456
 457        dplane->vbl.work.fn = armada_ovl_plane_work;
 458
 459        ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
 460                                       &armada_ovl_plane_funcs,
 461                                       armada_ovl_formats,
 462                                       ARRAY_SIZE(armada_ovl_formats),
 463                                       NULL,
 464                                       DRM_PLANE_TYPE_OVERLAY, NULL);
 465        if (ret) {
 466                kfree(dplane);
 467                return ret;
 468        }
 469
 470        dplane->prop.colorkey_yr = 0xfefefe00;
 471        dplane->prop.colorkey_ug = 0x01010100;
 472        dplane->prop.colorkey_vb = 0x01010100;
 473        dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
 474        dplane->prop.brightness = 0;
 475        dplane->prop.contrast = 0x4000;
 476        dplane->prop.saturation = 0x4000;
 477
 478        mobj = &dplane->base.base.base;
 479        drm_object_attach_property(mobj, priv->colorkey_prop,
 480                                   0x0101fe);
 481        drm_object_attach_property(mobj, priv->colorkey_min_prop,
 482                                   0x0101fe);
 483        drm_object_attach_property(mobj, priv->colorkey_max_prop,
 484                                   0x0101fe);
 485        drm_object_attach_property(mobj, priv->colorkey_val_prop,
 486                                   0x0101fe);
 487        drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
 488                                   0x000000);
 489        drm_object_attach_property(mobj, priv->colorkey_mode_prop,
 490                                   CKMODE_RGB);
 491        drm_object_attach_property(mobj, priv->brightness_prop, 256);
 492        drm_object_attach_property(mobj, priv->contrast_prop,
 493                                   dplane->prop.contrast);
 494        drm_object_attach_property(mobj, priv->saturation_prop,
 495                                   dplane->prop.saturation);
 496
 497        return 0;
 498}
 499