2 * Copyright 2016 Linaro Ltd.
3 * Copyright 2016 ZTE Corporation.
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.
11 #include <drm/drm_atomic.h>
12 #include <drm/drm_atomic_helper.h>
13 #include <drm/drm_fb_cma_helper.h>
14 #include <drm/drm_gem_cma_helper.h>
15 #include <drm/drm_modeset_helper_vtables.h>
16 #include <drm/drm_plane_helper.h>
19 #include "zx_common_regs.h"
20 #include "zx_drm_drv.h"
22 #include "zx_plane_regs.h"
25 static const uint32_t gl_formats
[] = {
34 static const uint32_t vl_formats
[] = {
35 DRM_FORMAT_NV12
, /* Semi-planar YUV420 */
36 DRM_FORMAT_YUV420
, /* Planar YUV420 */
37 DRM_FORMAT_YUYV
, /* Packed YUV422 */
41 DRM_FORMAT_YUV444
, /* YUV444 8bit */
43 * TODO: add formats below that HW supports:
50 #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
52 static int zx_vl_plane_atomic_check(struct drm_plane
*plane
,
53 struct drm_plane_state
*plane_state
)
55 struct drm_framebuffer
*fb
= plane_state
->fb
;
56 struct drm_crtc
*crtc
= plane_state
->crtc
;
57 struct drm_crtc_state
*crtc_state
;
59 int min_scale
= FRAC_16_16(1, 8);
60 int max_scale
= FRAC_16_16(8, 1);
65 crtc_state
= drm_atomic_get_existing_crtc_state(plane_state
->state
,
67 if (WARN_ON(!crtc_state
))
70 /* nothing to check when disabling or disabled */
71 if (!crtc_state
->enable
)
74 /* plane must be enabled */
75 if (!plane_state
->crtc
)
80 clip
.x2
= crtc_state
->adjusted_mode
.hdisplay
;
81 clip
.y2
= crtc_state
->adjusted_mode
.vdisplay
;
83 return drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
84 &clip
, min_scale
, max_scale
,
88 static int zx_vl_get_fmt(uint32_t format
)
93 case DRM_FORMAT_YUV420
:
94 return VL_YUV420_PLANAR
| VL_FMT_YUV420
;
96 return VL_YUV422_YUYV
| VL_FMT_YUV422
;
98 return VL_YUV422_YVYU
| VL_FMT_YUV422
;
100 return VL_YUV422_UYVY
| VL_FMT_YUV422
;
101 case DRM_FORMAT_VYUY
:
102 return VL_YUV422_VYUY
| VL_FMT_YUV422
;
103 case DRM_FORMAT_YUV444
:
104 return VL_FMT_YUV444_8BIT
;
106 WARN_ONCE(1, "invalid pixel format %d\n", format
);
111 static inline void zx_vl_set_update(struct zx_plane
*zplane
)
113 void __iomem
*layer
= zplane
->layer
;
115 zx_writel_mask(layer
+ VL_CTRL0
, VL_UPDATE
, VL_UPDATE
);
118 static inline void zx_vl_rsz_set_update(struct zx_plane
*zplane
)
120 zx_writel(zplane
->rsz
+ RSZ_VL_ENABLE_CFG
, 1);
123 static int zx_vl_rsz_get_fmt(uint32_t format
)
126 case DRM_FORMAT_NV12
:
127 case DRM_FORMAT_YUV420
:
128 return RSZ_VL_FMT_YCBCR420
;
129 case DRM_FORMAT_YUYV
:
130 case DRM_FORMAT_YVYU
:
131 case DRM_FORMAT_UYVY
:
132 case DRM_FORMAT_VYUY
:
133 return RSZ_VL_FMT_YCBCR422
;
134 case DRM_FORMAT_YUV444
:
135 return RSZ_VL_FMT_YCBCR444
;
137 WARN_ONCE(1, "invalid pixel format %d\n", format
);
142 static inline u32
rsz_step_value(u32 src
, u32 dst
)
149 val
= RSZ_PARA_STEP((src
<< 16) / dst
);
151 val
= RSZ_DATA_STEP(src
/ dst
) |
152 RSZ_PARA_STEP(((src
<< 16) / dst
) & 0xffff);
157 static void zx_vl_rsz_setup(struct zx_plane
*zplane
, uint32_t format
,
158 u32 src_w
, u32 src_h
, u32 dst_w
, u32 dst_h
)
160 void __iomem
*rsz
= zplane
->rsz
;
161 u32 src_chroma_w
= src_w
;
162 u32 src_chroma_h
= src_h
;
165 /* Set up source and destination resolution */
166 zx_writel(rsz
+ RSZ_SRC_CFG
, RSZ_VER(src_h
- 1) | RSZ_HOR(src_w
- 1));
167 zx_writel(rsz
+ RSZ_DEST_CFG
, RSZ_VER(dst_h
- 1) | RSZ_HOR(dst_w
- 1));
169 /* Configure data format for VL RSZ */
170 fmt
= zx_vl_rsz_get_fmt(format
);
172 zx_writel_mask(rsz
+ RSZ_VL_CTRL_CFG
, RSZ_VL_FMT_MASK
, fmt
);
174 /* Calculate Chroma height and width */
175 if (fmt
== RSZ_VL_FMT_YCBCR420
) {
176 src_chroma_w
= src_w
>> 1;
177 src_chroma_h
= src_h
>> 1;
178 } else if (fmt
== RSZ_VL_FMT_YCBCR422
) {
179 src_chroma_w
= src_w
>> 1;
182 /* Set up Luma and Chroma step registers */
183 zx_writel(rsz
+ RSZ_VL_LUMA_HOR
, rsz_step_value(src_w
, dst_w
));
184 zx_writel(rsz
+ RSZ_VL_LUMA_VER
, rsz_step_value(src_h
, dst_h
));
185 zx_writel(rsz
+ RSZ_VL_CHROMA_HOR
, rsz_step_value(src_chroma_w
, dst_w
));
186 zx_writel(rsz
+ RSZ_VL_CHROMA_VER
, rsz_step_value(src_chroma_h
, dst_h
));
188 zx_vl_rsz_set_update(zplane
);
191 static void zx_vl_plane_atomic_update(struct drm_plane
*plane
,
192 struct drm_plane_state
*old_state
)
194 struct zx_plane
*zplane
= to_zx_plane(plane
);
195 struct drm_plane_state
*state
= plane
->state
;
196 struct drm_framebuffer
*fb
= state
->fb
;
197 struct drm_rect
*src
= &state
->src
;
198 struct drm_rect
*dst
= &state
->dst
;
199 struct drm_gem_cma_object
*cma_obj
;
200 void __iomem
*layer
= zplane
->layer
;
201 void __iomem
*hbsc
= zplane
->hbsc
;
202 void __iomem
*paddr_reg
;
204 u32 src_x
, src_y
, src_w
, src_h
;
205 u32 dst_x
, dst_y
, dst_w
, dst_h
;
214 format
= fb
->format
->format
;
216 src_x
= src
->x1
>> 16;
217 src_y
= src
->y1
>> 16;
218 src_w
= drm_rect_width(src
) >> 16;
219 src_h
= drm_rect_height(src
) >> 16;
223 dst_w
= drm_rect_width(dst
);
224 dst_h
= drm_rect_height(dst
);
226 /* Set up data address registers for Y, Cb and Cr planes */
227 num_planes
= drm_format_num_planes(format
);
228 paddr_reg
= layer
+ VL_Y
;
229 for (i
= 0; i
< num_planes
; i
++) {
230 cma_obj
= drm_fb_cma_get_gem_obj(fb
, i
);
231 paddr
= cma_obj
->paddr
+ fb
->offsets
[i
];
232 paddr
+= src_y
* fb
->pitches
[i
];
233 paddr
+= src_x
* drm_format_plane_cpp(format
, i
);
234 zx_writel(paddr_reg
, paddr
);
238 /* Set up source height/width register */
239 zx_writel(layer
+ VL_SRC_SIZE
, GL_SRC_W(src_w
) | GL_SRC_H(src_h
));
241 /* Set up start position register */
242 zx_writel(layer
+ VL_POS_START
, GL_POS_X(dst_x
) | GL_POS_Y(dst_y
));
244 /* Set up end position register */
245 zx_writel(layer
+ VL_POS_END
,
246 GL_POS_X(dst_x
+ dst_w
) | GL_POS_Y(dst_y
+ dst_h
));
248 /* Strides of Cb and Cr planes should be identical */
249 zx_writel(layer
+ VL_STRIDE
, LUMA_STRIDE(fb
->pitches
[0]) |
250 CHROMA_STRIDE(fb
->pitches
[1]));
252 /* Set up video layer data format */
253 fmt
= zx_vl_get_fmt(format
);
255 zx_writel(layer
+ VL_CTRL1
, fmt
);
257 /* Always use scaler since it exists (set for not bypass) */
258 zx_writel_mask(layer
+ VL_CTRL2
, VL_SCALER_BYPASS_MODE
,
259 VL_SCALER_BYPASS_MODE
);
261 zx_vl_rsz_setup(zplane
, format
, src_w
, src_h
, dst_w
, dst_h
);
263 /* Enable HBSC block */
264 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, HBSC_CTRL_EN
);
266 zx_vou_layer_enable(plane
);
268 zx_vl_set_update(zplane
);
271 static void zx_plane_atomic_disable(struct drm_plane
*plane
,
272 struct drm_plane_state
*old_state
)
274 struct zx_plane
*zplane
= to_zx_plane(plane
);
275 void __iomem
*hbsc
= zplane
->hbsc
;
277 zx_vou_layer_disable(plane
);
279 /* Disable HBSC block */
280 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, 0);
283 static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs
= {
284 .atomic_check
= zx_vl_plane_atomic_check
,
285 .atomic_update
= zx_vl_plane_atomic_update
,
286 .atomic_disable
= zx_plane_atomic_disable
,
289 static int zx_gl_plane_atomic_check(struct drm_plane
*plane
,
290 struct drm_plane_state
*plane_state
)
292 struct drm_framebuffer
*fb
= plane_state
->fb
;
293 struct drm_crtc
*crtc
= plane_state
->crtc
;
294 struct drm_crtc_state
*crtc_state
;
295 struct drm_rect clip
;
300 crtc_state
= drm_atomic_get_existing_crtc_state(plane_state
->state
,
302 if (WARN_ON(!crtc_state
))
305 /* nothing to check when disabling or disabled */
306 if (!crtc_state
->enable
)
309 /* plane must be enabled */
310 if (!plane_state
->crtc
)
315 clip
.x2
= crtc_state
->adjusted_mode
.hdisplay
;
316 clip
.y2
= crtc_state
->adjusted_mode
.vdisplay
;
318 return drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
320 DRM_PLANE_HELPER_NO_SCALING
,
321 DRM_PLANE_HELPER_NO_SCALING
,
325 static int zx_gl_get_fmt(uint32_t format
)
328 case DRM_FORMAT_ARGB8888
:
329 case DRM_FORMAT_XRGB8888
:
330 return GL_FMT_ARGB8888
;
331 case DRM_FORMAT_RGB888
:
332 return GL_FMT_RGB888
;
333 case DRM_FORMAT_RGB565
:
334 return GL_FMT_RGB565
;
335 case DRM_FORMAT_ARGB1555
:
336 return GL_FMT_ARGB1555
;
337 case DRM_FORMAT_ARGB4444
:
338 return GL_FMT_ARGB4444
;
340 WARN_ONCE(1, "invalid pixel format %d\n", format
);
345 static inline void zx_gl_set_update(struct zx_plane
*zplane
)
347 void __iomem
*layer
= zplane
->layer
;
349 zx_writel_mask(layer
+ GL_CTRL0
, GL_UPDATE
, GL_UPDATE
);
352 static inline void zx_gl_rsz_set_update(struct zx_plane
*zplane
)
354 zx_writel(zplane
->rsz
+ RSZ_ENABLE_CFG
, 1);
357 static void zx_gl_rsz_setup(struct zx_plane
*zplane
, u32 src_w
, u32 src_h
,
358 u32 dst_w
, u32 dst_h
)
360 void __iomem
*rsz
= zplane
->rsz
;
362 zx_writel(rsz
+ RSZ_SRC_CFG
, RSZ_VER(src_h
- 1) | RSZ_HOR(src_w
- 1));
363 zx_writel(rsz
+ RSZ_DEST_CFG
, RSZ_VER(dst_h
- 1) | RSZ_HOR(dst_w
- 1));
365 zx_gl_rsz_set_update(zplane
);
368 static void zx_gl_plane_atomic_update(struct drm_plane
*plane
,
369 struct drm_plane_state
*old_state
)
371 struct zx_plane
*zplane
= to_zx_plane(plane
);
372 struct drm_framebuffer
*fb
= plane
->state
->fb
;
373 struct drm_gem_cma_object
*cma_obj
;
374 void __iomem
*layer
= zplane
->layer
;
375 void __iomem
*csc
= zplane
->csc
;
376 void __iomem
*hbsc
= zplane
->hbsc
;
377 u32 src_x
, src_y
, src_w
, src_h
;
378 u32 dst_x
, dst_y
, dst_w
, dst_h
;
388 format
= fb
->format
->format
;
389 stride
= fb
->pitches
[0];
391 src_x
= plane
->state
->src_x
>> 16;
392 src_y
= plane
->state
->src_y
>> 16;
393 src_w
= plane
->state
->src_w
>> 16;
394 src_h
= plane
->state
->src_h
>> 16;
396 dst_x
= plane
->state
->crtc_x
;
397 dst_y
= plane
->state
->crtc_y
;
398 dst_w
= plane
->state
->crtc_w
;
399 dst_h
= plane
->state
->crtc_h
;
401 bpp
= fb
->format
->cpp
[0];
403 cma_obj
= drm_fb_cma_get_gem_obj(fb
, 0);
404 paddr
= cma_obj
->paddr
+ fb
->offsets
[0];
405 paddr
+= src_y
* stride
+ src_x
* bpp
/ 8;
406 zx_writel(layer
+ GL_ADDR
, paddr
);
408 /* Set up source height/width register */
409 zx_writel(layer
+ GL_SRC_SIZE
, GL_SRC_W(src_w
) | GL_SRC_H(src_h
));
411 /* Set up start position register */
412 zx_writel(layer
+ GL_POS_START
, GL_POS_X(dst_x
) | GL_POS_Y(dst_y
));
414 /* Set up end position register */
415 zx_writel(layer
+ GL_POS_END
,
416 GL_POS_X(dst_x
+ dst_w
) | GL_POS_Y(dst_y
+ dst_h
));
418 /* Set up stride register */
419 zx_writel(layer
+ GL_STRIDE
, stride
& 0xffff);
421 /* Set up graphic layer data format */
422 fmt
= zx_gl_get_fmt(format
);
424 zx_writel_mask(layer
+ GL_CTRL1
, GL_DATA_FMT_MASK
,
425 fmt
<< GL_DATA_FMT_SHIFT
);
427 /* Initialize global alpha with a sane value */
428 zx_writel_mask(layer
+ GL_CTRL2
, GL_GLOBAL_ALPHA_MASK
,
429 0xff << GL_GLOBAL_ALPHA_SHIFT
);
431 /* Setup CSC for the GL */
433 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_COV_MODE_MASK
,
434 CSC_BT709_IMAGE_RGB2YCBCR
<< CSC_COV_MODE_SHIFT
);
436 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_COV_MODE_MASK
,
437 CSC_BT601_IMAGE_RGB2YCBCR
<< CSC_COV_MODE_SHIFT
);
438 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_WORK_ENABLE
, CSC_WORK_ENABLE
);
440 /* Always use scaler since it exists (set for not bypass) */
441 zx_writel_mask(layer
+ GL_CTRL3
, GL_SCALER_BYPASS_MODE
,
442 GL_SCALER_BYPASS_MODE
);
444 zx_gl_rsz_setup(zplane
, src_w
, src_h
, dst_w
, dst_h
);
446 /* Enable HBSC block */
447 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, HBSC_CTRL_EN
);
449 zx_vou_layer_enable(plane
);
451 zx_gl_set_update(zplane
);
454 static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs
= {
455 .atomic_check
= zx_gl_plane_atomic_check
,
456 .atomic_update
= zx_gl_plane_atomic_update
,
457 .atomic_disable
= zx_plane_atomic_disable
,
460 static void zx_plane_destroy(struct drm_plane
*plane
)
462 drm_plane_helper_disable(plane
);
463 drm_plane_cleanup(plane
);
466 static const struct drm_plane_funcs zx_plane_funcs
= {
467 .update_plane
= drm_atomic_helper_update_plane
,
468 .disable_plane
= drm_atomic_helper_disable_plane
,
469 .destroy
= zx_plane_destroy
,
470 .reset
= drm_atomic_helper_plane_reset
,
471 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
472 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
475 void zx_plane_set_update(struct drm_plane
*plane
)
477 struct zx_plane
*zplane
= to_zx_plane(plane
);
479 /* Do nothing if the plane is not enabled */
480 if (!plane
->state
->crtc
)
483 switch (plane
->type
) {
484 case DRM_PLANE_TYPE_PRIMARY
:
485 zx_gl_rsz_set_update(zplane
);
486 zx_gl_set_update(zplane
);
488 case DRM_PLANE_TYPE_OVERLAY
:
489 zx_vl_rsz_set_update(zplane
);
490 zx_vl_set_update(zplane
);
493 WARN_ONCE(1, "unsupported plane type %d\n", plane
->type
);
497 static void zx_plane_hbsc_init(struct zx_plane
*zplane
)
499 void __iomem
*hbsc
= zplane
->hbsc
;
502 * Initialize HBSC block with a sane configuration per recommedation
505 zx_writel(hbsc
+ HBSC_SATURATION
, 0x200);
506 zx_writel(hbsc
+ HBSC_HUE
, 0x0);
507 zx_writel(hbsc
+ HBSC_BRIGHT
, 0x0);
508 zx_writel(hbsc
+ HBSC_CONTRAST
, 0x200);
510 zx_writel(hbsc
+ HBSC_THRESHOLD_COL1
, (0x3ac << 16) | 0x40);
511 zx_writel(hbsc
+ HBSC_THRESHOLD_COL2
, (0x3c0 << 16) | 0x40);
512 zx_writel(hbsc
+ HBSC_THRESHOLD_COL3
, (0x3c0 << 16) | 0x40);
515 int zx_plane_init(struct drm_device
*drm
, struct zx_plane
*zplane
,
516 enum drm_plane_type type
)
518 const struct drm_plane_helper_funcs
*helper
;
519 struct drm_plane
*plane
= &zplane
->plane
;
520 struct device
*dev
= zplane
->dev
;
521 const uint32_t *formats
;
522 unsigned int format_count
;
525 zx_plane_hbsc_init(zplane
);
528 case DRM_PLANE_TYPE_PRIMARY
:
529 helper
= &zx_gl_plane_helper_funcs
;
530 formats
= gl_formats
;
531 format_count
= ARRAY_SIZE(gl_formats
);
533 case DRM_PLANE_TYPE_OVERLAY
:
534 helper
= &zx_vl_plane_helper_funcs
;
535 formats
= vl_formats
;
536 format_count
= ARRAY_SIZE(vl_formats
);
542 ret
= drm_universal_plane_init(drm
, plane
, VOU_CRTC_MASK
,
543 &zx_plane_funcs
, formats
, format_count
,
546 DRM_DEV_ERROR(dev
, "failed to init universal plane: %d\n", ret
);
550 drm_plane_helper_add(plane
, helper
);