1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
8 #include <linux/bitfield.h>
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_device.h>
13 #include <drm/drm_fourcc.h>
14 #include <drm/drm_plane_helper.h>
15 #include <drm/drm_gem_cma_helper.h>
16 #include <drm/drm_fb_cma_helper.h>
17 #include <drm/drm_gem_framebuffer_helper.h>
19 #include "meson_overlay.h"
20 #include "meson_registers.h"
21 #include "meson_viu.h"
22 #include "meson_vpp.h"
25 #define VD_URGENT_CHROMA BIT(28)
26 #define VD_URGENT_LUMA BIT(27)
27 #define VD_HOLD_LINES(lines) FIELD_PREP(GENMASK(24, 19), lines)
28 #define VD_DEMUX_MODE_RGB BIT(16)
29 #define VD_BYTES_PER_PIXEL(val) FIELD_PREP(GENMASK(15, 14), val)
30 #define VD_CHRO_RPT_LASTL_CTRL BIT(6)
31 #define VD_LITTLE_ENDIAN BIT(4)
32 #define VD_SEPARATE_EN BIT(1)
33 #define VD_ENABLE BIT(0)
36 #define CANVAS_ADDR2(addr) FIELD_PREP(GENMASK(23, 16), addr)
37 #define CANVAS_ADDR1(addr) FIELD_PREP(GENMASK(15, 8), addr)
38 #define CANVAS_ADDR0(addr) FIELD_PREP(GENMASK(7, 0), addr)
40 /* VD1_IF0_LUMA_X0 VD1_IF0_CHROMA_X0 */
41 #define VD_X_START(value) FIELD_PREP(GENMASK(14, 0), value)
42 #define VD_X_END(value) FIELD_PREP(GENMASK(30, 16), value)
44 /* VD1_IF0_LUMA_Y0 VD1_IF0_CHROMA_Y0 */
45 #define VD_Y_START(value) FIELD_PREP(GENMASK(12, 0), value)
46 #define VD_Y_END(value) FIELD_PREP(GENMASK(28, 16), value)
48 /* VD1_IF0_GEN_REG2 */
49 #define VD_COLOR_MAP(value) FIELD_PREP(GENMASK(1, 0), value)
51 /* VIU_VD1_FMT_CTRL */
52 #define VD_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value)
53 #define VD_HORZ_FMT_EN BIT(20)
54 #define VD_VERT_RPT_LINE0 BIT(16)
55 #define VD_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value)
56 #define VD_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value)
57 #define VD_VERT_FMT_EN BIT(0)
59 /* VPP_POSTBLEND_VD1_H_START_END */
60 #define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value)
61 #define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), value)
63 /* VPP_POSTBLEND_VD1_V_START_END */
64 #define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value)
65 #define VD_V_START(value) FIELD_PREP(GENMASK(27, 16), value)
67 /* VPP_BLEND_VD2_V_START_END */
68 #define VD2_V_END(value) FIELD_PREP(GENMASK(11, 0), value)
69 #define VD2_V_START(value) FIELD_PREP(GENMASK(27, 16), value)
72 #define VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value)
73 #define VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value)
75 /* VPP_HSC_REGION12_STARTP VPP_HSC_REGION34_STARTP */
76 #define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value)
77 #define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value)
79 struct meson_overlay
{
80 struct drm_plane base
;
81 struct meson_drm
*priv
;
83 #define to_meson_overlay(x) container_of(x, struct meson_overlay, base)
85 #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
87 static int meson_overlay_atomic_check(struct drm_plane
*plane
,
88 struct drm_plane_state
*state
)
90 struct drm_crtc_state
*crtc_state
;
95 crtc_state
= drm_atomic_get_crtc_state(state
->state
, state
->crtc
);
96 if (IS_ERR(crtc_state
))
97 return PTR_ERR(crtc_state
);
99 return drm_atomic_helper_check_plane_state(state
, crtc_state
,
105 /* Takes a fixed 16.16 number and converts it to integer. */
106 static inline int64_t fixed16_to_int(int64_t value
)
111 static const uint8_t skip_tab
[6] = {
112 0x24, 0x04, 0x68, 0x48, 0x28, 0x08,
115 static void meson_overlay_get_vertical_phase(unsigned int ratio_y
, int *phase
,
116 int *repeat
, bool interlace
)
122 if (!interlace
&& ratio_y
> (1 << 18))
123 offset_out
= (1 * ratio_y
) >> 10;
125 while ((offset_in
+ (4 << 8)) <= offset_out
) {
130 *phase
= (offset_out
- offset_in
) >> 2;
135 *phase
= *phase
& 0xff;
140 *repeat
= skip_tab
[repeat_skip
];
143 static void meson_overlay_setup_scaler_params(struct meson_drm
*priv
,
144 struct drm_plane
*plane
,
147 struct drm_crtc_state
*crtc_state
= priv
->crtc
->state
;
148 int video_top
, video_left
, video_width
, video_height
;
149 struct drm_plane_state
*state
= plane
->state
;
150 unsigned int vd_start_lines
, vd_end_lines
;
151 unsigned int hd_start_lines
, hd_end_lines
;
152 unsigned int crtc_height
, crtc_width
;
153 unsigned int vsc_startp
, vsc_endp
;
154 unsigned int hsc_startp
, hsc_endp
;
155 unsigned int crop_top
, crop_left
;
156 int vphase
, vphase_repeat_skip
;
157 unsigned int ratio_x
, ratio_y
;
158 int temp_height
, temp_width
;
159 unsigned int w_in
, h_in
;
160 int temp
, start
, end
;
163 DRM_ERROR("Invalid crtc_state\n");
167 crtc_height
= crtc_state
->mode
.vdisplay
;
168 crtc_width
= crtc_state
->mode
.hdisplay
;
170 w_in
= fixed16_to_int(state
->src_w
);
171 h_in
= fixed16_to_int(state
->src_h
);
172 crop_top
= fixed16_to_int(state
->src_x
);
173 crop_left
= fixed16_to_int(state
->src_x
);
175 video_top
= state
->crtc_y
;
176 video_left
= state
->crtc_x
;
177 video_width
= state
->crtc_w
;
178 video_height
= state
->crtc_h
;
180 DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n",
181 crtc_width
, crtc_height
, interlace_mode
);
182 DRM_DEBUG("w_in %d h_in %d crop_top %d crop_left %d\n",
183 w_in
, h_in
, crop_top
, crop_left
);
184 DRM_DEBUG("video top %d left %d width %d height %d\n",
185 video_top
, video_left
, video_width
, video_height
);
187 ratio_x
= (w_in
<< 18) / video_width
;
188 ratio_y
= (h_in
<< 18) / video_height
;
190 if (ratio_x
* video_width
< (w_in
<< 18))
193 DRM_DEBUG("ratio x 0x%x y 0x%x\n", ratio_x
, ratio_y
);
195 meson_overlay_get_vertical_phase(ratio_y
, &vphase
, &vphase_repeat_skip
,
198 DRM_DEBUG("vphase 0x%x skip %d\n", vphase
, vphase_repeat_skip
);
202 start
= video_top
+ video_height
/ 2 - ((h_in
<< 17) / ratio_y
);
203 end
= (h_in
<< 18) / ratio_y
+ start
- 1;
205 if (video_top
< 0 && start
< 0)
206 vd_start_lines
= (-(start
) * ratio_y
) >> 18;
207 else if (start
< video_top
)
208 vd_start_lines
= ((video_top
- start
) * ratio_y
) >> 18;
213 temp_height
= min_t(unsigned int,
214 video_top
+ video_height
- 1,
217 temp_height
= min_t(unsigned int,
218 video_top
+ video_height
- 1,
219 crtc_height
- 1) - video_top
+ 1;
221 temp
= vd_start_lines
+ (temp_height
* ratio_y
>> 18);
222 vd_end_lines
= (temp
<= (h_in
- 1)) ? temp
: (h_in
- 1);
224 vd_start_lines
+= crop_left
;
225 vd_end_lines
+= crop_left
;
228 * TOFIX: Input frames are handled and scaled like progressive frames,
229 * proper handling of interlaced field input frames need to be figured
230 * out using the proper framebuffer flags set by userspace.
232 if (interlace_mode
) {
237 vsc_startp
= max_t(int, start
,
238 max_t(int, 0, video_top
));
239 vsc_endp
= min_t(int, end
,
240 min_t(int, crtc_height
- 1,
241 video_top
+ video_height
- 1));
243 DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n",
244 vsc_startp
, vsc_endp
, vd_start_lines
, vd_end_lines
);
248 start
= video_left
+ video_width
/ 2 - ((w_in
<< 17) / ratio_x
);
249 end
= (w_in
<< 18) / ratio_x
+ start
- 1;
251 if (video_left
< 0 && start
< 0)
252 hd_start_lines
= (-(start
) * ratio_x
) >> 18;
253 else if (start
< video_left
)
254 hd_start_lines
= ((video_left
- start
) * ratio_x
) >> 18;
259 temp_width
= min_t(unsigned int,
260 video_left
+ video_width
- 1,
263 temp_width
= min_t(unsigned int,
264 video_left
+ video_width
- 1,
265 crtc_width
- 1) - video_left
+ 1;
267 temp
= hd_start_lines
+ (temp_width
* ratio_x
>> 18);
268 hd_end_lines
= (temp
<= (w_in
- 1)) ? temp
: (w_in
- 1);
270 priv
->viu
.vpp_line_in_length
= hd_end_lines
- hd_start_lines
+ 1;
271 hsc_startp
= max_t(int, start
, max_t(int, 0, video_left
));
272 hsc_endp
= min_t(int, end
, min_t(int, crtc_width
- 1,
273 video_left
+ video_width
- 1));
275 hd_start_lines
+= crop_top
;
276 hd_end_lines
+= crop_top
;
278 DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n",
279 hsc_startp
, hsc_endp
, hd_start_lines
, hd_end_lines
);
281 priv
->viu
.vpp_vsc_start_phase_step
= ratio_y
<< 6;
283 priv
->viu
.vpp_vsc_ini_phase
= vphase
<< 8;
284 priv
->viu
.vpp_vsc_phase_ctrl
= (1 << 13) | (4 << 8) |
287 priv
->viu
.vd1_if0_luma_x0
= VD_X_START(hd_start_lines
) |
288 VD_X_END(hd_end_lines
);
289 priv
->viu
.vd1_if0_chroma_x0
= VD_X_START(hd_start_lines
>> 1) |
290 VD_X_END(hd_end_lines
>> 1);
292 priv
->viu
.viu_vd1_fmt_w
=
293 VD_H_WIDTH(hd_end_lines
- hd_start_lines
+ 1) |
294 VD_V_WIDTH(hd_end_lines
/2 - hd_start_lines
/2 + 1);
296 priv
->viu
.vd1_if0_luma_y0
= VD_Y_START(vd_start_lines
) |
297 VD_Y_END(vd_end_lines
);
299 priv
->viu
.vd1_if0_chroma_y0
= VD_Y_START(vd_start_lines
>> 1) |
300 VD_Y_END(vd_end_lines
>> 1);
302 priv
->viu
.vpp_pic_in_height
= h_in
;
304 priv
->viu
.vpp_postblend_vd1_h_start_end
= VD_H_START(hsc_startp
) |
306 priv
->viu
.vpp_blend_vd2_h_start_end
= VD_H_START(hd_start_lines
) |
307 VD_H_END(hd_end_lines
);
308 priv
->viu
.vpp_hsc_region12_startp
= VD_REGION13_END(0) |
309 VD_REGION24_START(hsc_startp
);
310 priv
->viu
.vpp_hsc_region34_startp
=
311 VD_REGION13_END(hsc_startp
) |
312 VD_REGION24_START(hsc_endp
- hsc_startp
);
313 priv
->viu
.vpp_hsc_region4_endp
= hsc_endp
- hsc_startp
;
314 priv
->viu
.vpp_hsc_start_phase_step
= ratio_x
<< 6;
315 priv
->viu
.vpp_hsc_region1_phase_slope
= 0;
316 priv
->viu
.vpp_hsc_region3_phase_slope
= 0;
317 priv
->viu
.vpp_hsc_phase_ctrl
= (1 << 21) | (4 << 16);
319 priv
->viu
.vpp_line_in_length
= hd_end_lines
- hd_start_lines
+ 1;
320 priv
->viu
.vpp_preblend_h_size
= hd_end_lines
- hd_start_lines
+ 1;
322 priv
->viu
.vpp_postblend_vd1_v_start_end
= VD_V_START(vsc_startp
) |
324 priv
->viu
.vpp_blend_vd2_v_start_end
=
325 VD2_V_START((vd_end_lines
+ 1) >> 1) |
326 VD2_V_END(vd_end_lines
);
328 priv
->viu
.vpp_vsc_region12_startp
= 0;
329 priv
->viu
.vpp_vsc_region34_startp
=
330 VD_REGION13_END(vsc_endp
- vsc_startp
) |
331 VD_REGION24_START(vsc_endp
- vsc_startp
);
332 priv
->viu
.vpp_vsc_region4_endp
= vsc_endp
- vsc_startp
;
333 priv
->viu
.vpp_vsc_start_phase_step
= ratio_y
<< 6;
336 static void meson_overlay_atomic_update(struct drm_plane
*plane
,
337 struct drm_plane_state
*old_state
)
339 struct meson_overlay
*meson_overlay
= to_meson_overlay(plane
);
340 struct drm_plane_state
*state
= plane
->state
;
341 struct drm_framebuffer
*fb
= state
->fb
;
342 struct meson_drm
*priv
= meson_overlay
->priv
;
343 struct drm_gem_cma_object
*gem
;
347 DRM_DEBUG_DRIVER("\n");
349 interlace_mode
= state
->crtc
->mode
.flags
& DRM_MODE_FLAG_INTERLACE
;
351 spin_lock_irqsave(&priv
->drm
->event_lock
, flags
);
353 priv
->viu
.vd1_if0_gen_reg
= VD_URGENT_CHROMA
|
356 VD_CHRO_RPT_LASTL_CTRL
|
359 /* Setup scaler params */
360 meson_overlay_setup_scaler_params(priv
, plane
, interlace_mode
);
362 priv
->viu
.vd1_if0_repeat_loop
= 0;
363 priv
->viu
.vd1_if0_luma0_rpt_pat
= interlace_mode
? 8 : 0;
364 priv
->viu
.vd1_if0_chroma0_rpt_pat
= interlace_mode
? 8 : 0;
365 priv
->viu
.vd1_range_map_y
= 0;
366 priv
->viu
.vd1_range_map_cb
= 0;
367 priv
->viu
.vd1_range_map_cr
= 0;
369 /* Default values for RGB888/YUV444 */
370 priv
->viu
.vd1_if0_gen_reg2
= 0;
371 priv
->viu
.viu_vd1_fmt_ctrl
= 0;
373 switch (fb
->format
->format
) {
374 /* TOFIX DRM_FORMAT_RGB888 should be supported */
375 case DRM_FORMAT_YUYV
:
376 priv
->viu
.vd1_if0_gen_reg
|= VD_BYTES_PER_PIXEL(1);
377 priv
->viu
.vd1_if0_canvas0
=
378 CANVAS_ADDR2(priv
->canvas_id_vd1_0
) |
379 CANVAS_ADDR1(priv
->canvas_id_vd1_0
) |
380 CANVAS_ADDR0(priv
->canvas_id_vd1_0
);
381 priv
->viu
.viu_vd1_fmt_ctrl
= VD_HORZ_Y_C_RATIO(1) | /* /2 */
384 VD_VERT_INITIAL_PHASE(12) |
385 VD_VERT_PHASE_STEP(16) | /* /2 */
388 case DRM_FORMAT_NV12
:
389 case DRM_FORMAT_NV21
:
390 priv
->viu
.vd1_if0_gen_reg
|= VD_SEPARATE_EN
;
391 priv
->viu
.vd1_if0_canvas0
=
392 CANVAS_ADDR2(priv
->canvas_id_vd1_1
) |
393 CANVAS_ADDR1(priv
->canvas_id_vd1_1
) |
394 CANVAS_ADDR0(priv
->canvas_id_vd1_0
);
395 if (fb
->format
->format
== DRM_FORMAT_NV12
)
396 priv
->viu
.vd1_if0_gen_reg2
= VD_COLOR_MAP(1);
398 priv
->viu
.vd1_if0_gen_reg2
= VD_COLOR_MAP(2);
399 priv
->viu
.viu_vd1_fmt_ctrl
= VD_HORZ_Y_C_RATIO(1) | /* /2 */
402 VD_VERT_INITIAL_PHASE(12) |
403 VD_VERT_PHASE_STEP(8) | /* /4 */
406 case DRM_FORMAT_YUV444
:
407 case DRM_FORMAT_YUV422
:
408 case DRM_FORMAT_YUV420
:
409 case DRM_FORMAT_YUV411
:
410 case DRM_FORMAT_YUV410
:
411 priv
->viu
.vd1_if0_gen_reg
|= VD_SEPARATE_EN
;
412 priv
->viu
.vd1_if0_canvas0
=
413 CANVAS_ADDR2(priv
->canvas_id_vd1_2
) |
414 CANVAS_ADDR1(priv
->canvas_id_vd1_1
) |
415 CANVAS_ADDR0(priv
->canvas_id_vd1_0
);
416 switch (fb
->format
->format
) {
417 case DRM_FORMAT_YUV422
:
418 priv
->viu
.viu_vd1_fmt_ctrl
=
419 VD_HORZ_Y_C_RATIO(1) | /* /2 */
422 VD_VERT_INITIAL_PHASE(12) |
423 VD_VERT_PHASE_STEP(16) | /* /2 */
426 case DRM_FORMAT_YUV420
:
427 priv
->viu
.viu_vd1_fmt_ctrl
=
428 VD_HORZ_Y_C_RATIO(1) | /* /2 */
431 VD_VERT_INITIAL_PHASE(12) |
432 VD_VERT_PHASE_STEP(8) | /* /4 */
435 case DRM_FORMAT_YUV411
:
436 priv
->viu
.viu_vd1_fmt_ctrl
=
437 VD_HORZ_Y_C_RATIO(2) | /* /4 */
440 VD_VERT_INITIAL_PHASE(12) |
441 VD_VERT_PHASE_STEP(16) | /* /2 */
444 case DRM_FORMAT_YUV410
:
445 priv
->viu
.viu_vd1_fmt_ctrl
=
446 VD_HORZ_Y_C_RATIO(2) | /* /4 */
449 VD_VERT_INITIAL_PHASE(12) |
450 VD_VERT_PHASE_STEP(8) | /* /4 */
457 /* Update Canvas with buffer address */
458 priv
->viu
.vd1_planes
= fb
->format
->num_planes
;
460 switch (priv
->viu
.vd1_planes
) {
462 gem
= drm_fb_cma_get_gem_obj(fb
, 2);
463 priv
->viu
.vd1_addr2
= gem
->paddr
+ fb
->offsets
[2];
464 priv
->viu
.vd1_stride2
= fb
->pitches
[2];
465 priv
->viu
.vd1_height2
=
466 drm_format_info_plane_height(fb
->format
,
468 DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n",
470 priv
->viu
.vd1_stride2
,
471 priv
->viu
.vd1_height2
);
474 gem
= drm_fb_cma_get_gem_obj(fb
, 1);
475 priv
->viu
.vd1_addr1
= gem
->paddr
+ fb
->offsets
[1];
476 priv
->viu
.vd1_stride1
= fb
->pitches
[1];
477 priv
->viu
.vd1_height1
=
478 drm_format_info_plane_height(fb
->format
,
480 DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n",
482 priv
->viu
.vd1_stride1
,
483 priv
->viu
.vd1_height1
);
486 gem
= drm_fb_cma_get_gem_obj(fb
, 0);
487 priv
->viu
.vd1_addr0
= gem
->paddr
+ fb
->offsets
[0];
488 priv
->viu
.vd1_stride0
= fb
->pitches
[0];
489 priv
->viu
.vd1_height0
=
490 drm_format_info_plane_height(fb
->format
,
492 DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n",
494 priv
->viu
.vd1_stride0
,
495 priv
->viu
.vd1_height0
);
498 priv
->viu
.vd1_enabled
= true;
500 spin_unlock_irqrestore(&priv
->drm
->event_lock
, flags
);
502 DRM_DEBUG_DRIVER("\n");
505 static void meson_overlay_atomic_disable(struct drm_plane
*plane
,
506 struct drm_plane_state
*old_state
)
508 struct meson_overlay
*meson_overlay
= to_meson_overlay(plane
);
509 struct meson_drm
*priv
= meson_overlay
->priv
;
511 DRM_DEBUG_DRIVER("\n");
513 priv
->viu
.vd1_enabled
= false;
516 if (meson_vpu_is_compatible(priv
, VPU_COMPATIBLE_G12A
)) {
517 writel_relaxed(0, priv
->io_base
+ _REG(VD1_BLEND_SRC_CTRL
));
518 writel_relaxed(0, priv
->io_base
+ _REG(VD2_BLEND_SRC_CTRL
));
519 writel_relaxed(0, priv
->io_base
+ _REG(VD1_IF0_GEN_REG
+ 0x17b0));
520 writel_relaxed(0, priv
->io_base
+ _REG(VD2_IF0_GEN_REG
+ 0x17b0));
522 writel_bits_relaxed(VPP_VD1_POSTBLEND
| VPP_VD1_PREBLEND
, 0,
523 priv
->io_base
+ _REG(VPP_MISC
));
527 static const struct drm_plane_helper_funcs meson_overlay_helper_funcs
= {
528 .atomic_check
= meson_overlay_atomic_check
,
529 .atomic_disable
= meson_overlay_atomic_disable
,
530 .atomic_update
= meson_overlay_atomic_update
,
531 .prepare_fb
= drm_gem_fb_prepare_fb
,
534 static const struct drm_plane_funcs meson_overlay_funcs
= {
535 .update_plane
= drm_atomic_helper_update_plane
,
536 .disable_plane
= drm_atomic_helper_disable_plane
,
537 .destroy
= drm_plane_cleanup
,
538 .reset
= drm_atomic_helper_plane_reset
,
539 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
540 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
543 static const uint32_t supported_drm_formats
[] = {
554 int meson_overlay_create(struct meson_drm
*priv
)
556 struct meson_overlay
*meson_overlay
;
557 struct drm_plane
*plane
;
559 DRM_DEBUG_DRIVER("\n");
561 meson_overlay
= devm_kzalloc(priv
->drm
->dev
, sizeof(*meson_overlay
),
566 meson_overlay
->priv
= priv
;
567 plane
= &meson_overlay
->base
;
569 drm_universal_plane_init(priv
->drm
, plane
, 0xFF,
570 &meson_overlay_funcs
,
571 supported_drm_formats
,
572 ARRAY_SIZE(supported_drm_formats
),
574 DRM_PLANE_TYPE_OVERLAY
, "meson_overlay_plane");
576 drm_plane_helper_add(plane
, &meson_overlay_helper_funcs
);
578 /* For now, VD Overlay plane is always on the back */
579 drm_plane_create_zpos_immutable_property(plane
, 0);
581 priv
->overlay_plane
= plane
;
583 DRM_DEBUG_DRIVER("\n");