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 20 struct 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 33 struct 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 46 static void 47 armada_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 70 static 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 === */ 82 static 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 93 static int 94 armada_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 struct drm_rect src = { 103 .x1 = src_x, 104 .y1 = src_y, 105 .x2 = src_x + src_w, 106 .y2 = src_y + src_h, 107 }; 108 struct drm_rect dest = { 109 .x1 = crtc_x, 110 .y1 = crtc_y, 111 .x2 = crtc_x + crtc_w, 112 .y2 = crtc_y + crtc_h, 113 }; 114 const struct drm_rect clip = { 115 .x2 = crtc->mode.hdisplay, 116 .y2 = crtc->mode.vdisplay, 117 }; 118 uint32_t val, ctrl0; 119 unsigned idx = 0; 120 bool visible; 121 int ret; 122 123 trace_armada_ovl_plane_update(plane, crtc, fb, 124 crtc_x, crtc_y, crtc_w, crtc_h, 125 src_x, src_y, src_w, src_h); 126 127 ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, 128 DRM_MODE_ROTATE_0, 129 0, INT_MAX, true, false, &visible); 130 if (ret) 131 return ret; 132 133 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) | 134 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) | 135 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA; 136 137 /* Does the position/size result in nothing to display? */ 138 if (!visible) 139 ctrl0 &= ~CFG_DMA_ENA; 140 141 if (!dcrtc->plane) { 142 dcrtc->plane = plane; 143 armada_ovl_update_attr(&dplane->prop, dcrtc); 144 } 145 146 /* FIXME: overlay on an interlaced display */ 147 /* Just updating the position/size? */ 148 if (plane->fb == fb && dplane->base.state.ctrl0 == ctrl0) { 149 val = (drm_rect_height(&src) & 0xffff0000) | 150 drm_rect_width(&src) >> 16; 151 dplane->base.state.src_hw = val; 152 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN); 153 154 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest); 155 dplane->base.state.dst_hw = val; 156 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN); 157 158 val = dest.y1 << 16 | dest.x1; 159 dplane->base.state.dst_yx = val; 160 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN); 161 162 return 0; 163 } else if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) { 164 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ 165 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66, 166 dcrtc->base + LCD_SPU_SRAM_PARA1); 167 } 168 169 if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) 170 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 171 172 if (plane->fb != fb) { 173 u32 addrs[3], pixel_format; 174 int num_planes, hsub; 175 176 /* 177 * Take a reference on the new framebuffer - we want to 178 * hold on to it while the hardware is displaying it. 179 */ 180 drm_framebuffer_reference(fb); 181 182 if (plane->fb) 183 armada_ovl_retire_fb(dplane, plane->fb); 184 185 src_y = src.y1 >> 16; 186 src_x = src.x1 >> 16; 187 188 armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y); 189 190 pixel_format = fb->format->format; 191 hsub = drm_format_horz_chroma_subsampling(pixel_format); 192 num_planes = fb->format->num_planes; 193 194 /* 195 * Annoyingly, shifting a YUYV-format image by one pixel 196 * causes the U/V planes to toggle. Toggle the UV swap. 197 * (Unfortunately, this causes momentary colour flickering.) 198 */ 199 if (src_x & (hsub - 1) && num_planes == 1) 200 ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV); 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 261 static 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_unreference(fb); 282 283 return 0; 284 } 285 286 static 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 295 static 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 363 static 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 370 static 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 391 static 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 402 static 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 436 int 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