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