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 { 99 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 100 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 101 struct drm_rect src = { 102 .x1 = src_x, 103 .y1 = src_y, 104 .x2 = src_x + src_w, 105 .y2 = src_y + src_h, 106 }; 107 struct drm_rect dest = { 108 .x1 = crtc_x, 109 .y1 = crtc_y, 110 .x2 = crtc_x + crtc_w, 111 .y2 = crtc_y + crtc_h, 112 }; 113 const struct drm_rect clip = { 114 .x2 = crtc->mode.hdisplay, 115 .y2 = crtc->mode.vdisplay, 116 }; 117 uint32_t val, ctrl0; 118 unsigned idx = 0; 119 bool visible; 120 int ret; 121 122 trace_armada_ovl_plane_update(plane, crtc, fb, 123 crtc_x, crtc_y, crtc_w, crtc_h, 124 src_x, src_y, src_w, src_h); 125 126 ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, 127 DRM_ROTATE_0, 128 0, INT_MAX, true, false, &visible); 129 if (ret) 130 return ret; 131 132 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) | 133 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) | 134 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA; 135 136 /* Does the position/size result in nothing to display? */ 137 if (!visible) 138 ctrl0 &= ~CFG_DMA_ENA; 139 140 if (!dcrtc->plane) { 141 dcrtc->plane = plane; 142 armada_ovl_update_attr(&dplane->prop, dcrtc); 143 } 144 145 /* FIXME: overlay on an interlaced display */ 146 /* Just updating the position/size? */ 147 if (plane->fb == fb && dplane->base.state.ctrl0 == ctrl0) { 148 val = (drm_rect_height(&src) & 0xffff0000) | 149 drm_rect_width(&src) >> 16; 150 dplane->base.state.src_hw = val; 151 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN); 152 153 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest); 154 dplane->base.state.dst_hw = val; 155 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN); 156 157 val = dest.y1 << 16 | dest.x1; 158 dplane->base.state.dst_yx = val; 159 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN); 160 161 return 0; 162 } else if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) { 163 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ 164 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66, 165 dcrtc->base + LCD_SPU_SRAM_PARA1); 166 } 167 168 if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) 169 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 170 171 if (plane->fb != fb) { 172 u32 addrs[3], pixel_format; 173 int num_planes, hsub; 174 175 /* 176 * Take a reference on the new framebuffer - we want to 177 * hold on to it while the hardware is displaying it. 178 */ 179 drm_framebuffer_reference(fb); 180 181 if (plane->fb) 182 armada_ovl_retire_fb(dplane, plane->fb); 183 184 src_y = src.y1 >> 16; 185 src_x = src.x1 >> 16; 186 187 armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y); 188 189 pixel_format = fb->pixel_format; 190 hsub = drm_format_horz_chroma_subsampling(pixel_format); 191 num_planes = drm_format_num_planes(pixel_format); 192 193 /* 194 * Annoyingly, shifting a YUYV-format image by one pixel 195 * causes the U/V planes to toggle. Toggle the UV swap. 196 * (Unfortunately, this causes momentary colour flickering.) 197 */ 198 if (src_x & (hsub - 1) && num_planes == 1) 199 ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV); 200 201 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0], 202 LCD_SPU_DMA_START_ADDR_Y0); 203 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1], 204 LCD_SPU_DMA_START_ADDR_U0); 205 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2], 206 LCD_SPU_DMA_START_ADDR_V0); 207 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0], 208 LCD_SPU_DMA_START_ADDR_Y1); 209 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1], 210 LCD_SPU_DMA_START_ADDR_U1); 211 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2], 212 LCD_SPU_DMA_START_ADDR_V1); 213 214 val = fb->pitches[0] << 16 | fb->pitches[0]; 215 armada_reg_queue_set(dplane->vbl.regs, idx, val, 216 LCD_SPU_DMA_PITCH_YC); 217 val = fb->pitches[1] << 16 | fb->pitches[2]; 218 armada_reg_queue_set(dplane->vbl.regs, idx, val, 219 LCD_SPU_DMA_PITCH_UV); 220 } 221 222 val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16; 223 if (dplane->base.state.src_hw != val) { 224 dplane->base.state.src_hw = val; 225 armada_reg_queue_set(dplane->vbl.regs, idx, val, 226 LCD_SPU_DMA_HPXL_VLN); 227 } 228 229 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest); 230 if (dplane->base.state.dst_hw != val) { 231 dplane->base.state.dst_hw = val; 232 armada_reg_queue_set(dplane->vbl.regs, idx, val, 233 LCD_SPU_DZM_HPXL_VLN); 234 } 235 236 val = dest.y1 << 16 | dest.x1; 237 if (dplane->base.state.dst_yx != val) { 238 dplane->base.state.dst_yx = val; 239 armada_reg_queue_set(dplane->vbl.regs, idx, val, 240 LCD_SPU_DMA_OVSA_HPXL_VLN); 241 } 242 243 if (dplane->base.state.ctrl0 != ctrl0) { 244 dplane->base.state.ctrl0 = ctrl0; 245 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0, 246 CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE | 247 CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE | 248 CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU | 249 CFG_YUV2RGB) | CFG_DMA_ENA, 250 LCD_SPU_DMA_CTRL0); 251 } 252 if (idx) { 253 armada_reg_queue_end(dplane->vbl.regs, idx); 254 armada_drm_plane_work_queue(dcrtc, &dplane->base, 255 &dplane->vbl.work); 256 } 257 return 0; 258 } 259 260 static int armada_ovl_plane_disable(struct drm_plane *plane) 261 { 262 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 263 struct drm_framebuffer *fb; 264 struct armada_crtc *dcrtc; 265 266 if (!dplane->base.base.crtc) 267 return 0; 268 269 dcrtc = drm_to_armada_crtc(dplane->base.base.crtc); 270 271 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 272 armada_drm_crtc_plane_disable(dcrtc, plane); 273 274 dcrtc->plane = NULL; 275 dplane->base.state.ctrl0 = 0; 276 277 fb = xchg(&dplane->old_fb, NULL); 278 if (fb) 279 drm_framebuffer_unreference(fb); 280 281 return 0; 282 } 283 284 static void armada_ovl_plane_destroy(struct drm_plane *plane) 285 { 286 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 287 288 drm_plane_cleanup(plane); 289 290 kfree(dplane); 291 } 292 293 static int armada_ovl_plane_set_property(struct drm_plane *plane, 294 struct drm_property *property, uint64_t val) 295 { 296 struct armada_private *priv = plane->dev->dev_private; 297 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 298 bool update_attr = false; 299 300 if (property == priv->colorkey_prop) { 301 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) 302 dplane->prop.colorkey_yr = CCC(K2R(val)); 303 dplane->prop.colorkey_ug = CCC(K2G(val)); 304 dplane->prop.colorkey_vb = CCC(K2B(val)); 305 #undef CCC 306 update_attr = true; 307 } else if (property == priv->colorkey_min_prop) { 308 dplane->prop.colorkey_yr &= ~0x00ff0000; 309 dplane->prop.colorkey_yr |= K2R(val) << 16; 310 dplane->prop.colorkey_ug &= ~0x00ff0000; 311 dplane->prop.colorkey_ug |= K2G(val) << 16; 312 dplane->prop.colorkey_vb &= ~0x00ff0000; 313 dplane->prop.colorkey_vb |= K2B(val) << 16; 314 update_attr = true; 315 } else if (property == priv->colorkey_max_prop) { 316 dplane->prop.colorkey_yr &= ~0xff000000; 317 dplane->prop.colorkey_yr |= K2R(val) << 24; 318 dplane->prop.colorkey_ug &= ~0xff000000; 319 dplane->prop.colorkey_ug |= K2G(val) << 24; 320 dplane->prop.colorkey_vb &= ~0xff000000; 321 dplane->prop.colorkey_vb |= K2B(val) << 24; 322 update_attr = true; 323 } else if (property == priv->colorkey_val_prop) { 324 dplane->prop.colorkey_yr &= ~0x0000ff00; 325 dplane->prop.colorkey_yr |= K2R(val) << 8; 326 dplane->prop.colorkey_ug &= ~0x0000ff00; 327 dplane->prop.colorkey_ug |= K2G(val) << 8; 328 dplane->prop.colorkey_vb &= ~0x0000ff00; 329 dplane->prop.colorkey_vb |= K2B(val) << 8; 330 update_attr = true; 331 } else if (property == priv->colorkey_alpha_prop) { 332 dplane->prop.colorkey_yr &= ~0x000000ff; 333 dplane->prop.colorkey_yr |= K2R(val); 334 dplane->prop.colorkey_ug &= ~0x000000ff; 335 dplane->prop.colorkey_ug |= K2G(val); 336 dplane->prop.colorkey_vb &= ~0x000000ff; 337 dplane->prop.colorkey_vb |= K2B(val); 338 update_attr = true; 339 } else if (property == priv->colorkey_mode_prop) { 340 dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK; 341 dplane->prop.colorkey_mode |= CFG_CKMODE(val); 342 update_attr = true; 343 } else if (property == priv->brightness_prop) { 344 dplane->prop.brightness = val - 256; 345 update_attr = true; 346 } else if (property == priv->contrast_prop) { 347 dplane->prop.contrast = val; 348 update_attr = true; 349 } else if (property == priv->saturation_prop) { 350 dplane->prop.saturation = val; 351 update_attr = true; 352 } 353 354 if (update_attr && dplane->base.base.crtc) 355 armada_ovl_update_attr(&dplane->prop, 356 drm_to_armada_crtc(dplane->base.base.crtc)); 357 358 return 0; 359 } 360 361 static const struct drm_plane_funcs armada_ovl_plane_funcs = { 362 .update_plane = armada_ovl_plane_update, 363 .disable_plane = armada_ovl_plane_disable, 364 .destroy = armada_ovl_plane_destroy, 365 .set_property = armada_ovl_plane_set_property, 366 }; 367 368 static const uint32_t armada_ovl_formats[] = { 369 DRM_FORMAT_UYVY, 370 DRM_FORMAT_YUYV, 371 DRM_FORMAT_YUV420, 372 DRM_FORMAT_YVU420, 373 DRM_FORMAT_YUV422, 374 DRM_FORMAT_YVU422, 375 DRM_FORMAT_VYUY, 376 DRM_FORMAT_YVYU, 377 DRM_FORMAT_ARGB8888, 378 DRM_FORMAT_ABGR8888, 379 DRM_FORMAT_XRGB8888, 380 DRM_FORMAT_XBGR8888, 381 DRM_FORMAT_RGB888, 382 DRM_FORMAT_BGR888, 383 DRM_FORMAT_ARGB1555, 384 DRM_FORMAT_ABGR1555, 385 DRM_FORMAT_RGB565, 386 DRM_FORMAT_BGR565, 387 }; 388 389 static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = { 390 { CKMODE_DISABLE, "disabled" }, 391 { CKMODE_Y, "Y component" }, 392 { CKMODE_U, "U component" }, 393 { CKMODE_V, "V component" }, 394 { CKMODE_RGB, "RGB" }, 395 { CKMODE_R, "R component" }, 396 { CKMODE_G, "G component" }, 397 { CKMODE_B, "B component" }, 398 }; 399 400 static int armada_overlay_create_properties(struct drm_device *dev) 401 { 402 struct armada_private *priv = dev->dev_private; 403 404 if (priv->colorkey_prop) 405 return 0; 406 407 priv->colorkey_prop = drm_property_create_range(dev, 0, 408 "colorkey", 0, 0xffffff); 409 priv->colorkey_min_prop = drm_property_create_range(dev, 0, 410 "colorkey_min", 0, 0xffffff); 411 priv->colorkey_max_prop = drm_property_create_range(dev, 0, 412 "colorkey_max", 0, 0xffffff); 413 priv->colorkey_val_prop = drm_property_create_range(dev, 0, 414 "colorkey_val", 0, 0xffffff); 415 priv->colorkey_alpha_prop = drm_property_create_range(dev, 0, 416 "colorkey_alpha", 0, 0xffffff); 417 priv->colorkey_mode_prop = drm_property_create_enum(dev, 0, 418 "colorkey_mode", 419 armada_drm_colorkey_enum_list, 420 ARRAY_SIZE(armada_drm_colorkey_enum_list)); 421 priv->brightness_prop = drm_property_create_range(dev, 0, 422 "brightness", 0, 256 + 255); 423 priv->contrast_prop = drm_property_create_range(dev, 0, 424 "contrast", 0, 0x7fff); 425 priv->saturation_prop = drm_property_create_range(dev, 0, 426 "saturation", 0, 0x7fff); 427 428 if (!priv->colorkey_prop) 429 return -ENOMEM; 430 431 return 0; 432 } 433 434 int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) 435 { 436 struct armada_private *priv = dev->dev_private; 437 struct drm_mode_object *mobj; 438 struct armada_ovl_plane *dplane; 439 int ret; 440 441 ret = armada_overlay_create_properties(dev); 442 if (ret) 443 return ret; 444 445 dplane = kzalloc(sizeof(*dplane), GFP_KERNEL); 446 if (!dplane) 447 return -ENOMEM; 448 449 ret = armada_drm_plane_init(&dplane->base); 450 if (ret) { 451 kfree(dplane); 452 return ret; 453 } 454 455 dplane->vbl.work.fn = armada_ovl_plane_work; 456 457 ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs, 458 &armada_ovl_plane_funcs, 459 armada_ovl_formats, 460 ARRAY_SIZE(armada_ovl_formats), 461 DRM_PLANE_TYPE_OVERLAY, NULL); 462 if (ret) { 463 kfree(dplane); 464 return ret; 465 } 466 467 dplane->prop.colorkey_yr = 0xfefefe00; 468 dplane->prop.colorkey_ug = 0x01010100; 469 dplane->prop.colorkey_vb = 0x01010100; 470 dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB); 471 dplane->prop.brightness = 0; 472 dplane->prop.contrast = 0x4000; 473 dplane->prop.saturation = 0x4000; 474 475 mobj = &dplane->base.base.base; 476 drm_object_attach_property(mobj, priv->colorkey_prop, 477 0x0101fe); 478 drm_object_attach_property(mobj, priv->colorkey_min_prop, 479 0x0101fe); 480 drm_object_attach_property(mobj, priv->colorkey_max_prop, 481 0x0101fe); 482 drm_object_attach_property(mobj, priv->colorkey_val_prop, 483 0x0101fe); 484 drm_object_attach_property(mobj, priv->colorkey_alpha_prop, 485 0x000000); 486 drm_object_attach_property(mobj, priv->colorkey_mode_prop, 487 CKMODE_RGB); 488 drm_object_attach_property(mobj, priv->brightness_prop, 256); 489 drm_object_attach_property(mobj, priv->contrast_prop, 490 dplane->prop.contrast); 491 drm_object_attach_property(mobj, priv->saturation_prop, 492 dplane->prop.saturation); 493 494 return 0; 495 } 496