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 "armada_crtc.h" 11 #include "armada_drm.h" 12 #include "armada_fb.h" 13 #include "armada_gem.h" 14 #include "armada_hw.h" 15 #include <drm/armada_drm.h> 16 #include "armada_ioctlP.h" 17 18 struct armada_plane_properties { 19 uint32_t colorkey_yr; 20 uint32_t colorkey_ug; 21 uint32_t colorkey_vb; 22 #define K2R(val) (((val) >> 0) & 0xff) 23 #define K2G(val) (((val) >> 8) & 0xff) 24 #define K2B(val) (((val) >> 16) & 0xff) 25 int16_t brightness; 26 uint16_t contrast; 27 uint16_t saturation; 28 uint32_t colorkey_mode; 29 }; 30 31 struct armada_plane { 32 struct drm_plane base; 33 spinlock_t lock; 34 struct drm_framebuffer *old_fb; 35 uint32_t src_hw; 36 uint32_t dst_hw; 37 uint32_t dst_yx; 38 uint32_t ctrl0; 39 struct { 40 struct armada_vbl_event update; 41 struct armada_regs regs[13]; 42 wait_queue_head_t wait; 43 } vbl; 44 struct armada_plane_properties prop; 45 }; 46 #define drm_to_armada_plane(p) container_of(p, struct armada_plane, base) 47 48 49 static void 50 armada_ovl_update_attr(struct armada_plane_properties *prop, 51 struct armada_crtc *dcrtc) 52 { 53 writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y); 54 writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U); 55 writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V); 56 57 writel_relaxed(prop->brightness << 16 | prop->contrast, 58 dcrtc->base + LCD_SPU_CONTRAST); 59 /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */ 60 writel_relaxed(prop->saturation << 16, 61 dcrtc->base + LCD_SPU_SATURATION); 62 writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE); 63 64 spin_lock_irq(&dcrtc->irq_lock); 65 armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA, 66 CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK, 67 dcrtc->base + LCD_SPU_DMA_CTRL1); 68 69 armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG); 70 spin_unlock_irq(&dcrtc->irq_lock); 71 } 72 73 /* === Plane support === */ 74 static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data) 75 { 76 struct armada_plane *dplane = data; 77 struct drm_framebuffer *fb; 78 79 armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); 80 81 spin_lock(&dplane->lock); 82 fb = dplane->old_fb; 83 dplane->old_fb = NULL; 84 spin_unlock(&dplane->lock); 85 86 if (fb) 87 armada_drm_queue_unref_work(dcrtc->crtc.dev, fb); 88 } 89 90 static unsigned armada_limit(int start, unsigned size, unsigned max) 91 { 92 int end = start + size; 93 if (end < 0) 94 return 0; 95 if (start < 0) 96 start = 0; 97 return (unsigned)end > max ? max - start : end - start; 98 } 99 100 static int 101 armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, 102 struct drm_framebuffer *fb, 103 int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h, 104 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) 105 { 106 struct armada_plane *dplane = drm_to_armada_plane(plane); 107 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 108 uint32_t val, ctrl0; 109 unsigned idx = 0; 110 int ret; 111 112 crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay); 113 crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay); 114 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) | 115 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) | 116 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA; 117 118 /* Does the position/size result in nothing to display? */ 119 if (crtc_w == 0 || crtc_h == 0) { 120 ctrl0 &= ~CFG_DMA_ENA; 121 } 122 123 /* 124 * FIXME: if the starting point is off screen, we need to 125 * adjust src_x, src_y, src_w, src_h appropriately, and 126 * according to the scale. 127 */ 128 129 if (!dcrtc->plane) { 130 dcrtc->plane = plane; 131 armada_ovl_update_attr(&dplane->prop, dcrtc); 132 } 133 134 /* FIXME: overlay on an interlaced display */ 135 /* Just updating the position/size? */ 136 if (plane->fb == fb && dplane->ctrl0 == ctrl0) { 137 val = (src_h & 0xffff0000) | src_w >> 16; 138 dplane->src_hw = val; 139 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN); 140 val = crtc_h << 16 | crtc_w; 141 dplane->dst_hw = val; 142 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN); 143 val = crtc_y << 16 | crtc_x; 144 dplane->dst_yx = val; 145 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN); 146 return 0; 147 } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) { 148 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ 149 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66, 150 dcrtc->base + LCD_SPU_SRAM_PARA1); 151 } 152 153 ret = wait_event_timeout(dplane->vbl.wait, 154 list_empty(&dplane->vbl.update.node), 155 HZ/25); 156 if (ret < 0) 157 return ret; 158 159 if (plane->fb != fb) { 160 struct armada_gem_object *obj = drm_fb_obj(fb); 161 uint32_t sy, su, sv; 162 163 /* 164 * Take a reference on the new framebuffer - we want to 165 * hold on to it while the hardware is displaying it. 166 */ 167 drm_framebuffer_reference(fb); 168 169 if (plane->fb) { 170 struct drm_framebuffer *older_fb; 171 172 spin_lock_irq(&dplane->lock); 173 older_fb = dplane->old_fb; 174 dplane->old_fb = plane->fb; 175 spin_unlock_irq(&dplane->lock); 176 if (older_fb) 177 armada_drm_queue_unref_work(dcrtc->crtc.dev, 178 older_fb); 179 } 180 181 src_y >>= 16; 182 src_x >>= 16; 183 sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] + 184 src_x * fb->bits_per_pixel / 8; 185 su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] + 186 src_x; 187 sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] + 188 src_x; 189 190 armada_reg_queue_set(dplane->vbl.regs, idx, sy, 191 LCD_SPU_DMA_START_ADDR_Y0); 192 armada_reg_queue_set(dplane->vbl.regs, idx, su, 193 LCD_SPU_DMA_START_ADDR_U0); 194 armada_reg_queue_set(dplane->vbl.regs, idx, sv, 195 LCD_SPU_DMA_START_ADDR_V0); 196 armada_reg_queue_set(dplane->vbl.regs, idx, sy, 197 LCD_SPU_DMA_START_ADDR_Y1); 198 armada_reg_queue_set(dplane->vbl.regs, idx, su, 199 LCD_SPU_DMA_START_ADDR_U1); 200 armada_reg_queue_set(dplane->vbl.regs, idx, sv, 201 LCD_SPU_DMA_START_ADDR_V1); 202 203 val = fb->pitches[0] << 16 | fb->pitches[0]; 204 armada_reg_queue_set(dplane->vbl.regs, idx, val, 205 LCD_SPU_DMA_PITCH_YC); 206 val = fb->pitches[1] << 16 | fb->pitches[2]; 207 armada_reg_queue_set(dplane->vbl.regs, idx, val, 208 LCD_SPU_DMA_PITCH_UV); 209 } 210 211 val = (src_h & 0xffff0000) | src_w >> 16; 212 if (dplane->src_hw != val) { 213 dplane->src_hw = val; 214 armada_reg_queue_set(dplane->vbl.regs, idx, val, 215 LCD_SPU_DMA_HPXL_VLN); 216 } 217 val = crtc_h << 16 | crtc_w; 218 if (dplane->dst_hw != val) { 219 dplane->dst_hw = val; 220 armada_reg_queue_set(dplane->vbl.regs, idx, val, 221 LCD_SPU_DZM_HPXL_VLN); 222 } 223 val = crtc_y << 16 | crtc_x; 224 if (dplane->dst_yx != val) { 225 dplane->dst_yx = val; 226 armada_reg_queue_set(dplane->vbl.regs, idx, val, 227 LCD_SPU_DMA_OVSA_HPXL_VLN); 228 } 229 if (dplane->ctrl0 != ctrl0) { 230 dplane->ctrl0 = ctrl0; 231 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0, 232 CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE | 233 CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE | 234 CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU | 235 CFG_YUV2RGB) | CFG_DMA_ENA, 236 LCD_SPU_DMA_CTRL0); 237 } 238 if (idx) { 239 armada_reg_queue_end(dplane->vbl.regs, idx); 240 armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update); 241 } 242 return 0; 243 } 244 245 static int armada_plane_disable(struct drm_plane *plane) 246 { 247 struct armada_plane *dplane = drm_to_armada_plane(plane); 248 struct drm_framebuffer *fb; 249 struct armada_crtc *dcrtc; 250 251 if (!dplane->base.crtc) 252 return 0; 253 254 dcrtc = drm_to_armada_crtc(dplane->base.crtc); 255 dcrtc->plane = NULL; 256 257 spin_lock_irq(&dcrtc->irq_lock); 258 armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update); 259 armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); 260 dplane->ctrl0 = 0; 261 spin_unlock_irq(&dcrtc->irq_lock); 262 263 /* Power down the Y/U/V FIFOs */ 264 armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0, 265 dcrtc->base + LCD_SPU_SRAM_PARA1); 266 267 if (plane->fb) 268 drm_framebuffer_unreference(plane->fb); 269 270 spin_lock_irq(&dplane->lock); 271 fb = dplane->old_fb; 272 dplane->old_fb = NULL; 273 spin_unlock_irq(&dplane->lock); 274 if (fb) 275 drm_framebuffer_unreference(fb); 276 277 return 0; 278 } 279 280 static void armada_plane_destroy(struct drm_plane *plane) 281 { 282 kfree(plane); 283 } 284 285 static int armada_plane_set_property(struct drm_plane *plane, 286 struct drm_property *property, uint64_t val) 287 { 288 struct armada_private *priv = plane->dev->dev_private; 289 struct armada_plane *dplane = drm_to_armada_plane(plane); 290 bool update_attr = false; 291 292 if (property == priv->colorkey_prop) { 293 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) 294 dplane->prop.colorkey_yr = CCC(K2R(val)); 295 dplane->prop.colorkey_ug = CCC(K2G(val)); 296 dplane->prop.colorkey_vb = CCC(K2B(val)); 297 #undef CCC 298 update_attr = true; 299 } else if (property == priv->colorkey_min_prop) { 300 dplane->prop.colorkey_yr &= ~0x00ff0000; 301 dplane->prop.colorkey_yr |= K2R(val) << 16; 302 dplane->prop.colorkey_ug &= ~0x00ff0000; 303 dplane->prop.colorkey_ug |= K2G(val) << 16; 304 dplane->prop.colorkey_vb &= ~0x00ff0000; 305 dplane->prop.colorkey_vb |= K2B(val) << 16; 306 update_attr = true; 307 } else if (property == priv->colorkey_max_prop) { 308 dplane->prop.colorkey_yr &= ~0xff000000; 309 dplane->prop.colorkey_yr |= K2R(val) << 24; 310 dplane->prop.colorkey_ug &= ~0xff000000; 311 dplane->prop.colorkey_ug |= K2G(val) << 24; 312 dplane->prop.colorkey_vb &= ~0xff000000; 313 dplane->prop.colorkey_vb |= K2B(val) << 24; 314 update_attr = true; 315 } else if (property == priv->colorkey_val_prop) { 316 dplane->prop.colorkey_yr &= ~0x0000ff00; 317 dplane->prop.colorkey_yr |= K2R(val) << 8; 318 dplane->prop.colorkey_ug &= ~0x0000ff00; 319 dplane->prop.colorkey_ug |= K2G(val) << 8; 320 dplane->prop.colorkey_vb &= ~0x0000ff00; 321 dplane->prop.colorkey_vb |= K2B(val) << 8; 322 update_attr = true; 323 } else if (property == priv->colorkey_alpha_prop) { 324 dplane->prop.colorkey_yr &= ~0x000000ff; 325 dplane->prop.colorkey_yr |= K2R(val); 326 dplane->prop.colorkey_ug &= ~0x000000ff; 327 dplane->prop.colorkey_ug |= K2G(val); 328 dplane->prop.colorkey_vb &= ~0x000000ff; 329 dplane->prop.colorkey_vb |= K2B(val); 330 update_attr = true; 331 } else if (property == priv->colorkey_mode_prop) { 332 dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK; 333 dplane->prop.colorkey_mode |= CFG_CKMODE(val); 334 update_attr = true; 335 } else if (property == priv->brightness_prop) { 336 dplane->prop.brightness = val - 256; 337 update_attr = true; 338 } else if (property == priv->contrast_prop) { 339 dplane->prop.contrast = val; 340 update_attr = true; 341 } else if (property == priv->saturation_prop) { 342 dplane->prop.saturation = val; 343 update_attr = true; 344 } 345 346 if (update_attr && dplane->base.crtc) 347 armada_ovl_update_attr(&dplane->prop, 348 drm_to_armada_crtc(dplane->base.crtc)); 349 350 return 0; 351 } 352 353 static const struct drm_plane_funcs armada_plane_funcs = { 354 .update_plane = armada_plane_update, 355 .disable_plane = armada_plane_disable, 356 .destroy = armada_plane_destroy, 357 .set_property = armada_plane_set_property, 358 }; 359 360 static const uint32_t armada_formats[] = { 361 DRM_FORMAT_UYVY, 362 DRM_FORMAT_YUYV, 363 DRM_FORMAT_YUV420, 364 DRM_FORMAT_YVU420, 365 DRM_FORMAT_YUV422, 366 DRM_FORMAT_YVU422, 367 DRM_FORMAT_VYUY, 368 DRM_FORMAT_YVYU, 369 DRM_FORMAT_ARGB8888, 370 DRM_FORMAT_ABGR8888, 371 DRM_FORMAT_XRGB8888, 372 DRM_FORMAT_XBGR8888, 373 DRM_FORMAT_RGB888, 374 DRM_FORMAT_BGR888, 375 DRM_FORMAT_ARGB1555, 376 DRM_FORMAT_ABGR1555, 377 DRM_FORMAT_RGB565, 378 DRM_FORMAT_BGR565, 379 }; 380 381 static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = { 382 { CKMODE_DISABLE, "disabled" }, 383 { CKMODE_Y, "Y component" }, 384 { CKMODE_U, "U component" }, 385 { CKMODE_V, "V component" }, 386 { CKMODE_RGB, "RGB" }, 387 { CKMODE_R, "R component" }, 388 { CKMODE_G, "G component" }, 389 { CKMODE_B, "B component" }, 390 }; 391 392 static int armada_overlay_create_properties(struct drm_device *dev) 393 { 394 struct armada_private *priv = dev->dev_private; 395 396 if (priv->colorkey_prop) 397 return 0; 398 399 priv->colorkey_prop = drm_property_create_range(dev, 0, 400 "colorkey", 0, 0xffffff); 401 priv->colorkey_min_prop = drm_property_create_range(dev, 0, 402 "colorkey_min", 0, 0xffffff); 403 priv->colorkey_max_prop = drm_property_create_range(dev, 0, 404 "colorkey_max", 0, 0xffffff); 405 priv->colorkey_val_prop = drm_property_create_range(dev, 0, 406 "colorkey_val", 0, 0xffffff); 407 priv->colorkey_alpha_prop = drm_property_create_range(dev, 0, 408 "colorkey_alpha", 0, 0xffffff); 409 priv->colorkey_mode_prop = drm_property_create_enum(dev, 0, 410 "colorkey_mode", 411 armada_drm_colorkey_enum_list, 412 ARRAY_SIZE(armada_drm_colorkey_enum_list)); 413 priv->brightness_prop = drm_property_create_range(dev, 0, 414 "brightness", 0, 256 + 255); 415 priv->contrast_prop = drm_property_create_range(dev, 0, 416 "contrast", 0, 0x7fff); 417 priv->saturation_prop = drm_property_create_range(dev, 0, 418 "saturation", 0, 0x7fff); 419 420 if (!priv->colorkey_prop) 421 return -ENOMEM; 422 423 return 0; 424 } 425 426 int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) 427 { 428 struct armada_private *priv = dev->dev_private; 429 struct drm_mode_object *mobj; 430 struct armada_plane *dplane; 431 int ret; 432 433 ret = armada_overlay_create_properties(dev); 434 if (ret) 435 return ret; 436 437 dplane = kzalloc(sizeof(*dplane), GFP_KERNEL); 438 if (!dplane) 439 return -ENOMEM; 440 441 spin_lock_init(&dplane->lock); 442 init_waitqueue_head(&dplane->vbl.wait); 443 armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl, 444 dplane); 445 446 drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs, 447 armada_formats, ARRAY_SIZE(armada_formats), false); 448 449 dplane->prop.colorkey_yr = 0xfefefe00; 450 dplane->prop.colorkey_ug = 0x01010100; 451 dplane->prop.colorkey_vb = 0x01010100; 452 dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB); 453 dplane->prop.brightness = 0; 454 dplane->prop.contrast = 0x4000; 455 dplane->prop.saturation = 0x4000; 456 457 mobj = &dplane->base.base; 458 drm_object_attach_property(mobj, priv->colorkey_prop, 459 0x0101fe); 460 drm_object_attach_property(mobj, priv->colorkey_min_prop, 461 0x0101fe); 462 drm_object_attach_property(mobj, priv->colorkey_max_prop, 463 0x0101fe); 464 drm_object_attach_property(mobj, priv->colorkey_val_prop, 465 0x0101fe); 466 drm_object_attach_property(mobj, priv->colorkey_alpha_prop, 467 0x000000); 468 drm_object_attach_property(mobj, priv->colorkey_mode_prop, 469 CKMODE_RGB); 470 drm_object_attach_property(mobj, priv->brightness_prop, 256); 471 drm_object_attach_property(mobj, priv->contrast_prop, 472 dplane->prop.contrast); 473 drm_object_attach_property(mobj, priv->saturation_prop, 474 dplane->prop.saturation); 475 476 return 0; 477 } 478