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 <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"
20 struct armada_ovl_plane_properties
{
24 #define K2R(val) (((val) >> 0) & 0xff)
25 #define K2G(val) (((val) >> 8) & 0xff)
26 #define K2B(val) (((val) >> 16) & 0xff)
30 uint32_t colorkey_mode
;
33 struct armada_ovl_plane
{
34 struct armada_plane base
;
35 struct armada_ovl_plane_properties prop
;
37 #define drm_to_armada_ovl_plane(p) \
38 container_of(p, struct armada_ovl_plane, base.base)
42 armada_ovl_update_attr(struct armada_ovl_plane_properties
*prop
,
43 struct armada_crtc
*dcrtc
)
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
);
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
);
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
);
61 armada_updatel(ADV_GRACOLORKEY
, 0, dcrtc
->base
+ LCD_SPU_ADV_REG
);
62 spin_unlock_irq(&dcrtc
->irq_lock
);
65 /* === Plane support === */
66 static void armada_ovl_plane_work(struct armada_crtc
*dcrtc
,
67 struct armada_plane_work
*work
)
71 trace_armada_ovl_plane_work(&dcrtc
->crtc
, work
->plane
);
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
);
78 static void armada_ovl_plane_update_state(struct drm_plane_state
*state
,
79 struct armada_regs
*regs
)
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
;
89 ctrl0
= CFG_DMA_FMT(dfb
->fmt
) | CFG_DMA_MOD(dfb
->mod
) | CFG_CBSH_ENA
;
92 if (drm_rect_width(&state
->src
) >> 16 != drm_rect_width(&state
->dst
))
93 ctrl0
|= CFG_DMA_HSMOOTH
;
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.
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
);
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
,
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;
114 dplane
->base
.state
.vsync_update
= fb_changed
;
116 /* FIXME: overlay on an interlaced display */
120 dplane
->base
.state
.src_y
= src_y
= state
->src
.y1
>> 16;
121 dplane
->base
.state
.src_x
= src_x
= state
->src
.x1
>> 16;
123 armada_drm_plane_calc_addrs(addrs
, &dfb
->fb
, src_x
, src_y
);
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
);
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
);
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
);
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
);
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
);
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
,
176 dplane
->base
.state
.vsync_update
= true;
179 dplane
->base
.state
.changed
= idx
!= 0;
181 armada_reg_queue_end(regs
, idx
);
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
)
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
= {
206 .rotation
= DRM_MODE_ROTATE_0
,
208 const struct drm_rect clip
= {
209 .x2
= crtc
->mode
.hdisplay
,
210 .y2
= crtc
->mode
.vdisplay
,
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
);
218 ret
= drm_atomic_helper_check_plane_state(&state
, crtc
->state
, &clip
, 0,
219 INT_MAX
, true, false);
223 work
= &dplane
->base
.works
[dplane
->base
.next_work
];
225 if (plane
->fb
!= fb
) {
227 * Take a reference on the new framebuffer - we want to
228 * hold on to it while the hardware is displaying it.
230 drm_framebuffer_reference(fb
);
232 work
->old_fb
= plane
->fb
;
237 armada_ovl_plane_update_state(&state
, work
->regs
);
239 if (!dplane
->base
.state
.changed
)
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
);
246 /* Just updating the position/size? */
247 if (!dplane
->base
.state
.vsync_update
) {
248 armada_ovl_plane_work(dcrtc
, work
);
253 dcrtc
->plane
= plane
;
254 armada_ovl_update_attr(&dplane
->prop
, dcrtc
);
257 /* Queue it for update on the next interrupt if we are enabled */
258 ret
= armada_drm_plane_work_queue(dcrtc
, work
);
260 DRM_ERROR("failed to queue plane work: %d\n", ret
);
262 dplane
->base
.next_work
= !dplane
->base
.next_work
;
267 static void armada_ovl_plane_destroy(struct drm_plane
*plane
)
269 struct armada_ovl_plane
*dplane
= drm_to_armada_ovl_plane(plane
);
271 drm_plane_cleanup(plane
);
276 static int armada_ovl_plane_set_property(struct drm_plane
*plane
,
277 struct drm_property
*property
, uint64_t val
)
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;
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
));
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;
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;
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;
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
);
322 } else if (property
== priv
->colorkey_mode_prop
) {
323 dplane
->prop
.colorkey_mode
&= ~CFG_CKMODE_MASK
;
324 dplane
->prop
.colorkey_mode
|= CFG_CKMODE(val
);
326 } else if (property
== priv
->brightness_prop
) {
327 dplane
->prop
.brightness
= val
- 256;
329 } else if (property
== priv
->contrast_prop
) {
330 dplane
->prop
.contrast
= val
;
332 } else if (property
== priv
->saturation_prop
) {
333 dplane
->prop
.saturation
= val
;
337 if (update_attr
&& dplane
->base
.base
.crtc
)
338 armada_ovl_update_attr(&dplane
->prop
,
339 drm_to_armada_crtc(dplane
->base
.base
.crtc
));
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
,
351 static const uint32_t armada_ovl_formats
[] = {
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" },
383 static int armada_overlay_create_properties(struct drm_device
*dev
)
385 struct armada_private
*priv
= dev
->dev_private
;
387 if (priv
->colorkey_prop
)
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,
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);
411 if (!priv
->colorkey_prop
)
417 int armada_overlay_plane_create(struct drm_device
*dev
, unsigned long crtcs
)
419 struct armada_private
*priv
= dev
->dev_private
;
420 struct drm_mode_object
*mobj
;
421 struct armada_ovl_plane
*dplane
;
424 ret
= armada_overlay_create_properties(dev
);
428 dplane
= kzalloc(sizeof(*dplane
), GFP_KERNEL
);
432 ret
= armada_drm_plane_init(&dplane
->base
);
438 dplane
->base
.works
[0].fn
= armada_ovl_plane_work
;
439 dplane
->base
.works
[1].fn
= armada_ovl_plane_work
;
441 ret
= drm_universal_plane_init(dev
, &dplane
->base
.base
, crtcs
,
442 &armada_ovl_plane_funcs
,
444 ARRAY_SIZE(armada_ovl_formats
),
446 DRM_PLANE_TYPE_OVERLAY
, NULL
);
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;
460 mobj
= &dplane
->base
.base
.base
;
461 drm_object_attach_property(mobj
, priv
->colorkey_prop
,
463 drm_object_attach_property(mobj
, priv
->colorkey_min_prop
,
465 drm_object_attach_property(mobj
, priv
->colorkey_max_prop
,
467 drm_object_attach_property(mobj
, priv
->colorkey_val_prop
,
469 drm_object_attach_property(mobj
, priv
->colorkey_alpha_prop
,
471 drm_object_attach_property(mobj
, priv
->colorkey_mode_prop
,
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
);