1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2016 Linaro Ltd.
4 * Copyright 2016 ZTE Corporation.
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_fb_cma_helper.h>
10 #include <drm/drm_fourcc.h>
11 #include <drm/drm_gem_cma_helper.h>
12 #include <drm/drm_modeset_helper_vtables.h>
13 #include <drm/drm_plane_helper.h>
15 #include "zx_common_regs.h"
16 #include "zx_drm_drv.h"
18 #include "zx_plane_regs.h"
21 static const uint32_t gl_formats
[] = {
30 static const uint32_t vl_formats
[] = {
31 DRM_FORMAT_NV12
, /* Semi-planar YUV420 */
32 DRM_FORMAT_YUV420
, /* Planar YUV420 */
33 DRM_FORMAT_YUYV
, /* Packed YUV422 */
37 DRM_FORMAT_YUV444
, /* YUV444 8bit */
39 * TODO: add formats below that HW supports:
46 #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
48 static int zx_vl_plane_atomic_check(struct drm_plane
*plane
,
49 struct drm_plane_state
*plane_state
)
51 struct drm_framebuffer
*fb
= plane_state
->fb
;
52 struct drm_crtc
*crtc
= plane_state
->crtc
;
53 struct drm_crtc_state
*crtc_state
;
54 int min_scale
= FRAC_16_16(1, 8);
55 int max_scale
= FRAC_16_16(8, 1);
57 if (!crtc
|| WARN_ON(!fb
))
60 crtc_state
= drm_atomic_get_existing_crtc_state(plane_state
->state
,
62 if (WARN_ON(!crtc_state
))
65 /* nothing to check when disabling or disabled */
66 if (!crtc_state
->enable
)
69 /* plane must be enabled */
70 if (!plane_state
->crtc
)
73 return drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
78 static int zx_vl_get_fmt(uint32_t format
)
83 case DRM_FORMAT_YUV420
:
84 return VL_YUV420_PLANAR
| VL_FMT_YUV420
;
86 return VL_YUV422_YUYV
| VL_FMT_YUV422
;
88 return VL_YUV422_YVYU
| VL_FMT_YUV422
;
90 return VL_YUV422_UYVY
| VL_FMT_YUV422
;
92 return VL_YUV422_VYUY
| VL_FMT_YUV422
;
93 case DRM_FORMAT_YUV444
:
94 return VL_FMT_YUV444_8BIT
;
96 WARN_ONCE(1, "invalid pixel format %d\n", format
);
101 static inline void zx_vl_set_update(struct zx_plane
*zplane
)
103 void __iomem
*layer
= zplane
->layer
;
105 zx_writel_mask(layer
+ VL_CTRL0
, VL_UPDATE
, VL_UPDATE
);
108 static inline void zx_vl_rsz_set_update(struct zx_plane
*zplane
)
110 zx_writel(zplane
->rsz
+ RSZ_VL_ENABLE_CFG
, 1);
113 static int zx_vl_rsz_get_fmt(uint32_t format
)
116 case DRM_FORMAT_NV12
:
117 case DRM_FORMAT_YUV420
:
118 return RSZ_VL_FMT_YCBCR420
;
119 case DRM_FORMAT_YUYV
:
120 case DRM_FORMAT_YVYU
:
121 case DRM_FORMAT_UYVY
:
122 case DRM_FORMAT_VYUY
:
123 return RSZ_VL_FMT_YCBCR422
;
124 case DRM_FORMAT_YUV444
:
125 return RSZ_VL_FMT_YCBCR444
;
127 WARN_ONCE(1, "invalid pixel format %d\n", format
);
132 static inline u32
rsz_step_value(u32 src
, u32 dst
)
139 val
= RSZ_PARA_STEP((src
<< 16) / dst
);
141 val
= RSZ_DATA_STEP(src
/ dst
) |
142 RSZ_PARA_STEP(((src
<< 16) / dst
) & 0xffff);
147 static void zx_vl_rsz_setup(struct zx_plane
*zplane
, uint32_t format
,
148 u32 src_w
, u32 src_h
, u32 dst_w
, u32 dst_h
)
150 void __iomem
*rsz
= zplane
->rsz
;
151 u32 src_chroma_w
= src_w
;
152 u32 src_chroma_h
= src_h
;
155 /* Set up source and destination resolution */
156 zx_writel(rsz
+ RSZ_SRC_CFG
, RSZ_VER(src_h
- 1) | RSZ_HOR(src_w
- 1));
157 zx_writel(rsz
+ RSZ_DEST_CFG
, RSZ_VER(dst_h
- 1) | RSZ_HOR(dst_w
- 1));
159 /* Configure data format for VL RSZ */
160 fmt
= zx_vl_rsz_get_fmt(format
);
162 zx_writel_mask(rsz
+ RSZ_VL_CTRL_CFG
, RSZ_VL_FMT_MASK
, fmt
);
164 /* Calculate Chroma height and width */
165 if (fmt
== RSZ_VL_FMT_YCBCR420
) {
166 src_chroma_w
= src_w
>> 1;
167 src_chroma_h
= src_h
>> 1;
168 } else if (fmt
== RSZ_VL_FMT_YCBCR422
) {
169 src_chroma_w
= src_w
>> 1;
172 /* Set up Luma and Chroma step registers */
173 zx_writel(rsz
+ RSZ_VL_LUMA_HOR
, rsz_step_value(src_w
, dst_w
));
174 zx_writel(rsz
+ RSZ_VL_LUMA_VER
, rsz_step_value(src_h
, dst_h
));
175 zx_writel(rsz
+ RSZ_VL_CHROMA_HOR
, rsz_step_value(src_chroma_w
, dst_w
));
176 zx_writel(rsz
+ RSZ_VL_CHROMA_VER
, rsz_step_value(src_chroma_h
, dst_h
));
178 zx_vl_rsz_set_update(zplane
);
181 static void zx_vl_plane_atomic_update(struct drm_plane
*plane
,
182 struct drm_plane_state
*old_state
)
184 struct zx_plane
*zplane
= to_zx_plane(plane
);
185 struct drm_plane_state
*state
= plane
->state
;
186 struct drm_framebuffer
*fb
= state
->fb
;
187 struct drm_rect
*src
= &state
->src
;
188 struct drm_rect
*dst
= &state
->dst
;
189 struct drm_gem_cma_object
*cma_obj
;
190 void __iomem
*layer
= zplane
->layer
;
191 void __iomem
*hbsc
= zplane
->hbsc
;
192 void __iomem
*paddr_reg
;
194 u32 src_x
, src_y
, src_w
, src_h
;
195 u32 dst_x
, dst_y
, dst_w
, dst_h
;
203 format
= fb
->format
->format
;
205 src_x
= src
->x1
>> 16;
206 src_y
= src
->y1
>> 16;
207 src_w
= drm_rect_width(src
) >> 16;
208 src_h
= drm_rect_height(src
) >> 16;
212 dst_w
= drm_rect_width(dst
);
213 dst_h
= drm_rect_height(dst
);
215 /* Set up data address registers for Y, Cb and Cr planes */
216 paddr_reg
= layer
+ VL_Y
;
217 for (i
= 0; i
< fb
->format
->num_planes
; i
++) {
218 cma_obj
= drm_fb_cma_get_gem_obj(fb
, i
);
219 paddr
= cma_obj
->paddr
+ fb
->offsets
[i
];
220 paddr
+= src_y
* fb
->pitches
[i
];
221 paddr
+= src_x
* fb
->format
->cpp
[i
];
222 zx_writel(paddr_reg
, paddr
);
226 /* Set up source height/width register */
227 zx_writel(layer
+ VL_SRC_SIZE
, GL_SRC_W(src_w
) | GL_SRC_H(src_h
));
229 /* Set up start position register */
230 zx_writel(layer
+ VL_POS_START
, GL_POS_X(dst_x
) | GL_POS_Y(dst_y
));
232 /* Set up end position register */
233 zx_writel(layer
+ VL_POS_END
,
234 GL_POS_X(dst_x
+ dst_w
) | GL_POS_Y(dst_y
+ dst_h
));
236 /* Strides of Cb and Cr planes should be identical */
237 zx_writel(layer
+ VL_STRIDE
, LUMA_STRIDE(fb
->pitches
[0]) |
238 CHROMA_STRIDE(fb
->pitches
[1]));
240 /* Set up video layer data format */
241 fmt
= zx_vl_get_fmt(format
);
243 zx_writel(layer
+ VL_CTRL1
, fmt
);
245 /* Always use scaler since it exists (set for not bypass) */
246 zx_writel_mask(layer
+ VL_CTRL2
, VL_SCALER_BYPASS_MODE
,
247 VL_SCALER_BYPASS_MODE
);
249 zx_vl_rsz_setup(zplane
, format
, src_w
, src_h
, dst_w
, dst_h
);
251 /* Enable HBSC block */
252 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, HBSC_CTRL_EN
);
254 zx_vou_layer_enable(plane
);
256 zx_vl_set_update(zplane
);
259 static void zx_plane_atomic_disable(struct drm_plane
*plane
,
260 struct drm_plane_state
*old_state
)
262 struct zx_plane
*zplane
= to_zx_plane(plane
);
263 void __iomem
*hbsc
= zplane
->hbsc
;
265 zx_vou_layer_disable(plane
, old_state
);
267 /* Disable HBSC block */
268 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, 0);
271 static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs
= {
272 .atomic_check
= zx_vl_plane_atomic_check
,
273 .atomic_update
= zx_vl_plane_atomic_update
,
274 .atomic_disable
= zx_plane_atomic_disable
,
277 static int zx_gl_plane_atomic_check(struct drm_plane
*plane
,
278 struct drm_plane_state
*plane_state
)
280 struct drm_framebuffer
*fb
= plane_state
->fb
;
281 struct drm_crtc
*crtc
= plane_state
->crtc
;
282 struct drm_crtc_state
*crtc_state
;
284 if (!crtc
|| WARN_ON(!fb
))
287 crtc_state
= drm_atomic_get_existing_crtc_state(plane_state
->state
,
289 if (WARN_ON(!crtc_state
))
292 /* nothing to check when disabling or disabled */
293 if (!crtc_state
->enable
)
296 /* plane must be enabled */
297 if (!plane_state
->crtc
)
300 return drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
301 DRM_PLANE_HELPER_NO_SCALING
,
302 DRM_PLANE_HELPER_NO_SCALING
,
306 static int zx_gl_get_fmt(uint32_t format
)
309 case DRM_FORMAT_ARGB8888
:
310 case DRM_FORMAT_XRGB8888
:
311 return GL_FMT_ARGB8888
;
312 case DRM_FORMAT_RGB888
:
313 return GL_FMT_RGB888
;
314 case DRM_FORMAT_RGB565
:
315 return GL_FMT_RGB565
;
316 case DRM_FORMAT_ARGB1555
:
317 return GL_FMT_ARGB1555
;
318 case DRM_FORMAT_ARGB4444
:
319 return GL_FMT_ARGB4444
;
321 WARN_ONCE(1, "invalid pixel format %d\n", format
);
326 static inline void zx_gl_set_update(struct zx_plane
*zplane
)
328 void __iomem
*layer
= zplane
->layer
;
330 zx_writel_mask(layer
+ GL_CTRL0
, GL_UPDATE
, GL_UPDATE
);
333 static inline void zx_gl_rsz_set_update(struct zx_plane
*zplane
)
335 zx_writel(zplane
->rsz
+ RSZ_ENABLE_CFG
, 1);
338 static void zx_gl_rsz_setup(struct zx_plane
*zplane
, u32 src_w
, u32 src_h
,
339 u32 dst_w
, u32 dst_h
)
341 void __iomem
*rsz
= zplane
->rsz
;
343 zx_writel(rsz
+ RSZ_SRC_CFG
, RSZ_VER(src_h
- 1) | RSZ_HOR(src_w
- 1));
344 zx_writel(rsz
+ RSZ_DEST_CFG
, RSZ_VER(dst_h
- 1) | RSZ_HOR(dst_w
- 1));
346 zx_gl_rsz_set_update(zplane
);
349 static void zx_gl_plane_atomic_update(struct drm_plane
*plane
,
350 struct drm_plane_state
*old_state
)
352 struct zx_plane
*zplane
= to_zx_plane(plane
);
353 struct drm_framebuffer
*fb
= plane
->state
->fb
;
354 struct drm_gem_cma_object
*cma_obj
;
355 void __iomem
*layer
= zplane
->layer
;
356 void __iomem
*csc
= zplane
->csc
;
357 void __iomem
*hbsc
= zplane
->hbsc
;
358 u32 src_x
, src_y
, src_w
, src_h
;
359 u32 dst_x
, dst_y
, dst_w
, dst_h
;
369 format
= fb
->format
->format
;
370 stride
= fb
->pitches
[0];
372 src_x
= plane
->state
->src_x
>> 16;
373 src_y
= plane
->state
->src_y
>> 16;
374 src_w
= plane
->state
->src_w
>> 16;
375 src_h
= plane
->state
->src_h
>> 16;
377 dst_x
= plane
->state
->crtc_x
;
378 dst_y
= plane
->state
->crtc_y
;
379 dst_w
= plane
->state
->crtc_w
;
380 dst_h
= plane
->state
->crtc_h
;
382 bpp
= fb
->format
->cpp
[0];
384 cma_obj
= drm_fb_cma_get_gem_obj(fb
, 0);
385 paddr
= cma_obj
->paddr
+ fb
->offsets
[0];
386 paddr
+= src_y
* stride
+ src_x
* bpp
/ 8;
387 zx_writel(layer
+ GL_ADDR
, paddr
);
389 /* Set up source height/width register */
390 zx_writel(layer
+ GL_SRC_SIZE
, GL_SRC_W(src_w
) | GL_SRC_H(src_h
));
392 /* Set up start position register */
393 zx_writel(layer
+ GL_POS_START
, GL_POS_X(dst_x
) | GL_POS_Y(dst_y
));
395 /* Set up end position register */
396 zx_writel(layer
+ GL_POS_END
,
397 GL_POS_X(dst_x
+ dst_w
) | GL_POS_Y(dst_y
+ dst_h
));
399 /* Set up stride register */
400 zx_writel(layer
+ GL_STRIDE
, stride
& 0xffff);
402 /* Set up graphic layer data format */
403 fmt
= zx_gl_get_fmt(format
);
405 zx_writel_mask(layer
+ GL_CTRL1
, GL_DATA_FMT_MASK
,
406 fmt
<< GL_DATA_FMT_SHIFT
);
408 /* Initialize global alpha with a sane value */
409 zx_writel_mask(layer
+ GL_CTRL2
, GL_GLOBAL_ALPHA_MASK
,
410 0xff << GL_GLOBAL_ALPHA_SHIFT
);
412 /* Setup CSC for the GL */
414 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_COV_MODE_MASK
,
415 CSC_BT709_IMAGE_RGB2YCBCR
<< CSC_COV_MODE_SHIFT
);
417 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_COV_MODE_MASK
,
418 CSC_BT601_IMAGE_RGB2YCBCR
<< CSC_COV_MODE_SHIFT
);
419 zx_writel_mask(csc
+ CSC_CTRL0
, CSC_WORK_ENABLE
, CSC_WORK_ENABLE
);
421 /* Always use scaler since it exists (set for not bypass) */
422 zx_writel_mask(layer
+ GL_CTRL3
, GL_SCALER_BYPASS_MODE
,
423 GL_SCALER_BYPASS_MODE
);
425 zx_gl_rsz_setup(zplane
, src_w
, src_h
, dst_w
, dst_h
);
427 /* Enable HBSC block */
428 zx_writel_mask(hbsc
+ HBSC_CTRL0
, HBSC_CTRL_EN
, HBSC_CTRL_EN
);
430 zx_vou_layer_enable(plane
);
432 zx_gl_set_update(zplane
);
435 static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs
= {
436 .atomic_check
= zx_gl_plane_atomic_check
,
437 .atomic_update
= zx_gl_plane_atomic_update
,
438 .atomic_disable
= zx_plane_atomic_disable
,
441 static void zx_plane_destroy(struct drm_plane
*plane
)
443 drm_plane_cleanup(plane
);
446 static const struct drm_plane_funcs zx_plane_funcs
= {
447 .update_plane
= drm_atomic_helper_update_plane
,
448 .disable_plane
= drm_atomic_helper_disable_plane
,
449 .destroy
= zx_plane_destroy
,
450 .reset
= drm_atomic_helper_plane_reset
,
451 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
452 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
455 void zx_plane_set_update(struct drm_plane
*plane
)
457 struct zx_plane
*zplane
= to_zx_plane(plane
);
459 /* Do nothing if the plane is not enabled */
460 if (!plane
->state
->crtc
)
463 switch (plane
->type
) {
464 case DRM_PLANE_TYPE_PRIMARY
:
465 zx_gl_rsz_set_update(zplane
);
466 zx_gl_set_update(zplane
);
468 case DRM_PLANE_TYPE_OVERLAY
:
469 zx_vl_rsz_set_update(zplane
);
470 zx_vl_set_update(zplane
);
473 WARN_ONCE(1, "unsupported plane type %d\n", plane
->type
);
477 static void zx_plane_hbsc_init(struct zx_plane
*zplane
)
479 void __iomem
*hbsc
= zplane
->hbsc
;
482 * Initialize HBSC block with a sane configuration per recommedation
485 zx_writel(hbsc
+ HBSC_SATURATION
, 0x200);
486 zx_writel(hbsc
+ HBSC_HUE
, 0x0);
487 zx_writel(hbsc
+ HBSC_BRIGHT
, 0x0);
488 zx_writel(hbsc
+ HBSC_CONTRAST
, 0x200);
490 zx_writel(hbsc
+ HBSC_THRESHOLD_COL1
, (0x3ac << 16) | 0x40);
491 zx_writel(hbsc
+ HBSC_THRESHOLD_COL2
, (0x3c0 << 16) | 0x40);
492 zx_writel(hbsc
+ HBSC_THRESHOLD_COL3
, (0x3c0 << 16) | 0x40);
495 int zx_plane_init(struct drm_device
*drm
, struct zx_plane
*zplane
,
496 enum drm_plane_type type
)
498 const struct drm_plane_helper_funcs
*helper
;
499 struct drm_plane
*plane
= &zplane
->plane
;
500 struct device
*dev
= zplane
->dev
;
501 const uint32_t *formats
;
502 unsigned int format_count
;
505 zx_plane_hbsc_init(zplane
);
508 case DRM_PLANE_TYPE_PRIMARY
:
509 helper
= &zx_gl_plane_helper_funcs
;
510 formats
= gl_formats
;
511 format_count
= ARRAY_SIZE(gl_formats
);
513 case DRM_PLANE_TYPE_OVERLAY
:
514 helper
= &zx_vl_plane_helper_funcs
;
515 formats
= vl_formats
;
516 format_count
= ARRAY_SIZE(vl_formats
);
522 ret
= drm_universal_plane_init(drm
, plane
, VOU_CRTC_MASK
,
523 &zx_plane_funcs
, formats
, format_count
,
526 DRM_DEV_ERROR(dev
, "failed to init universal plane: %d\n", ret
);
530 drm_plane_helper_add(plane
, helper
);