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;
58 int min_scale = FRAC_16_16(1, 8);
59 int max_scale = FRAC_16_16(8, 1);
64 crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
66 if (WARN_ON(!crtc_state))
69 /* nothing to check when disabling or disabled */
70 if (!crtc_state->enable)
73 /* plane must be enabled */
74 if (!plane_state->crtc)
77 return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
82 static int zx_vl_get_fmt(uint32_t format)
87 case DRM_FORMAT_YUV420:
88 return VL_YUV420_PLANAR | VL_FMT_YUV420;
90 return VL_YUV422_YUYV | VL_FMT_YUV422;
92 return VL_YUV422_YVYU | VL_FMT_YUV422;
94 return VL_YUV422_UYVY | VL_FMT_YUV422;
96 return VL_YUV422_VYUY | VL_FMT_YUV422;
97 case DRM_FORMAT_YUV444:
98 return VL_FMT_YUV444_8BIT;
100 WARN_ONCE(1, "invalid pixel format %d\n", format);
105 static inline void zx_vl_set_update(struct zx_plane *zplane)
107 void __iomem *layer = zplane->layer;
109 zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
112 static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
114 zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
117 static int zx_vl_rsz_get_fmt(uint32_t format)
120 case DRM_FORMAT_NV12:
121 case DRM_FORMAT_YUV420:
122 return RSZ_VL_FMT_YCBCR420;
123 case DRM_FORMAT_YUYV:
124 case DRM_FORMAT_YVYU:
125 case DRM_FORMAT_UYVY:
126 case DRM_FORMAT_VYUY:
127 return RSZ_VL_FMT_YCBCR422;
128 case DRM_FORMAT_YUV444:
129 return RSZ_VL_FMT_YCBCR444;
131 WARN_ONCE(1, "invalid pixel format %d\n", format);
136 static inline u32 rsz_step_value(u32 src, u32 dst)
143 val = RSZ_PARA_STEP((src << 16) / dst);
145 val = RSZ_DATA_STEP(src / dst) |
146 RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
151 static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
152 u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
154 void __iomem *rsz = zplane->rsz;
155 u32 src_chroma_w = src_w;
156 u32 src_chroma_h = src_h;
159 /* Set up source and destination resolution */
160 zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
161 zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
163 /* Configure data format for VL RSZ */
164 fmt = zx_vl_rsz_get_fmt(format);
166 zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
168 /* Calculate Chroma height and width */
169 if (fmt == RSZ_VL_FMT_YCBCR420) {
170 src_chroma_w = src_w >> 1;
171 src_chroma_h = src_h >> 1;
172 } else if (fmt == RSZ_VL_FMT_YCBCR422) {
173 src_chroma_w = src_w >> 1;
176 /* Set up Luma and Chroma step registers */
177 zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
178 zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
179 zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
180 zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
182 zx_vl_rsz_set_update(zplane);
185 static void zx_vl_plane_atomic_update(struct drm_plane *plane,
186 struct drm_plane_state *old_state)
188 struct zx_plane *zplane = to_zx_plane(plane);
189 struct drm_plane_state *state = plane->state;
190 struct drm_framebuffer *fb = state->fb;
191 struct drm_rect *src = &state->src;
192 struct drm_rect *dst = &state->dst;
193 struct drm_gem_cma_object *cma_obj;
194 void __iomem *layer = zplane->layer;
195 void __iomem *hbsc = zplane->hbsc;
196 void __iomem *paddr_reg;
198 u32 src_x, src_y, src_w, src_h;
199 u32 dst_x, dst_y, dst_w, dst_h;
208 format = fb->format->format;
210 src_x = src->x1 >> 16;
211 src_y = src->y1 >> 16;
212 src_w = drm_rect_width(src) >> 16;
213 src_h = drm_rect_height(src) >> 16;
217 dst_w = drm_rect_width(dst);
218 dst_h = drm_rect_height(dst);
220 /* Set up data address registers for Y, Cb and Cr planes */
221 num_planes = drm_format_num_planes(format);
222 paddr_reg = layer + VL_Y;
223 for (i = 0; i < num_planes; i++) {
224 cma_obj = drm_fb_cma_get_gem_obj(fb, i);
225 paddr = cma_obj->paddr + fb->offsets[i];
226 paddr += src_y * fb->pitches[i];
227 paddr += src_x * drm_format_plane_cpp(format, i);
228 zx_writel(paddr_reg, paddr);
232 /* Set up source height/width register */
233 zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
235 /* Set up start position register */
236 zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
238 /* Set up end position register */
239 zx_writel(layer + VL_POS_END,
240 GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
242 /* Strides of Cb and Cr planes should be identical */
243 zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
244 CHROMA_STRIDE(fb->pitches[1]));
246 /* Set up video layer data format */
247 fmt = zx_vl_get_fmt(format);
249 zx_writel(layer + VL_CTRL1, fmt);
251 /* Always use scaler since it exists (set for not bypass) */
252 zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
253 VL_SCALER_BYPASS_MODE);
255 zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
257 /* Enable HBSC block */
258 zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
260 zx_vou_layer_enable(plane);
262 zx_vl_set_update(zplane);
265 static void zx_plane_atomic_disable(struct drm_plane *plane,
266 struct drm_plane_state *old_state)
268 struct zx_plane *zplane = to_zx_plane(plane);
269 void __iomem *hbsc = zplane->hbsc;
271 zx_vou_layer_disable(plane, old_state);
273 /* Disable HBSC block */
274 zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
277 static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
278 .atomic_check = zx_vl_plane_atomic_check,
279 .atomic_update = zx_vl_plane_atomic_update,
280 .atomic_disable = zx_plane_atomic_disable,
283 static int zx_gl_plane_atomic_check(struct drm_plane *plane,
284 struct drm_plane_state *plane_state)
286 struct drm_framebuffer *fb = plane_state->fb;
287 struct drm_crtc *crtc = plane_state->crtc;
288 struct drm_crtc_state *crtc_state;
293 crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
295 if (WARN_ON(!crtc_state))
298 /* nothing to check when disabling or disabled */
299 if (!crtc_state->enable)
302 /* plane must be enabled */
303 if (!plane_state->crtc)
306 return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
307 DRM_PLANE_HELPER_NO_SCALING,
308 DRM_PLANE_HELPER_NO_SCALING,
312 static int zx_gl_get_fmt(uint32_t format)
315 case DRM_FORMAT_ARGB8888:
316 case DRM_FORMAT_XRGB8888:
317 return GL_FMT_ARGB8888;
318 case DRM_FORMAT_RGB888:
319 return GL_FMT_RGB888;
320 case DRM_FORMAT_RGB565:
321 return GL_FMT_RGB565;
322 case DRM_FORMAT_ARGB1555:
323 return GL_FMT_ARGB1555;
324 case DRM_FORMAT_ARGB4444:
325 return GL_FMT_ARGB4444;
327 WARN_ONCE(1, "invalid pixel format %d\n", format);
332 static inline void zx_gl_set_update(struct zx_plane *zplane)
334 void __iomem *layer = zplane->layer;
336 zx_writel_mask(layer + GL_CTRL0, GL_UPDATE, GL_UPDATE);
339 static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
341 zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
344 static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
345 u32 dst_w, u32 dst_h)
347 void __iomem *rsz = zplane->rsz;
349 zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
350 zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
352 zx_gl_rsz_set_update(zplane);
355 static void zx_gl_plane_atomic_update(struct drm_plane *plane,
356 struct drm_plane_state *old_state)
358 struct zx_plane *zplane = to_zx_plane(plane);
359 struct drm_framebuffer *fb = plane->state->fb;
360 struct drm_gem_cma_object *cma_obj;
361 void __iomem *layer = zplane->layer;
362 void __iomem *csc = zplane->csc;
363 void __iomem *hbsc = zplane->hbsc;
364 u32 src_x, src_y, src_w, src_h;
365 u32 dst_x, dst_y, dst_w, dst_h;
375 format = fb->format->format;
376 stride = fb->pitches[0];
378 src_x = plane->state->src_x >> 16;
379 src_y = plane->state->src_y >> 16;
380 src_w = plane->state->src_w >> 16;
381 src_h = plane->state->src_h >> 16;
383 dst_x = plane->state->crtc_x;
384 dst_y = plane->state->crtc_y;
385 dst_w = plane->state->crtc_w;
386 dst_h = plane->state->crtc_h;
388 bpp = fb->format->cpp[0];
390 cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
391 paddr = cma_obj->paddr + fb->offsets[0];
392 paddr += src_y * stride + src_x * bpp / 8;
393 zx_writel(layer + GL_ADDR, paddr);
395 /* Set up source height/width register */
396 zx_writel(layer + GL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
398 /* Set up start position register */
399 zx_writel(layer + GL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
401 /* Set up end position register */
402 zx_writel(layer + GL_POS_END,
403 GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
405 /* Set up stride register */
406 zx_writel(layer + GL_STRIDE, stride & 0xffff);
408 /* Set up graphic layer data format */
409 fmt = zx_gl_get_fmt(format);
411 zx_writel_mask(layer + GL_CTRL1, GL_DATA_FMT_MASK,
412 fmt << GL_DATA_FMT_SHIFT);
414 /* Initialize global alpha with a sane value */
415 zx_writel_mask(layer + GL_CTRL2, GL_GLOBAL_ALPHA_MASK,
416 0xff << GL_GLOBAL_ALPHA_SHIFT);
418 /* Setup CSC for the GL */
420 zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
421 CSC_BT709_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT);
423 zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
424 CSC_BT601_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT);
425 zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, CSC_WORK_ENABLE);
427 /* Always use scaler since it exists (set for not bypass) */
428 zx_writel_mask(layer + GL_CTRL3, GL_SCALER_BYPASS_MODE,
429 GL_SCALER_BYPASS_MODE);
431 zx_gl_rsz_setup(zplane, src_w, src_h, dst_w, dst_h);
433 /* Enable HBSC block */
434 zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
436 zx_vou_layer_enable(plane);
438 zx_gl_set_update(zplane);
441 static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
442 .atomic_check = zx_gl_plane_atomic_check,
443 .atomic_update = zx_gl_plane_atomic_update,
444 .atomic_disable = zx_plane_atomic_disable,
447 static void zx_plane_destroy(struct drm_plane *plane)
449 drm_plane_cleanup(plane);
452 static const struct drm_plane_funcs zx_plane_funcs = {
453 .update_plane = drm_atomic_helper_update_plane,
454 .disable_plane = drm_atomic_helper_disable_plane,
455 .destroy = zx_plane_destroy,
456 .reset = drm_atomic_helper_plane_reset,
457 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
458 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
461 void zx_plane_set_update(struct drm_plane *plane)
463 struct zx_plane *zplane = to_zx_plane(plane);
465 /* Do nothing if the plane is not enabled */
466 if (!plane->state->crtc)
469 switch (plane->type) {
470 case DRM_PLANE_TYPE_PRIMARY:
471 zx_gl_rsz_set_update(zplane);
472 zx_gl_set_update(zplane);
474 case DRM_PLANE_TYPE_OVERLAY:
475 zx_vl_rsz_set_update(zplane);
476 zx_vl_set_update(zplane);
479 WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
483 static void zx_plane_hbsc_init(struct zx_plane *zplane)
485 void __iomem *hbsc = zplane->hbsc;
488 * Initialize HBSC block with a sane configuration per recommedation
491 zx_writel(hbsc + HBSC_SATURATION, 0x200);
492 zx_writel(hbsc + HBSC_HUE, 0x0);
493 zx_writel(hbsc + HBSC_BRIGHT, 0x0);
494 zx_writel(hbsc + HBSC_CONTRAST, 0x200);
496 zx_writel(hbsc + HBSC_THRESHOLD_COL1, (0x3ac << 16) | 0x40);
497 zx_writel(hbsc + HBSC_THRESHOLD_COL2, (0x3c0 << 16) | 0x40);
498 zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40);
501 int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
502 enum drm_plane_type type)
504 const struct drm_plane_helper_funcs *helper;
505 struct drm_plane *plane = &zplane->plane;
506 struct device *dev = zplane->dev;
507 const uint32_t *formats;
508 unsigned int format_count;
511 zx_plane_hbsc_init(zplane);
514 case DRM_PLANE_TYPE_PRIMARY:
515 helper = &zx_gl_plane_helper_funcs;
516 formats = gl_formats;
517 format_count = ARRAY_SIZE(gl_formats);
519 case DRM_PLANE_TYPE_OVERLAY:
520 helper = &zx_vl_plane_helper_funcs;
521 formats = vl_formats;
522 format_count = ARRAY_SIZE(vl_formats);
528 ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
529 &zx_plane_funcs, formats, format_count,
532 DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret);
536 drm_plane_helper_add(plane, helper);