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