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 const struct drm_rect clip = { 209 .x2 = crtc->mode.hdisplay, 210 .y2 = crtc->mode.vdisplay, 211 }; 212 int ret; 213 214 trace_armada_ovl_plane_update(plane, crtc, fb, 215 crtc_x, crtc_y, crtc_w, crtc_h, 216 src_x, src_y, src_w, src_h); 217 218 ret = drm_atomic_helper_check_plane_state(&state, crtc->state, &clip, 0, 219 INT_MAX, true, false); 220 if (ret) 221 return ret; 222 223 work = &dplane->base.works[dplane->base.next_work]; 224 225 if (plane->fb != fb) { 226 /* 227 * Take a reference on the new framebuffer - we want to 228 * hold on to it while the hardware is displaying it. 229 */ 230 drm_framebuffer_reference(fb); 231 232 work->old_fb = plane->fb; 233 } else { 234 work->old_fb = NULL; 235 } 236 237 armada_ovl_plane_update_state(&state, work->regs); 238 239 if (!dplane->base.state.changed) 240 return 0; 241 242 /* Wait for pending work to complete */ 243 if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) 244 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 245 246 /* Just updating the position/size? */ 247 if (!dplane->base.state.vsync_update) { 248 armada_ovl_plane_work(dcrtc, work); 249 return 0; 250 } 251 252 if (!dcrtc->plane) { 253 dcrtc->plane = plane; 254 armada_ovl_update_attr(&dplane->prop, dcrtc); 255 } 256 257 /* Queue it for update on the next interrupt if we are enabled */ 258 ret = armada_drm_plane_work_queue(dcrtc, work); 259 if (ret) 260 DRM_ERROR("failed to queue plane work: %d\n", ret); 261 262 dplane->base.next_work = !dplane->base.next_work; 263 264 return 0; 265 } 266 267 static void armada_ovl_plane_destroy(struct drm_plane *plane) 268 { 269 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 270 271 drm_plane_cleanup(plane); 272 273 kfree(dplane); 274 } 275 276 static int armada_ovl_plane_set_property(struct drm_plane *plane, 277 struct drm_property *property, uint64_t val) 278 { 279 struct armada_private *priv = plane->dev->dev_private; 280 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 281 bool update_attr = false; 282 283 if (property == priv->colorkey_prop) { 284 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) 285 dplane->prop.colorkey_yr = CCC(K2R(val)); 286 dplane->prop.colorkey_ug = CCC(K2G(val)); 287 dplane->prop.colorkey_vb = CCC(K2B(val)); 288 #undef CCC 289 update_attr = true; 290 } else if (property == priv->colorkey_min_prop) { 291 dplane->prop.colorkey_yr &= ~0x00ff0000; 292 dplane->prop.colorkey_yr |= K2R(val) << 16; 293 dplane->prop.colorkey_ug &= ~0x00ff0000; 294 dplane->prop.colorkey_ug |= K2G(val) << 16; 295 dplane->prop.colorkey_vb &= ~0x00ff0000; 296 dplane->prop.colorkey_vb |= K2B(val) << 16; 297 update_attr = true; 298 } else if (property == priv->colorkey_max_prop) { 299 dplane->prop.colorkey_yr &= ~0xff000000; 300 dplane->prop.colorkey_yr |= K2R(val) << 24; 301 dplane->prop.colorkey_ug &= ~0xff000000; 302 dplane->prop.colorkey_ug |= K2G(val) << 24; 303 dplane->prop.colorkey_vb &= ~0xff000000; 304 dplane->prop.colorkey_vb |= K2B(val) << 24; 305 update_attr = true; 306 } else if (property == priv->colorkey_val_prop) { 307 dplane->prop.colorkey_yr &= ~0x0000ff00; 308 dplane->prop.colorkey_yr |= K2R(val) << 8; 309 dplane->prop.colorkey_ug &= ~0x0000ff00; 310 dplane->prop.colorkey_ug |= K2G(val) << 8; 311 dplane->prop.colorkey_vb &= ~0x0000ff00; 312 dplane->prop.colorkey_vb |= K2B(val) << 8; 313 update_attr = true; 314 } else if (property == priv->colorkey_alpha_prop) { 315 dplane->prop.colorkey_yr &= ~0x000000ff; 316 dplane->prop.colorkey_yr |= K2R(val); 317 dplane->prop.colorkey_ug &= ~0x000000ff; 318 dplane->prop.colorkey_ug |= K2G(val); 319 dplane->prop.colorkey_vb &= ~0x000000ff; 320 dplane->prop.colorkey_vb |= K2B(val); 321 update_attr = true; 322 } else if (property == priv->colorkey_mode_prop) { 323 dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK; 324 dplane->prop.colorkey_mode |= CFG_CKMODE(val); 325 update_attr = true; 326 } else if (property == priv->brightness_prop) { 327 dplane->prop.brightness = val - 256; 328 update_attr = true; 329 } else if (property == priv->contrast_prop) { 330 dplane->prop.contrast = val; 331 update_attr = true; 332 } else if (property == priv->saturation_prop) { 333 dplane->prop.saturation = val; 334 update_attr = true; 335 } 336 337 if (update_attr && dplane->base.base.crtc) 338 armada_ovl_update_attr(&dplane->prop, 339 drm_to_armada_crtc(dplane->base.base.crtc)); 340 341 return 0; 342 } 343 344 static const struct drm_plane_funcs armada_ovl_plane_funcs = { 345 .update_plane = armada_ovl_plane_update, 346 .disable_plane = armada_drm_plane_disable, 347 .destroy = armada_ovl_plane_destroy, 348 .set_property = armada_ovl_plane_set_property, 349 }; 350 351 static const uint32_t armada_ovl_formats[] = { 352 DRM_FORMAT_UYVY, 353 DRM_FORMAT_YUYV, 354 DRM_FORMAT_YUV420, 355 DRM_FORMAT_YVU420, 356 DRM_FORMAT_YUV422, 357 DRM_FORMAT_YVU422, 358 DRM_FORMAT_VYUY, 359 DRM_FORMAT_YVYU, 360 DRM_FORMAT_ARGB8888, 361 DRM_FORMAT_ABGR8888, 362 DRM_FORMAT_XRGB8888, 363 DRM_FORMAT_XBGR8888, 364 DRM_FORMAT_RGB888, 365 DRM_FORMAT_BGR888, 366 DRM_FORMAT_ARGB1555, 367 DRM_FORMAT_ABGR1555, 368 DRM_FORMAT_RGB565, 369 DRM_FORMAT_BGR565, 370 }; 371 372 static const struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = { 373 { CKMODE_DISABLE, "disabled" }, 374 { CKMODE_Y, "Y component" }, 375 { CKMODE_U, "U component" }, 376 { CKMODE_V, "V component" }, 377 { CKMODE_RGB, "RGB" }, 378 { CKMODE_R, "R component" }, 379 { CKMODE_G, "G component" }, 380 { CKMODE_B, "B component" }, 381 }; 382 383 static int armada_overlay_create_properties(struct drm_device *dev) 384 { 385 struct armada_private *priv = dev->dev_private; 386 387 if (priv->colorkey_prop) 388 return 0; 389 390 priv->colorkey_prop = drm_property_create_range(dev, 0, 391 "colorkey", 0, 0xffffff); 392 priv->colorkey_min_prop = drm_property_create_range(dev, 0, 393 "colorkey_min", 0, 0xffffff); 394 priv->colorkey_max_prop = drm_property_create_range(dev, 0, 395 "colorkey_max", 0, 0xffffff); 396 priv->colorkey_val_prop = drm_property_create_range(dev, 0, 397 "colorkey_val", 0, 0xffffff); 398 priv->colorkey_alpha_prop = drm_property_create_range(dev, 0, 399 "colorkey_alpha", 0, 0xffffff); 400 priv->colorkey_mode_prop = drm_property_create_enum(dev, 0, 401 "colorkey_mode", 402 armada_drm_colorkey_enum_list, 403 ARRAY_SIZE(armada_drm_colorkey_enum_list)); 404 priv->brightness_prop = drm_property_create_range(dev, 0, 405 "brightness", 0, 256 + 255); 406 priv->contrast_prop = drm_property_create_range(dev, 0, 407 "contrast", 0, 0x7fff); 408 priv->saturation_prop = drm_property_create_range(dev, 0, 409 "saturation", 0, 0x7fff); 410 411 if (!priv->colorkey_prop) 412 return -ENOMEM; 413 414 return 0; 415 } 416 417 int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) 418 { 419 struct armada_private *priv = dev->dev_private; 420 struct drm_mode_object *mobj; 421 struct armada_ovl_plane *dplane; 422 int ret; 423 424 ret = armada_overlay_create_properties(dev); 425 if (ret) 426 return ret; 427 428 dplane = kzalloc(sizeof(*dplane), GFP_KERNEL); 429 if (!dplane) 430 return -ENOMEM; 431 432 ret = armada_drm_plane_init(&dplane->base); 433 if (ret) { 434 kfree(dplane); 435 return ret; 436 } 437 438 dplane->base.works[0].fn = armada_ovl_plane_work; 439 dplane->base.works[1].fn = armada_ovl_plane_work; 440 441 ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs, 442 &armada_ovl_plane_funcs, 443 armada_ovl_formats, 444 ARRAY_SIZE(armada_ovl_formats), 445 NULL, 446 DRM_PLANE_TYPE_OVERLAY, NULL); 447 if (ret) { 448 kfree(dplane); 449 return ret; 450 } 451 452 dplane->prop.colorkey_yr = 0xfefefe00; 453 dplane->prop.colorkey_ug = 0x01010100; 454 dplane->prop.colorkey_vb = 0x01010100; 455 dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB); 456 dplane->prop.brightness = 0; 457 dplane->prop.contrast = 0x4000; 458 dplane->prop.saturation = 0x4000; 459 460 mobj = &dplane->base.base.base; 461 drm_object_attach_property(mobj, priv->colorkey_prop, 462 0x0101fe); 463 drm_object_attach_property(mobj, priv->colorkey_min_prop, 464 0x0101fe); 465 drm_object_attach_property(mobj, priv->colorkey_max_prop, 466 0x0101fe); 467 drm_object_attach_property(mobj, priv->colorkey_val_prop, 468 0x0101fe); 469 drm_object_attach_property(mobj, priv->colorkey_alpha_prop, 470 0x000000); 471 drm_object_attach_property(mobj, priv->colorkey_mode_prop, 472 CKMODE_RGB); 473 drm_object_attach_property(mobj, priv->brightness_prop, 256); 474 drm_object_attach_property(mobj, priv->contrast_prop, 475 dplane->prop.contrast); 476 drm_object_attach_property(mobj, priv->saturation_prop, 477 dplane->prop.saturation); 478 479 return 0; 480 } 481