2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
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.
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"
18 struct armada_plane_properties
{
22 #define K2R(val) (((val) >> 0) & 0xff)
23 #define K2G(val) (((val) >> 8) & 0xff)
24 #define K2B(val) (((val) >> 16) & 0xff)
28 uint32_t colorkey_mode
;
32 struct drm_plane base
;
34 struct drm_framebuffer
*old_fb
;
40 struct armada_vbl_event update
;
41 struct armada_regs regs
[13];
42 wait_queue_head_t wait
;
44 struct armada_plane_properties prop
;
46 #define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
50 armada_ovl_update_attr(struct armada_plane_properties
*prop
,
51 struct armada_crtc
*dcrtc
)
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
);
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
);
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
);
69 armada_updatel(ADV_GRACOLORKEY
, 0, dcrtc
->base
+ LCD_SPU_ADV_REG
);
70 spin_unlock_irq(&dcrtc
->irq_lock
);
73 /* === Plane support === */
74 static void armada_plane_vbl(struct armada_crtc
*dcrtc
, void *data
)
76 struct armada_plane
*dplane
= data
;
77 struct drm_framebuffer
*fb
;
79 armada_drm_crtc_update_regs(dcrtc
, dplane
->vbl
.regs
);
81 spin_lock(&dplane
->lock
);
83 dplane
->old_fb
= NULL
;
84 spin_unlock(&dplane
->lock
);
87 armada_drm_queue_unref_work(dcrtc
->crtc
.dev
, fb
);
90 static unsigned armada_limit(int start
, unsigned size
, unsigned max
)
92 int end
= start
+ size
;
97 return (unsigned)end
> max
? max
- start
: end
- start
;
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
)
106 struct armada_plane
*dplane
= drm_to_armada_plane(plane
);
107 struct armada_crtc
*dcrtc
= drm_to_armada_crtc(crtc
);
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
;
118 /* Does the position/size result in nothing to display? */
119 if (crtc_w
== 0 || crtc_h
== 0) {
120 ctrl0
&= ~CFG_DMA_ENA
;
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.
130 dcrtc
->plane
= plane
;
131 armada_ovl_update_attr(&dplane
->prop
, dcrtc
);
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
);
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
);
153 ret
= wait_event_timeout(dplane
->vbl
.wait
,
154 list_empty(&dplane
->vbl
.update
.node
),
159 if (plane
->fb
!= fb
) {
160 struct armada_gem_object
*obj
= drm_fb_obj(fb
);
164 * Take a reference on the new framebuffer - we want to
165 * hold on to it while the hardware is displaying it.
167 drm_framebuffer_reference(fb
);
170 struct drm_framebuffer
*older_fb
;
172 spin_lock_irq(&dplane
->lock
);
173 older_fb
= dplane
->old_fb
;
174 dplane
->old_fb
= plane
->fb
;
175 spin_unlock_irq(&dplane
->lock
);
177 armada_drm_queue_unref_work(dcrtc
->crtc
.dev
,
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] +
187 sv
= obj
->dev_addr
+ fb
->offsets
[2] + src_y
* fb
->pitches
[2] +
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
);
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
);
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
);
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
);
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
);
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
,
239 armada_reg_queue_end(dplane
->vbl
.regs
, idx
);
240 armada_drm_vbl_event_add(dcrtc
, &dplane
->vbl
.update
);
245 static int armada_plane_disable(struct drm_plane
*plane
)
247 struct armada_plane
*dplane
= drm_to_armada_plane(plane
);
248 struct drm_framebuffer
*fb
;
249 struct armada_crtc
*dcrtc
;
251 if (!dplane
->base
.crtc
)
254 dcrtc
= drm_to_armada_crtc(dplane
->base
.crtc
);
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
);
261 spin_unlock_irq(&dcrtc
->irq_lock
);
263 /* Power down the Y/U/V FIFOs */
264 armada_updatel(CFG_PDWN16x66
| CFG_PDWN32x66
, 0,
265 dcrtc
->base
+ LCD_SPU_SRAM_PARA1
);
268 drm_framebuffer_unreference(plane
->fb
);
270 spin_lock_irq(&dplane
->lock
);
272 dplane
->old_fb
= NULL
;
273 spin_unlock_irq(&dplane
->lock
);
275 drm_framebuffer_unreference(fb
);
280 static void armada_plane_destroy(struct drm_plane
*plane
)
285 static int armada_plane_set_property(struct drm_plane
*plane
,
286 struct drm_property
*property
, uint64_t val
)
288 struct armada_private
*priv
= plane
->dev
->dev_private
;
289 struct armada_plane
*dplane
= drm_to_armada_plane(plane
);
290 bool update_attr
= false;
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
));
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;
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;
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;
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
);
331 } else if (property
== priv
->colorkey_mode_prop
) {
332 dplane
->prop
.colorkey_mode
&= ~CFG_CKMODE_MASK
;
333 dplane
->prop
.colorkey_mode
|= CFG_CKMODE(val
);
335 } else if (property
== priv
->brightness_prop
) {
336 dplane
->prop
.brightness
= val
- 256;
338 } else if (property
== priv
->contrast_prop
) {
339 dplane
->prop
.contrast
= val
;
341 } else if (property
== priv
->saturation_prop
) {
342 dplane
->prop
.saturation
= val
;
346 if (update_attr
&& dplane
->base
.crtc
)
347 armada_ovl_update_attr(&dplane
->prop
,
348 drm_to_armada_crtc(dplane
->base
.crtc
));
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
,
360 static const uint32_t armada_formats
[] = {
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" },
392 static int armada_overlay_create_properties(struct drm_device
*dev
)
394 struct armada_private
*priv
= dev
->dev_private
;
396 if (priv
->colorkey_prop
)
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,
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);
420 if (!priv
->colorkey_prop
)
426 int armada_overlay_plane_create(struct drm_device
*dev
, unsigned long crtcs
)
428 struct armada_private
*priv
= dev
->dev_private
;
429 struct drm_mode_object
*mobj
;
430 struct armada_plane
*dplane
;
433 ret
= armada_overlay_create_properties(dev
);
437 dplane
= kzalloc(sizeof(*dplane
), GFP_KERNEL
);
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
,
446 drm_plane_init(dev
, &dplane
->base
, crtcs
, &armada_plane_funcs
,
447 armada_formats
, ARRAY_SIZE(armada_formats
), false);
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;
457 mobj
= &dplane
->base
.base
;
458 drm_object_attach_property(mobj
, priv
->colorkey_prop
,
460 drm_object_attach_property(mobj
, priv
->colorkey_min_prop
,
462 drm_object_attach_property(mobj
, priv
->colorkey_max_prop
,
464 drm_object_attach_property(mobj
, priv
->colorkey_val_prop
,
466 drm_object_attach_property(mobj
, priv
->colorkey_alpha_prop
,
468 drm_object_attach_property(mobj
, priv
->colorkey_mode_prop
,
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
);