xref: /linux/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
1604be855SAndy Yan // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2604be855SAndy Yan /*
3604be855SAndy Yan  * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
4604be855SAndy Yan  * Author: Andy Yan <andy.yan@rock-chips.com>
5604be855SAndy Yan  */
6604be855SAndy Yan #include <linux/bitfield.h>
7604be855SAndy Yan #include <linux/clk.h>
8604be855SAndy Yan #include <linux/component.h>
9604be855SAndy Yan #include <linux/delay.h>
10604be855SAndy Yan #include <linux/iopoll.h>
11604be855SAndy Yan #include <linux/kernel.h>
1272bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
13604be855SAndy Yan #include <linux/mfd/syscon.h>
14604be855SAndy Yan #include <linux/module.h>
15604be855SAndy Yan #include <linux/of.h>
16604be855SAndy Yan #include <linux/of_graph.h>
17604be855SAndy Yan #include <linux/platform_device.h>
18604be855SAndy Yan #include <linux/pm_runtime.h>
19604be855SAndy Yan #include <linux/regmap.h>
20604be855SAndy Yan #include <linux/swab.h>
21604be855SAndy Yan 
22604be855SAndy Yan #include <drm/drm.h>
23604be855SAndy Yan #include <drm/drm_atomic.h>
24604be855SAndy Yan #include <drm/drm_atomic_uapi.h>
2590bb087fSVille Syrjälä #include <drm/drm_blend.h>
26604be855SAndy Yan #include <drm/drm_crtc.h>
2777996455SAndy Yan #include <linux/debugfs.h>
28604be855SAndy Yan #include <drm/drm_debugfs.h>
29604be855SAndy Yan #include <drm/drm_flip_work.h>
30720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
3177996455SAndy Yan #include <drm/drm_gem_framebuffer_helper.h>
32604be855SAndy Yan #include <drm/drm_probe_helper.h>
33604be855SAndy Yan #include <drm/drm_vblank.h>
34604be855SAndy Yan 
35604be855SAndy Yan #include <uapi/linux/videodev2.h>
36604be855SAndy Yan 
37604be855SAndy Yan #include "rockchip_drm_gem.h"
38604be855SAndy Yan #include "rockchip_drm_vop2.h"
39c66c6d7cSMichael Riesch #include "rockchip_rgb.h"
40604be855SAndy Yan 
41604be855SAndy Yan /*
42604be855SAndy Yan  * VOP2 architecture
43604be855SAndy Yan  *
44604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
45604be855SAndy Yan  |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
46604be855SAndy Yan  |  window0 |   |    Layer0   |                                                        |    RGB    |
47604be855SAndy Yan  +----------+   +-------------+              +---------------+    +-------------+      +-----------+
48604be855SAndy Yan  +----------+   +-------------+              |N from 6 layers|    |             |
49604be855SAndy Yan  |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
50604be855SAndy Yan  |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
51604be855SAndy Yan  +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
52604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
53604be855SAndy Yan  |  Esmart  |   | Sel 1 from 6|
54604be855SAndy Yan  |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
55604be855SAndy Yan  +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
56604be855SAndy Yan  +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
57604be855SAndy Yan  |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
58604be855SAndy Yan  |  Window1 |   |   Layer3    |              +---------------+    +-------------+
59604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
60604be855SAndy Yan  +----------+   +-------------+                                                        | 1 from 3  |
61604be855SAndy Yan  |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
62604be855SAndy Yan  |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
63604be855SAndy Yan  +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
64604be855SAndy Yan  +----------+   +-------------+              |               |    |             |      +-----------+
65604be855SAndy Yan  |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
66604be855SAndy Yan  |  Window1 |   |    Layer5   |                                                        |    eDP    |
67604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
68604be855SAndy Yan  *
69604be855SAndy Yan  */
70604be855SAndy Yan 
71604be855SAndy Yan enum vop2_data_format {
72604be855SAndy Yan 	VOP2_FMT_ARGB8888 = 0,
73604be855SAndy Yan 	VOP2_FMT_RGB888,
74604be855SAndy Yan 	VOP2_FMT_RGB565,
75604be855SAndy Yan 	VOP2_FMT_XRGB101010,
76604be855SAndy Yan 	VOP2_FMT_YUV420SP,
77604be855SAndy Yan 	VOP2_FMT_YUV422SP,
78604be855SAndy Yan 	VOP2_FMT_YUV444SP,
79604be855SAndy Yan 	VOP2_FMT_YUYV422 = 8,
80604be855SAndy Yan 	VOP2_FMT_YUYV420,
81604be855SAndy Yan 	VOP2_FMT_VYUY422,
82604be855SAndy Yan 	VOP2_FMT_VYUY420,
83604be855SAndy Yan 	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
84604be855SAndy Yan 	VOP2_FMT_YUV420SP_TILE_16x2,
85604be855SAndy Yan 	VOP2_FMT_YUV422SP_TILE_8x4,
86604be855SAndy Yan 	VOP2_FMT_YUV422SP_TILE_16x2,
87604be855SAndy Yan 	VOP2_FMT_YUV420SP_10,
88604be855SAndy Yan 	VOP2_FMT_YUV422SP_10,
89604be855SAndy Yan 	VOP2_FMT_YUV444SP_10,
90604be855SAndy Yan };
91604be855SAndy Yan 
92604be855SAndy Yan enum vop2_afbc_format {
93604be855SAndy Yan 	VOP2_AFBC_FMT_RGB565,
94604be855SAndy Yan 	VOP2_AFBC_FMT_ARGB2101010 = 2,
95604be855SAndy Yan 	VOP2_AFBC_FMT_YUV420_10BIT,
96604be855SAndy Yan 	VOP2_AFBC_FMT_RGB888,
97604be855SAndy Yan 	VOP2_AFBC_FMT_ARGB8888,
98604be855SAndy Yan 	VOP2_AFBC_FMT_YUV420 = 9,
99604be855SAndy Yan 	VOP2_AFBC_FMT_YUV422 = 0xb,
100604be855SAndy Yan 	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
101604be855SAndy Yan 	VOP2_AFBC_FMT_INVALID = -1,
102604be855SAndy Yan };
103604be855SAndy Yan 
1042c1268e7SCristian Ciocaltea #define VOP2_MAX_DCLK_RATE		600000000
1052c1268e7SCristian Ciocaltea 
10677996455SAndy Yan /*
10777996455SAndy Yan  * bus-format types.
10877996455SAndy Yan  */
10977996455SAndy Yan struct drm_bus_format_enum_list {
11077996455SAndy Yan 	int type;
11177996455SAndy Yan 	const char *name;
11277996455SAndy Yan };
11377996455SAndy Yan 
11477996455SAndy Yan static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = {
11577996455SAndy Yan 	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
11677996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" },
11777996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" },
11877996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" },
11977996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" },
12077996455SAndy Yan 	{ MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" },
12177996455SAndy Yan 	{ MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" },
12277996455SAndy Yan 	{ MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" },
12377996455SAndy Yan 	{ MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" },
12477996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" },
12577996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" },
12677996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" },
12777996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" },
12877996455SAndy Yan 	{ MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" },
12977996455SAndy Yan 	{ MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" },
13077996455SAndy Yan 	{ MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" },
13177996455SAndy Yan 	{ MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1X30" },
13277996455SAndy Yan 	{ MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" },
13377996455SAndy Yan };
13477996455SAndy Yan 
DRM_ENUM_NAME_FN(drm_get_bus_format_name,drm_bus_format_enum_list)13577996455SAndy Yan static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list)
13677996455SAndy Yan 
13781a06f1dSAndy Yan static const struct regmap_config vop2_regmap_config;
13881a06f1dSAndy Yan 
139604be855SAndy Yan static void vop2_lock(struct vop2 *vop2)
140604be855SAndy Yan {
141604be855SAndy Yan 	mutex_lock(&vop2->vop2_lock);
142604be855SAndy Yan }
143604be855SAndy Yan 
vop2_unlock(struct vop2 * vop2)144604be855SAndy Yan static void vop2_unlock(struct vop2 *vop2)
145604be855SAndy Yan {
146604be855SAndy Yan 	mutex_unlock(&vop2->vop2_lock);
147604be855SAndy Yan }
148604be855SAndy Yan 
vop2_win_disable(struct vop2_win * win)149604be855SAndy Yan static void vop2_win_disable(struct vop2_win *win)
150604be855SAndy Yan {
151604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
152604be855SAndy Yan 
153604be855SAndy Yan 	if (vop2_cluster_window(win))
154604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
155604be855SAndy Yan }
156604be855SAndy Yan 
vop2_get_bpp(const struct drm_format_info * format)15745ad07c7SAndy Yan static u32 vop2_get_bpp(const struct drm_format_info *format)
15845ad07c7SAndy Yan {
15945ad07c7SAndy Yan 	switch (format->format) {
16045ad07c7SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
16145ad07c7SAndy Yan 		return 12;
16245ad07c7SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
16345ad07c7SAndy Yan 		return 15;
16445ad07c7SAndy Yan 	case DRM_FORMAT_VUY101010:
16545ad07c7SAndy Yan 		return 30;
16645ad07c7SAndy Yan 	default:
16745ad07c7SAndy Yan 		return drm_format_info_bpp(format, 0);
16845ad07c7SAndy Yan 	}
16945ad07c7SAndy Yan }
17045ad07c7SAndy Yan 
vop2_convert_format(u32 format)171604be855SAndy Yan static enum vop2_data_format vop2_convert_format(u32 format)
172604be855SAndy Yan {
173604be855SAndy Yan 	switch (format) {
174bfd8a5c2SAndy Yan 	case DRM_FORMAT_XRGB2101010:
175bfd8a5c2SAndy Yan 	case DRM_FORMAT_ARGB2101010:
176bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
177bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
178bfd8a5c2SAndy Yan 		return VOP2_FMT_XRGB101010;
179604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
180604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
181604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
182604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
183604be855SAndy Yan 		return VOP2_FMT_ARGB8888;
184604be855SAndy Yan 	case DRM_FORMAT_RGB888:
185604be855SAndy Yan 	case DRM_FORMAT_BGR888:
186604be855SAndy Yan 		return VOP2_FMT_RGB888;
187604be855SAndy Yan 	case DRM_FORMAT_RGB565:
188604be855SAndy Yan 	case DRM_FORMAT_BGR565:
189604be855SAndy Yan 		return VOP2_FMT_RGB565;
190604be855SAndy Yan 	case DRM_FORMAT_NV12:
191bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV21:
192bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
193604be855SAndy Yan 		return VOP2_FMT_YUV420SP;
194bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV15:
195bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
196bfd8a5c2SAndy Yan 		return VOP2_FMT_YUV420SP_10;
197604be855SAndy Yan 	case DRM_FORMAT_NV16:
198bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV61:
199604be855SAndy Yan 		return VOP2_FMT_YUV422SP;
2005fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV20:
201bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
202bfd8a5c2SAndy Yan 		return VOP2_FMT_YUV422SP_10;
203604be855SAndy Yan 	case DRM_FORMAT_NV24:
204bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV42:
205604be855SAndy Yan 		return VOP2_FMT_YUV444SP;
2065fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV30:
2075fc6aa7dSJonas Karlman 		return VOP2_FMT_YUV444SP_10;
208604be855SAndy Yan 	case DRM_FORMAT_YUYV:
209604be855SAndy Yan 	case DRM_FORMAT_YVYU:
210604be855SAndy Yan 		return VOP2_FMT_VYUY422;
211604be855SAndy Yan 	case DRM_FORMAT_VYUY:
212604be855SAndy Yan 	case DRM_FORMAT_UYVY:
213604be855SAndy Yan 		return VOP2_FMT_YUYV422;
214604be855SAndy Yan 	default:
215604be855SAndy Yan 		DRM_ERROR("unsupported format[%08x]\n", format);
216604be855SAndy Yan 		return -EINVAL;
217604be855SAndy Yan 	}
218604be855SAndy Yan }
219604be855SAndy Yan 
vop2_convert_afbc_format(u32 format)220604be855SAndy Yan static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
221604be855SAndy Yan {
222604be855SAndy Yan 	switch (format) {
223bfd8a5c2SAndy Yan 	case DRM_FORMAT_XRGB2101010:
224bfd8a5c2SAndy Yan 	case DRM_FORMAT_ARGB2101010:
225bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
226bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
227bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_ARGB2101010;
228604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
229604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
230604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
231604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
232604be855SAndy Yan 		return VOP2_AFBC_FMT_ARGB8888;
233604be855SAndy Yan 	case DRM_FORMAT_RGB888:
234604be855SAndy Yan 	case DRM_FORMAT_BGR888:
235604be855SAndy Yan 		return VOP2_AFBC_FMT_RGB888;
236604be855SAndy Yan 	case DRM_FORMAT_RGB565:
237604be855SAndy Yan 	case DRM_FORMAT_BGR565:
238604be855SAndy Yan 		return VOP2_AFBC_FMT_RGB565;
239bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
240bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV420;
241bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
242bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV420_10BIT;
243bfd8a5c2SAndy Yan 	case DRM_FORMAT_YVYU:
244bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
245bfd8a5c2SAndy Yan 	case DRM_FORMAT_VYUY:
246bfd8a5c2SAndy Yan 	case DRM_FORMAT_UYVY:
247bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV422;
248bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
249bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV422_10BIT;
250604be855SAndy Yan 	default:
251604be855SAndy Yan 		return VOP2_AFBC_FMT_INVALID;
252604be855SAndy Yan 	}
253604be855SAndy Yan 
254604be855SAndy Yan 	return VOP2_AFBC_FMT_INVALID;
255604be855SAndy Yan }
256604be855SAndy Yan 
vop2_win_rb_swap(u32 format)257604be855SAndy Yan static bool vop2_win_rb_swap(u32 format)
258604be855SAndy Yan {
259604be855SAndy Yan 	switch (format) {
260bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
261bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
262604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
263604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
264604be855SAndy Yan 	case DRM_FORMAT_BGR888:
265604be855SAndy Yan 	case DRM_FORMAT_BGR565:
266604be855SAndy Yan 		return true;
267604be855SAndy Yan 	default:
268604be855SAndy Yan 		return false;
269604be855SAndy Yan 	}
270604be855SAndy Yan }
271604be855SAndy Yan 
vop2_afbc_uv_swap(u32 format)272604be855SAndy Yan static bool vop2_afbc_uv_swap(u32 format)
273604be855SAndy Yan {
274bfd8a5c2SAndy Yan 	switch (format) {
275bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
276bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
277bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
278bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
279bfd8a5c2SAndy Yan 		return true;
280bfd8a5c2SAndy Yan 	default:
281604be855SAndy Yan 		return false;
282604be855SAndy Yan 	}
283bfd8a5c2SAndy Yan }
284604be855SAndy Yan 
vop2_win_uv_swap(u32 format)285604be855SAndy Yan static bool vop2_win_uv_swap(u32 format)
286604be855SAndy Yan {
287604be855SAndy Yan 	switch (format) {
288604be855SAndy Yan 	case DRM_FORMAT_NV12:
289604be855SAndy Yan 	case DRM_FORMAT_NV16:
290604be855SAndy Yan 	case DRM_FORMAT_NV24:
291bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV15:
2925fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV20:
2935fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV30:
294bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
295bfd8a5c2SAndy Yan 	case DRM_FORMAT_UYVY:
296604be855SAndy Yan 		return true;
297604be855SAndy Yan 	default:
298604be855SAndy Yan 		return false;
299604be855SAndy Yan 	}
300604be855SAndy Yan }
301604be855SAndy Yan 
vop2_win_dither_up(u32 format)302604be855SAndy Yan static bool vop2_win_dither_up(u32 format)
303604be855SAndy Yan {
304604be855SAndy Yan 	switch (format) {
305604be855SAndy Yan 	case DRM_FORMAT_BGR565:
306604be855SAndy Yan 	case DRM_FORMAT_RGB565:
307604be855SAndy Yan 		return true;
308604be855SAndy Yan 	default:
309604be855SAndy Yan 		return false;
310604be855SAndy Yan 	}
311604be855SAndy Yan }
312604be855SAndy Yan 
vop2_output_uv_swap(u32 bus_format,u32 output_mode)313604be855SAndy Yan static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
314604be855SAndy Yan {
315604be855SAndy Yan 	/*
316604be855SAndy Yan 	 * FIXME:
317604be855SAndy Yan 	 *
318604be855SAndy Yan 	 * There is no media type for YUV444 output,
319604be855SAndy Yan 	 * so when out_mode is AAAA or P888, assume output is YUV444 on
320604be855SAndy Yan 	 * yuv format.
321604be855SAndy Yan 	 *
322604be855SAndy Yan 	 * From H/W testing, YUV444 mode need a rb swap.
323604be855SAndy Yan 	 */
324604be855SAndy Yan 	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
325604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
326604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
327604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
328604be855SAndy Yan 	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
329604be855SAndy Yan 	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
330604be855SAndy Yan 	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
331604be855SAndy Yan 	      output_mode == ROCKCHIP_OUT_MODE_P888)))
332604be855SAndy Yan 		return true;
333604be855SAndy Yan 	else
334604be855SAndy Yan 		return false;
335604be855SAndy Yan }
336604be855SAndy Yan 
vop2_output_rg_swap(struct vop2 * vop2,u32 bus_format)3375a028e8fSAndy Yan static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format)
3385a028e8fSAndy Yan {
339301618edSAndy Yan 	if (vop2->version == VOP_VERSION_RK3588) {
3405a028e8fSAndy Yan 		if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
3415a028e8fSAndy Yan 		    bus_format == MEDIA_BUS_FMT_YUV10_1X30)
3425a028e8fSAndy Yan 			return true;
3435a028e8fSAndy Yan 	}
3445a028e8fSAndy Yan 
3455a028e8fSAndy Yan 	return false;
3465a028e8fSAndy Yan }
3475a028e8fSAndy Yan 
is_yuv_output(u32 bus_format)348604be855SAndy Yan static bool is_yuv_output(u32 bus_format)
349604be855SAndy Yan {
350604be855SAndy Yan 	switch (bus_format) {
351604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV8_1X24:
352604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV10_1X30:
353604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
354604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
355604be855SAndy Yan 	case MEDIA_BUS_FMT_YUYV8_2X8:
356604be855SAndy Yan 	case MEDIA_BUS_FMT_YVYU8_2X8:
357604be855SAndy Yan 	case MEDIA_BUS_FMT_UYVY8_2X8:
358604be855SAndy Yan 	case MEDIA_BUS_FMT_VYUY8_2X8:
359604be855SAndy Yan 	case MEDIA_BUS_FMT_YUYV8_1X16:
360604be855SAndy Yan 	case MEDIA_BUS_FMT_YVYU8_1X16:
361604be855SAndy Yan 	case MEDIA_BUS_FMT_UYVY8_1X16:
362604be855SAndy Yan 	case MEDIA_BUS_FMT_VYUY8_1X16:
363604be855SAndy Yan 		return true;
364604be855SAndy Yan 	default:
365604be855SAndy Yan 		return false;
366604be855SAndy Yan 	}
367604be855SAndy Yan }
368604be855SAndy Yan 
rockchip_afbc(struct drm_plane * plane,u64 modifier)369604be855SAndy Yan static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
370604be855SAndy Yan {
371604be855SAndy Yan 	int i;
372604be855SAndy Yan 
373604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_LINEAR)
374604be855SAndy Yan 		return false;
375604be855SAndy Yan 
376604be855SAndy Yan 	for (i = 0 ; i < plane->modifier_count; i++)
377604be855SAndy Yan 		if (plane->modifiers[i] == modifier)
378604be855SAndy Yan 			return true;
379604be855SAndy Yan 
380604be855SAndy Yan 	return false;
381604be855SAndy Yan }
382604be855SAndy Yan 
rockchip_vop2_mod_supported(struct drm_plane * plane,u32 format,u64 modifier)383604be855SAndy Yan static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
384604be855SAndy Yan 					u64 modifier)
385604be855SAndy Yan {
386604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
387604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
388604be855SAndy Yan 
389604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_INVALID)
390604be855SAndy Yan 		return false;
391604be855SAndy Yan 
392301618edSAndy Yan 	if (vop2->version == VOP_VERSION_RK3568) {
393df063c0bSAndy Yan 		if (vop2_cluster_window(win)) {
394df063c0bSAndy Yan 			if (modifier == DRM_FORMAT_MOD_LINEAR) {
395df063c0bSAndy Yan 				drm_dbg_kms(vop2->drm,
396df063c0bSAndy Yan 					    "Cluster window only supports format with afbc\n");
397df063c0bSAndy Yan 				return false;
398df063c0bSAndy Yan 			}
399df063c0bSAndy Yan 		}
400df063c0bSAndy Yan 	}
401df063c0bSAndy Yan 
4027e8a56c7SAndy Yan 	if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) {
403301618edSAndy Yan 		if (vop2->version == VOP_VERSION_RK3588) {
4047e8a56c7SAndy Yan 			if (!rockchip_afbc(plane, modifier)) {
4057e8a56c7SAndy Yan 				drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n");
4067e8a56c7SAndy Yan 				return false;
4077e8a56c7SAndy Yan 			}
4087e8a56c7SAndy Yan 		}
4097e8a56c7SAndy Yan 	}
4107e8a56c7SAndy Yan 
411604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_LINEAR)
412604be855SAndy Yan 		return true;
413604be855SAndy Yan 
414604be855SAndy Yan 	if (!rockchip_afbc(plane, modifier)) {
415eb23cffdSMichael Tretter 		drm_dbg_kms(vop2->drm, "Unsupported format modifier 0x%llx\n",
416604be855SAndy Yan 			    modifier);
417604be855SAndy Yan 
418604be855SAndy Yan 		return false;
419604be855SAndy Yan 	}
420604be855SAndy Yan 
421604be855SAndy Yan 	return vop2_convert_afbc_format(format) >= 0;
422604be855SAndy Yan }
423604be855SAndy Yan 
424bebad6bdSAndy Yan /*
425bebad6bdSAndy Yan  * 0: Full mode, 16 lines for one tail
426bebad6bdSAndy Yan  * 1: half block mode, 8 lines one tail
427bebad6bdSAndy Yan  */
vop2_half_block_enable(struct drm_plane_state * pstate)428bebad6bdSAndy Yan static bool vop2_half_block_enable(struct drm_plane_state *pstate)
429bebad6bdSAndy Yan {
430bebad6bdSAndy Yan 	if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90))
431bebad6bdSAndy Yan 		return false;
432bebad6bdSAndy Yan 	else
433bebad6bdSAndy Yan 		return true;
434bebad6bdSAndy Yan }
435bebad6bdSAndy Yan 
vop2_afbc_transform_offset(struct drm_plane_state * pstate,bool afbc_half_block_en)436604be855SAndy Yan static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
437604be855SAndy Yan 				      bool afbc_half_block_en)
438604be855SAndy Yan {
439604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
440604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
44145ad07c7SAndy Yan 	u32 bpp = vop2_get_bpp(fb->format);
442604be855SAndy Yan 	u32 vir_width = (fb->pitches[0] << 3) / bpp;
443604be855SAndy Yan 	u32 width = drm_rect_width(src) >> 16;
444604be855SAndy Yan 	u32 height = drm_rect_height(src) >> 16;
445604be855SAndy Yan 	u32 act_xoffset = src->x1 >> 16;
446604be855SAndy Yan 	u32 act_yoffset = src->y1 >> 16;
447604be855SAndy Yan 	u32 align16_crop = 0;
448604be855SAndy Yan 	u32 align64_crop = 0;
449604be855SAndy Yan 	u32 height_tmp;
450604be855SAndy Yan 	u8 tx, ty;
451604be855SAndy Yan 	u8 bottom_crop_line_num = 0;
452604be855SAndy Yan 
453604be855SAndy Yan 	/* 16 pixel align */
454604be855SAndy Yan 	if (height & 0xf)
455604be855SAndy Yan 		align16_crop = 16 - (height & 0xf);
456604be855SAndy Yan 
457604be855SAndy Yan 	height_tmp = height + align16_crop;
458604be855SAndy Yan 
459604be855SAndy Yan 	/* 64 pixel align */
460604be855SAndy Yan 	if (height_tmp & 0x3f)
461604be855SAndy Yan 		align64_crop = 64 - (height_tmp & 0x3f);
462604be855SAndy Yan 
463604be855SAndy Yan 	bottom_crop_line_num = align16_crop + align64_crop;
464604be855SAndy Yan 
465604be855SAndy Yan 	switch (pstate->rotation &
466604be855SAndy Yan 		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
467604be855SAndy Yan 		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
468604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
469604be855SAndy Yan 		tx = 16 - ((act_xoffset + width) & 0xf);
470604be855SAndy Yan 		ty = bottom_crop_line_num - act_yoffset;
471604be855SAndy Yan 		break;
472604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
473604be855SAndy Yan 		tx = bottom_crop_line_num - act_yoffset;
474604be855SAndy Yan 		ty = vir_width - width - act_xoffset;
475604be855SAndy Yan 		break;
476604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
477604be855SAndy Yan 		tx = act_yoffset;
478604be855SAndy Yan 		ty = act_xoffset;
479604be855SAndy Yan 		break;
480604be855SAndy Yan 	case DRM_MODE_REFLECT_X:
481604be855SAndy Yan 		tx = 16 - ((act_xoffset + width) & 0xf);
482604be855SAndy Yan 		ty = act_yoffset;
483604be855SAndy Yan 		break;
484604be855SAndy Yan 	case DRM_MODE_REFLECT_Y:
485604be855SAndy Yan 		tx = act_xoffset;
486604be855SAndy Yan 		ty = bottom_crop_line_num - act_yoffset;
487604be855SAndy Yan 		break;
488604be855SAndy Yan 	case DRM_MODE_ROTATE_90:
489604be855SAndy Yan 		tx = bottom_crop_line_num - act_yoffset;
490604be855SAndy Yan 		ty = act_xoffset;
491604be855SAndy Yan 		break;
492604be855SAndy Yan 	case DRM_MODE_ROTATE_270:
493604be855SAndy Yan 		tx = act_yoffset;
494604be855SAndy Yan 		ty = vir_width - width - act_xoffset;
495604be855SAndy Yan 		break;
496604be855SAndy Yan 	case 0:
497604be855SAndy Yan 		tx = act_xoffset;
498604be855SAndy Yan 		ty = act_yoffset;
499604be855SAndy Yan 		break;
500604be855SAndy Yan 	}
501604be855SAndy Yan 
502604be855SAndy Yan 	if (afbc_half_block_en)
503604be855SAndy Yan 		ty &= 0x7f;
504604be855SAndy Yan 
505604be855SAndy Yan #define TRANSFORM_XOFFSET GENMASK(7, 0)
506604be855SAndy Yan #define TRANSFORM_YOFFSET GENMASK(23, 16)
507604be855SAndy Yan 	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
508604be855SAndy Yan 		FIELD_PREP(TRANSFORM_YOFFSET, ty);
509604be855SAndy Yan }
510604be855SAndy Yan 
511604be855SAndy Yan /*
512604be855SAndy Yan  * A Cluster window has 2048 x 16 line buffer, which can
513604be855SAndy Yan  * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
514604be855SAndy Yan  * for Cluster_lb_mode register:
515604be855SAndy Yan  * 0: half mode, for plane input width range 2048 ~ 4096
516604be855SAndy Yan  * 1: half mode, for cluster work at 2 * 2048 plane mode
517604be855SAndy Yan  * 2: half mode, for rotate_90/270 mode
518604be855SAndy Yan  *
519604be855SAndy Yan  */
vop2_get_cluster_lb_mode(struct vop2_win * win,struct drm_plane_state * pstate)520604be855SAndy Yan static int vop2_get_cluster_lb_mode(struct vop2_win *win,
521604be855SAndy Yan 				    struct drm_plane_state *pstate)
522604be855SAndy Yan {
523604be855SAndy Yan 	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
524604be855SAndy Yan 	    (pstate->rotation & DRM_MODE_ROTATE_90))
525604be855SAndy Yan 		return 2;
526604be855SAndy Yan 	else
527604be855SAndy Yan 		return 0;
528604be855SAndy Yan }
529604be855SAndy Yan 
vop2_scale_factor(u32 src,u32 dst)530604be855SAndy Yan static u16 vop2_scale_factor(u32 src, u32 dst)
531604be855SAndy Yan {
532604be855SAndy Yan 	u32 fac;
533604be855SAndy Yan 	int shift;
534604be855SAndy Yan 
535604be855SAndy Yan 	if (src == dst)
536604be855SAndy Yan 		return 0;
537604be855SAndy Yan 
538604be855SAndy Yan 	if (dst < 2)
539604be855SAndy Yan 		return U16_MAX;
540604be855SAndy Yan 
541604be855SAndy Yan 	if (src < 2)
542604be855SAndy Yan 		return 0;
543604be855SAndy Yan 
544604be855SAndy Yan 	if (src > dst)
545604be855SAndy Yan 		shift = 12;
546604be855SAndy Yan 	else
547604be855SAndy Yan 		shift = 16;
548604be855SAndy Yan 
549604be855SAndy Yan 	src--;
550604be855SAndy Yan 	dst--;
551604be855SAndy Yan 
552604be855SAndy Yan 	fac = DIV_ROUND_UP(src << shift, dst) - 1;
553604be855SAndy Yan 
554604be855SAndy Yan 	if (fac > U16_MAX)
555604be855SAndy Yan 		return U16_MAX;
556604be855SAndy Yan 
557604be855SAndy Yan 	return fac;
558604be855SAndy Yan }
559604be855SAndy Yan 
vop2_setup_scale(struct vop2 * vop2,const struct vop2_win * win,u32 src_w,u32 src_h,u32 dst_w,u32 dst_h,u32 pixel_format)560604be855SAndy Yan static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
561604be855SAndy Yan 			     u32 src_w, u32 src_h, u32 dst_w,
562604be855SAndy Yan 			     u32 dst_h, u32 pixel_format)
563604be855SAndy Yan {
564604be855SAndy Yan 	const struct drm_format_info *info;
565604be855SAndy Yan 	u16 hor_scl_mode, ver_scl_mode;
566604be855SAndy Yan 	u16 hscl_filter_mode, vscl_filter_mode;
567e80c219fSDetlev Casanova 	uint16_t cbcr_src_w = src_w;
568e80c219fSDetlev Casanova 	uint16_t cbcr_src_h = src_h;
569604be855SAndy Yan 	u8 gt2 = 0;
570604be855SAndy Yan 	u8 gt4 = 0;
571604be855SAndy Yan 	u32 val;
572604be855SAndy Yan 
573604be855SAndy Yan 	info = drm_format_info(pixel_format);
574604be855SAndy Yan 
575604be855SAndy Yan 	if (src_h >= (4 * dst_h)) {
576604be855SAndy Yan 		gt4 = 1;
577604be855SAndy Yan 		src_h >>= 2;
578604be855SAndy Yan 	} else if (src_h >= (2 * dst_h)) {
579604be855SAndy Yan 		gt2 = 1;
580604be855SAndy Yan 		src_h >>= 1;
581604be855SAndy Yan 	}
582604be855SAndy Yan 
583604be855SAndy Yan 	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
584604be855SAndy Yan 	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
585604be855SAndy Yan 
586604be855SAndy Yan 	if (hor_scl_mode == SCALE_UP)
587604be855SAndy Yan 		hscl_filter_mode = VOP2_SCALE_UP_BIC;
588604be855SAndy Yan 	else
589604be855SAndy Yan 		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
590604be855SAndy Yan 
591604be855SAndy Yan 	if (ver_scl_mode == SCALE_UP)
592604be855SAndy Yan 		vscl_filter_mode = VOP2_SCALE_UP_BIL;
593604be855SAndy Yan 	else
594604be855SAndy Yan 		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
595604be855SAndy Yan 
596604be855SAndy Yan 	/*
597604be855SAndy Yan 	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
598604be855SAndy Yan 	 * at scale down mode
599604be855SAndy Yan 	 */
600604be855SAndy Yan 	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
601604be855SAndy Yan 		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
602604be855SAndy Yan 			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
603604be855SAndy Yan 				win->data->name, dst_w);
604604be855SAndy Yan 			dst_w++;
605604be855SAndy Yan 		}
606604be855SAndy Yan 	}
607604be855SAndy Yan 
608604be855SAndy Yan 	val = vop2_scale_factor(src_w, dst_w);
609604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
610604be855SAndy Yan 	val = vop2_scale_factor(src_h, dst_h);
611604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
612604be855SAndy Yan 
613604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
614604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
615604be855SAndy Yan 
616604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
617604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
618604be855SAndy Yan 
619604be855SAndy Yan 	if (vop2_cluster_window(win))
620604be855SAndy Yan 		return;
621604be855SAndy Yan 
622604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
623604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
624604be855SAndy Yan 
625604be855SAndy Yan 	if (info->is_yuv) {
626e80c219fSDetlev Casanova 		cbcr_src_w /= info->hsub;
627e80c219fSDetlev Casanova 		cbcr_src_h /= info->vsub;
628604be855SAndy Yan 
629604be855SAndy Yan 		gt4 = 0;
630604be855SAndy Yan 		gt2 = 0;
631604be855SAndy Yan 
632e80c219fSDetlev Casanova 		if (cbcr_src_h >= (4 * dst_h)) {
633604be855SAndy Yan 			gt4 = 1;
634e80c219fSDetlev Casanova 			cbcr_src_h >>= 2;
635e80c219fSDetlev Casanova 		} else if (cbcr_src_h >= (2 * dst_h)) {
636604be855SAndy Yan 			gt2 = 1;
637e80c219fSDetlev Casanova 			cbcr_src_h >>= 1;
638604be855SAndy Yan 		}
639604be855SAndy Yan 
640e80c219fSDetlev Casanova 		hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
641e80c219fSDetlev Casanova 		ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
642604be855SAndy Yan 
643e80c219fSDetlev Casanova 		val = vop2_scale_factor(cbcr_src_w, dst_w);
644604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
645604be855SAndy Yan 
646e80c219fSDetlev Casanova 		val = vop2_scale_factor(cbcr_src_h, dst_h);
647604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
648604be855SAndy Yan 
649604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
650604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
651604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
652604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
653604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
654604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
655604be855SAndy Yan 	}
656604be855SAndy Yan }
657604be855SAndy Yan 
vop2_convert_csc_mode(int csc_mode)658604be855SAndy Yan static int vop2_convert_csc_mode(int csc_mode)
659604be855SAndy Yan {
660604be855SAndy Yan 	switch (csc_mode) {
661604be855SAndy Yan 	case V4L2_COLORSPACE_SMPTE170M:
662604be855SAndy Yan 	case V4L2_COLORSPACE_470_SYSTEM_M:
663604be855SAndy Yan 	case V4L2_COLORSPACE_470_SYSTEM_BG:
664604be855SAndy Yan 		return CSC_BT601L;
665604be855SAndy Yan 	case V4L2_COLORSPACE_REC709:
666604be855SAndy Yan 	case V4L2_COLORSPACE_SMPTE240M:
667604be855SAndy Yan 	case V4L2_COLORSPACE_DEFAULT:
668604be855SAndy Yan 		return CSC_BT709L;
669604be855SAndy Yan 	case V4L2_COLORSPACE_JPEG:
670604be855SAndy Yan 		return CSC_BT601F;
671604be855SAndy Yan 	case V4L2_COLORSPACE_BT2020:
672604be855SAndy Yan 		return CSC_BT2020;
673604be855SAndy Yan 	default:
674604be855SAndy Yan 		return CSC_BT709L;
675604be855SAndy Yan 	}
676604be855SAndy Yan }
677604be855SAndy Yan 
678604be855SAndy Yan /*
679604be855SAndy Yan  * colorspace path:
680604be855SAndy Yan  *      Input        Win csc                     Output
681604be855SAndy Yan  * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
682604be855SAndy Yan  *    RGB        --> R2Y                  __/
683604be855SAndy Yan  *
684604be855SAndy Yan  * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
685604be855SAndy Yan  *    RGB        --> 709To2020->R2Y       __/
686604be855SAndy Yan  *
687604be855SAndy Yan  * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
688604be855SAndy Yan  *    RGB        --> R2Y                  __/
689604be855SAndy Yan  *
690604be855SAndy Yan  * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
691604be855SAndy Yan  *    RGB        --> 709To2020->R2Y       __/
692604be855SAndy Yan  *
693604be855SAndy Yan  * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
694604be855SAndy Yan  *    RGB        --> R2Y                  __/
695604be855SAndy Yan  *
696604be855SAndy Yan  * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
697604be855SAndy Yan  *    RGB        --> R2Y(601)             __/
698604be855SAndy Yan  *
699604be855SAndy Yan  * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
700604be855SAndy Yan  *    RGB        --> bypass               __/
701604be855SAndy Yan  *
702604be855SAndy Yan  * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
703604be855SAndy Yan  *
704604be855SAndy Yan  * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
705604be855SAndy Yan  *
706604be855SAndy Yan  * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
707604be855SAndy Yan  *
708604be855SAndy Yan  * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
709604be855SAndy Yan  */
710604be855SAndy Yan 
vop2_setup_csc_mode(struct vop2_video_port * vp,struct vop2_win * win,struct drm_plane_state * pstate)711604be855SAndy Yan static void vop2_setup_csc_mode(struct vop2_video_port *vp,
712604be855SAndy Yan 				struct vop2_win *win,
713604be855SAndy Yan 				struct drm_plane_state *pstate)
714604be855SAndy Yan {
715604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
716604be855SAndy Yan 	int is_input_yuv = pstate->fb->format->is_yuv;
717604be855SAndy Yan 	int is_output_yuv = is_yuv_output(vcstate->bus_format);
718604be855SAndy Yan 	int input_csc = V4L2_COLORSPACE_DEFAULT;
719604be855SAndy Yan 	int output_csc = vcstate->color_space;
720604be855SAndy Yan 	bool r2y_en, y2r_en;
721604be855SAndy Yan 	int csc_mode;
722604be855SAndy Yan 
723604be855SAndy Yan 	if (is_input_yuv && !is_output_yuv) {
724604be855SAndy Yan 		y2r_en = true;
725604be855SAndy Yan 		r2y_en = false;
726604be855SAndy Yan 		csc_mode = vop2_convert_csc_mode(input_csc);
727604be855SAndy Yan 	} else if (!is_input_yuv && is_output_yuv) {
728604be855SAndy Yan 		y2r_en = false;
729604be855SAndy Yan 		r2y_en = true;
730604be855SAndy Yan 		csc_mode = vop2_convert_csc_mode(output_csc);
731604be855SAndy Yan 	} else {
732604be855SAndy Yan 		y2r_en = false;
733604be855SAndy Yan 		r2y_en = false;
734604be855SAndy Yan 		csc_mode = false;
735604be855SAndy Yan 	}
736604be855SAndy Yan 
737604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
738604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
739604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
740604be855SAndy Yan }
741604be855SAndy Yan 
vop2_crtc_enable_irq(struct vop2_video_port * vp,u32 irq)742604be855SAndy Yan static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
743604be855SAndy Yan {
744604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
745604be855SAndy Yan 
746604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
747604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
748604be855SAndy Yan }
749604be855SAndy Yan 
vop2_crtc_disable_irq(struct vop2_video_port * vp,u32 irq)750604be855SAndy Yan static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
751604be855SAndy Yan {
752604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
753604be855SAndy Yan 
754604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
755604be855SAndy Yan }
756604be855SAndy Yan 
vop2_core_clks_prepare_enable(struct vop2 * vop2)757604be855SAndy Yan static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
758604be855SAndy Yan {
759604be855SAndy Yan 	int ret;
760604be855SAndy Yan 
761604be855SAndy Yan 	ret = clk_prepare_enable(vop2->hclk);
762604be855SAndy Yan 	if (ret < 0) {
763604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
764604be855SAndy Yan 		return ret;
765604be855SAndy Yan 	}
766604be855SAndy Yan 
767604be855SAndy Yan 	ret = clk_prepare_enable(vop2->aclk);
768604be855SAndy Yan 	if (ret < 0) {
769604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
770604be855SAndy Yan 		goto err;
771604be855SAndy Yan 	}
772604be855SAndy Yan 
7735a028e8fSAndy Yan 	ret = clk_prepare_enable(vop2->pclk);
7745a028e8fSAndy Yan 	if (ret < 0) {
7755a028e8fSAndy Yan 		drm_err(vop2->drm, "failed to enable pclk - %d\n", ret);
7765a028e8fSAndy Yan 		goto err1;
7775a028e8fSAndy Yan 	}
7785a028e8fSAndy Yan 
779604be855SAndy Yan 	return 0;
7805a028e8fSAndy Yan err1:
7815a028e8fSAndy Yan 	clk_disable_unprepare(vop2->aclk);
782604be855SAndy Yan err:
783604be855SAndy Yan 	clk_disable_unprepare(vop2->hclk);
784604be855SAndy Yan 
785604be855SAndy Yan 	return ret;
786604be855SAndy Yan }
787604be855SAndy Yan 
rk3588_vop2_power_domain_enable_all(struct vop2 * vop2)7885a028e8fSAndy Yan static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2)
7895a028e8fSAndy Yan {
7905a028e8fSAndy Yan 	u32 pd;
7915a028e8fSAndy Yan 
7925a028e8fSAndy Yan 	pd = vop2_readl(vop2, RK3588_SYS_PD_CTRL);
7935a028e8fSAndy Yan 	pd &= ~(VOP2_PD_CLUSTER0 | VOP2_PD_CLUSTER1 | VOP2_PD_CLUSTER2 |
7945a028e8fSAndy Yan 		VOP2_PD_CLUSTER3 | VOP2_PD_ESMART);
7955a028e8fSAndy Yan 
7965a028e8fSAndy Yan 	vop2_writel(vop2, RK3588_SYS_PD_CTRL, pd);
7975a028e8fSAndy Yan }
7985a028e8fSAndy Yan 
vop2_enable(struct vop2 * vop2)799604be855SAndy Yan static void vop2_enable(struct vop2 *vop2)
800604be855SAndy Yan {
801604be855SAndy Yan 	int ret;
802301618edSAndy Yan 	u32 version;
803604be855SAndy Yan 
804e3558747SYuan Can 	ret = pm_runtime_resume_and_get(vop2->dev);
805604be855SAndy Yan 	if (ret < 0) {
806604be855SAndy Yan 		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
807604be855SAndy Yan 		return;
808604be855SAndy Yan 	}
809604be855SAndy Yan 
810604be855SAndy Yan 	ret = vop2_core_clks_prepare_enable(vop2);
811604be855SAndy Yan 	if (ret) {
812604be855SAndy Yan 		pm_runtime_put_sync(vop2->dev);
813604be855SAndy Yan 		return;
814604be855SAndy Yan 	}
815604be855SAndy Yan 
816604be855SAndy Yan 	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
817604be855SAndy Yan 	if (ret) {
818604be855SAndy Yan 		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
819604be855SAndy Yan 		return;
820604be855SAndy Yan 	}
821604be855SAndy Yan 
822301618edSAndy Yan 	version = vop2_readl(vop2, RK3568_VERSION_INFO);
823301618edSAndy Yan 	if (version != vop2->version) {
824301618edSAndy Yan 		drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version);
825301618edSAndy Yan 		return;
826301618edSAndy Yan 	}
827301618edSAndy Yan 
828301618edSAndy Yan 	/*
829301618edSAndy Yan 	 * rk3566 share the same vop version with rk3568, so
830301618edSAndy Yan 	 * we need to use soc_id for identification here.
831301618edSAndy Yan 	 */
832604be855SAndy Yan 	if (vop2->data->soc_id == 3566)
833604be855SAndy Yan 		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
834604be855SAndy Yan 
835301618edSAndy Yan 	if (vop2->version == VOP_VERSION_RK3588)
8365a028e8fSAndy Yan 		rk3588_vop2_power_domain_enable_all(vop2);
8375a028e8fSAndy Yan 
838*3e89a8c6SAndy Yan 	if (vop2->version <= VOP_VERSION_RK3588) {
839*3e89a8c6SAndy Yan 		vop2->old_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
840*3e89a8c6SAndy Yan 		vop2->old_port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
841*3e89a8c6SAndy Yan 	}
842*3e89a8c6SAndy Yan 
843604be855SAndy Yan 	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
844604be855SAndy Yan 
845604be855SAndy Yan 	/*
846604be855SAndy Yan 	 * Disable auto gating, this is a workaround to
847604be855SAndy Yan 	 * avoid display image shift when a window enabled.
848604be855SAndy Yan 	 */
849604be855SAndy Yan 	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
850604be855SAndy Yan 			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
851604be855SAndy Yan 
852604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
853604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
854604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_EN,
855604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
856604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
857604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
858604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_EN,
859604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
860604be855SAndy Yan }
861604be855SAndy Yan 
vop2_disable(struct vop2 * vop2)862604be855SAndy Yan static void vop2_disable(struct vop2 *vop2)
863604be855SAndy Yan {
864604be855SAndy Yan 	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
865604be855SAndy Yan 
866604be855SAndy Yan 	pm_runtime_put_sync(vop2->dev);
867604be855SAndy Yan 
8683ee348ebSAndy Yan 	regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
8693ee348ebSAndy Yan 
8705a028e8fSAndy Yan 	clk_disable_unprepare(vop2->pclk);
871604be855SAndy Yan 	clk_disable_unprepare(vop2->aclk);
872604be855SAndy Yan 	clk_disable_unprepare(vop2->hclk);
873604be855SAndy Yan }
874604be855SAndy Yan 
vop2_vp_dsp_lut_is_enabled(struct vop2_video_port * vp)8754f537776SPiotr Zalewski static bool vop2_vp_dsp_lut_is_enabled(struct vop2_video_port *vp)
8764f537776SPiotr Zalewski {
8774f537776SPiotr Zalewski 	u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
8784f537776SPiotr Zalewski 
8794f537776SPiotr Zalewski 	return dsp_ctrl & RK3568_VP_DSP_CTRL__DSP_LUT_EN;
8804f537776SPiotr Zalewski }
8814f537776SPiotr Zalewski 
vop2_vp_dsp_lut_disable(struct vop2_video_port * vp)8824f537776SPiotr Zalewski static void vop2_vp_dsp_lut_disable(struct vop2_video_port *vp)
8834f537776SPiotr Zalewski {
8844f537776SPiotr Zalewski 	u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
8854f537776SPiotr Zalewski 
8864f537776SPiotr Zalewski 	dsp_ctrl &= ~RK3568_VP_DSP_CTRL__DSP_LUT_EN;
8874f537776SPiotr Zalewski 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
8884f537776SPiotr Zalewski }
8894f537776SPiotr Zalewski 
vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port * vp)8904f537776SPiotr Zalewski static bool vop2_vp_dsp_lut_poll_disabled(struct vop2_video_port *vp)
8914f537776SPiotr Zalewski {
8924f537776SPiotr Zalewski 	u32 dsp_ctrl;
8934f537776SPiotr Zalewski 	int ret = readx_poll_timeout(vop2_vp_dsp_lut_is_enabled, vp, dsp_ctrl,
8944f537776SPiotr Zalewski 				!dsp_ctrl, 5, 30 * 1000);
8954f537776SPiotr Zalewski 	if (ret) {
8964f537776SPiotr Zalewski 		drm_err(vp->vop2->drm, "display LUT RAM enable timeout!\n");
8974f537776SPiotr Zalewski 		return false;
8984f537776SPiotr Zalewski 	}
8994f537776SPiotr Zalewski 
9004f537776SPiotr Zalewski 	return true;
9014f537776SPiotr Zalewski }
9024f537776SPiotr Zalewski 
vop2_vp_dsp_lut_enable(struct vop2_video_port * vp)9034f537776SPiotr Zalewski static void vop2_vp_dsp_lut_enable(struct vop2_video_port *vp)
9044f537776SPiotr Zalewski {
9054f537776SPiotr Zalewski 	u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
9064f537776SPiotr Zalewski 
9074f537776SPiotr Zalewski 	dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_LUT_EN;
9084f537776SPiotr Zalewski 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
9094f537776SPiotr Zalewski }
9104f537776SPiotr Zalewski 
vop2_vp_dsp_lut_update_enable(struct vop2_video_port * vp)9114f537776SPiotr Zalewski static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp)
9124f537776SPiotr Zalewski {
9134f537776SPiotr Zalewski 	u32 dsp_ctrl = vop2_vp_read(vp, RK3568_VP_DSP_CTRL);
9144f537776SPiotr Zalewski 
9154f537776SPiotr Zalewski 	dsp_ctrl |= RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN;
9164f537776SPiotr Zalewski 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
9174f537776SPiotr Zalewski }
9184f537776SPiotr Zalewski 
vop2_supports_seamless_gamma_lut_update(struct vop2 * vop2)9194f537776SPiotr Zalewski static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2)
9204f537776SPiotr Zalewski {
921301618edSAndy Yan 	return vop2->version != VOP_VERSION_RK3568;
9224f537776SPiotr Zalewski }
9234f537776SPiotr Zalewski 
vop2_gamma_lut_in_use(struct vop2 * vop2,struct vop2_video_port * vp)9244f537776SPiotr Zalewski static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp)
9254f537776SPiotr Zalewski {
9264f537776SPiotr Zalewski 	const int nr_vps = vop2->data->nr_vps;
9274f537776SPiotr Zalewski 	int gamma_en_vp_id;
9284f537776SPiotr Zalewski 
9294f537776SPiotr Zalewski 	for (gamma_en_vp_id = 0; gamma_en_vp_id < nr_vps; gamma_en_vp_id++)
9304f537776SPiotr Zalewski 		if (vop2_vp_dsp_lut_is_enabled(&vop2->vps[gamma_en_vp_id]))
9314f537776SPiotr Zalewski 			break;
9324f537776SPiotr Zalewski 
9334f537776SPiotr Zalewski 	return gamma_en_vp_id != nr_vps && gamma_en_vp_id != vp->id;
9344f537776SPiotr Zalewski }
9354f537776SPiotr Zalewski 
vop2_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)936604be855SAndy Yan static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
937604be855SAndy Yan 				     struct drm_atomic_state *state)
938604be855SAndy Yan {
939604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
940604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
941447fb14bSMichael Tretter 	struct drm_crtc_state *old_crtc_state;
942604be855SAndy Yan 	int ret;
943604be855SAndy Yan 
944604be855SAndy Yan 	vop2_lock(vop2);
945604be855SAndy Yan 
946447fb14bSMichael Tretter 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
947447fb14bSMichael Tretter 	drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
948447fb14bSMichael Tretter 
949604be855SAndy Yan 	drm_crtc_vblank_off(crtc);
950604be855SAndy Yan 
951604be855SAndy Yan 	/*
952604be855SAndy Yan 	 * Vop standby will take effect at end of current frame,
953604be855SAndy Yan 	 * if dsp hold valid irq happen, it means standby complete.
954604be855SAndy Yan 	 *
955604be855SAndy Yan 	 * we must wait standby complete when we want to disable aclk,
956604be855SAndy Yan 	 * if not, memory bus maybe dead.
957604be855SAndy Yan 	 */
958604be855SAndy Yan 	reinit_completion(&vp->dsp_hold_completion);
959604be855SAndy Yan 
960604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
961604be855SAndy Yan 
962604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
963604be855SAndy Yan 
964604be855SAndy Yan 	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
965604be855SAndy Yan 					  msecs_to_jiffies(50));
966604be855SAndy Yan 	if (!ret)
967604be855SAndy Yan 		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
968604be855SAndy Yan 
969604be855SAndy Yan 	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
970604be855SAndy Yan 
9712c1268e7SCristian Ciocaltea 	if (vp->dclk_src)
9722c1268e7SCristian Ciocaltea 		clk_set_parent(vp->dclk, vp->dclk_src);
9732c1268e7SCristian Ciocaltea 
974604be855SAndy Yan 	clk_disable_unprepare(vp->dclk);
975604be855SAndy Yan 
976604be855SAndy Yan 	vop2->enable_count--;
977604be855SAndy Yan 
978604be855SAndy Yan 	if (!vop2->enable_count)
979604be855SAndy Yan 		vop2_disable(vop2);
980604be855SAndy Yan 
981604be855SAndy Yan 	vop2_unlock(vop2);
982604be855SAndy Yan 
983604be855SAndy Yan 	if (crtc->state->event && !crtc->state->active) {
984604be855SAndy Yan 		spin_lock_irq(&crtc->dev->event_lock);
985604be855SAndy Yan 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
986604be855SAndy Yan 		spin_unlock_irq(&crtc->dev->event_lock);
987604be855SAndy Yan 
988604be855SAndy Yan 		crtc->state->event = NULL;
989604be855SAndy Yan 	}
990604be855SAndy Yan }
991604be855SAndy Yan 
vop2_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * astate)992604be855SAndy Yan static int vop2_plane_atomic_check(struct drm_plane *plane,
993604be855SAndy Yan 				   struct drm_atomic_state *astate)
994604be855SAndy Yan {
995604be855SAndy Yan 	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
996604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
997604be855SAndy Yan 	struct drm_crtc *crtc = pstate->crtc;
998604be855SAndy Yan 	struct drm_crtc_state *cstate;
999604be855SAndy Yan 	struct vop2_video_port *vp;
1000604be855SAndy Yan 	struct vop2 *vop2;
1001604be855SAndy Yan 	const struct vop2_data *vop2_data;
1002604be855SAndy Yan 	struct drm_rect *dest = &pstate->dst;
1003604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
1004604be855SAndy Yan 	int min_scale = FRAC_16_16(1, 8);
1005604be855SAndy Yan 	int max_scale = FRAC_16_16(8, 1);
1006604be855SAndy Yan 	int format;
1007604be855SAndy Yan 	int ret;
1008604be855SAndy Yan 
1009604be855SAndy Yan 	if (!crtc)
1010604be855SAndy Yan 		return 0;
1011604be855SAndy Yan 
1012604be855SAndy Yan 	vp = to_vop2_video_port(crtc);
1013604be855SAndy Yan 	vop2 = vp->vop2;
1014604be855SAndy Yan 	vop2_data = vop2->data;
1015604be855SAndy Yan 
1016604be855SAndy Yan 	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
1017604be855SAndy Yan 	if (WARN_ON(!cstate))
1018604be855SAndy Yan 		return -EINVAL;
1019604be855SAndy Yan 
1020604be855SAndy Yan 	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
1021604be855SAndy Yan 						  min_scale, max_scale,
1022604be855SAndy Yan 						  true, true);
1023604be855SAndy Yan 	if (ret)
1024604be855SAndy Yan 		return ret;
1025604be855SAndy Yan 
1026604be855SAndy Yan 	if (!pstate->visible)
1027604be855SAndy Yan 		return 0;
1028604be855SAndy Yan 
1029604be855SAndy Yan 	format = vop2_convert_format(fb->format->format);
1030604be855SAndy Yan 	if (format < 0)
1031604be855SAndy Yan 		return format;
1032604be855SAndy Yan 
1033604be855SAndy Yan 	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
1034604be855SAndy Yan 	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
1035604be855SAndy Yan 		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
1036604be855SAndy Yan 			drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
1037604be855SAndy Yan 			drm_rect_width(dest), drm_rect_height(dest));
1038604be855SAndy Yan 		pstate->visible = false;
1039604be855SAndy Yan 		return 0;
1040604be855SAndy Yan 	}
1041604be855SAndy Yan 
1042604be855SAndy Yan 	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
1043604be855SAndy Yan 	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
1044604be855SAndy Yan 		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
1045604be855SAndy Yan 			drm_rect_width(src) >> 16,
1046604be855SAndy Yan 			drm_rect_height(src) >> 16,
1047604be855SAndy Yan 			vop2_data->max_input.width,
1048604be855SAndy Yan 			vop2_data->max_input.height);
1049604be855SAndy Yan 		return -EINVAL;
1050604be855SAndy Yan 	}
1051604be855SAndy Yan 
1052604be855SAndy Yan 	/*
1053604be855SAndy Yan 	 * Src.x1 can be odd when do clip, but yuv plane start point
1054604be855SAndy Yan 	 * need align with 2 pixel.
1055604be855SAndy Yan 	 */
1056604be855SAndy Yan 	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
1057604be855SAndy Yan 		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
1058604be855SAndy Yan 		return -EINVAL;
1059604be855SAndy Yan 	}
1060604be855SAndy Yan 
1061604be855SAndy Yan 	return 0;
1062604be855SAndy Yan }
1063604be855SAndy Yan 
vop2_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)1064604be855SAndy Yan static void vop2_plane_atomic_disable(struct drm_plane *plane,
1065604be855SAndy Yan 				      struct drm_atomic_state *state)
1066604be855SAndy Yan {
1067471bf240SMichael Tretter 	struct drm_plane_state *old_pstate = NULL;
1068604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1069604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
1070604be855SAndy Yan 
1071604be855SAndy Yan 	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
1072604be855SAndy Yan 
1073471bf240SMichael Tretter 	if (state)
1074471bf240SMichael Tretter 		old_pstate = drm_atomic_get_old_plane_state(state, plane);
1075471bf240SMichael Tretter 	if (old_pstate && !old_pstate->crtc)
1076604be855SAndy Yan 		return;
1077604be855SAndy Yan 
1078604be855SAndy Yan 	vop2_win_disable(win);
1079604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
1080604be855SAndy Yan }
1081604be855SAndy Yan 
1082604be855SAndy Yan /*
1083604be855SAndy Yan  * The color key is 10 bit, so all format should
1084604be855SAndy Yan  * convert to 10 bit here.
1085604be855SAndy Yan  */
vop2_plane_setup_color_key(struct drm_plane * plane,u32 color_key)1086604be855SAndy Yan static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
1087604be855SAndy Yan {
1088604be855SAndy Yan 	struct drm_plane_state *pstate = plane->state;
1089604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
1090604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1091604be855SAndy Yan 	u32 color_key_en = 0;
1092604be855SAndy Yan 	u32 r = 0;
1093604be855SAndy Yan 	u32 g = 0;
1094604be855SAndy Yan 	u32 b = 0;
1095604be855SAndy Yan 
1096604be855SAndy Yan 	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
1097604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
1098604be855SAndy Yan 		return;
1099604be855SAndy Yan 	}
1100604be855SAndy Yan 
1101604be855SAndy Yan 	switch (fb->format->format) {
1102604be855SAndy Yan 	case DRM_FORMAT_RGB565:
1103604be855SAndy Yan 	case DRM_FORMAT_BGR565:
1104604be855SAndy Yan 		r = (color_key & 0xf800) >> 11;
1105604be855SAndy Yan 		g = (color_key & 0x7e0) >> 5;
1106604be855SAndy Yan 		b = (color_key & 0x1f);
1107604be855SAndy Yan 		r <<= 5;
1108604be855SAndy Yan 		g <<= 4;
1109604be855SAndy Yan 		b <<= 5;
1110604be855SAndy Yan 		color_key_en = 1;
1111604be855SAndy Yan 		break;
1112604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
1113604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
1114604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
1115604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
1116604be855SAndy Yan 	case DRM_FORMAT_RGB888:
1117604be855SAndy Yan 	case DRM_FORMAT_BGR888:
1118604be855SAndy Yan 		r = (color_key & 0xff0000) >> 16;
1119604be855SAndy Yan 		g = (color_key & 0xff00) >> 8;
1120604be855SAndy Yan 		b = (color_key & 0xff);
1121604be855SAndy Yan 		r <<= 2;
1122604be855SAndy Yan 		g <<= 2;
1123604be855SAndy Yan 		b <<= 2;
1124604be855SAndy Yan 		color_key_en = 1;
1125604be855SAndy Yan 		break;
1126604be855SAndy Yan 	}
1127604be855SAndy Yan 
1128604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
1129604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
1130604be855SAndy Yan }
1131604be855SAndy Yan 
vop2_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)1132604be855SAndy Yan static void vop2_plane_atomic_update(struct drm_plane *plane,
1133604be855SAndy Yan 				     struct drm_atomic_state *state)
1134604be855SAndy Yan {
1135604be855SAndy Yan 	struct drm_plane_state *pstate = plane->state;
1136604be855SAndy Yan 	struct drm_crtc *crtc = pstate->crtc;
1137604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1138604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1139604be855SAndy Yan 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
1140604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
1141604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
114245ad07c7SAndy Yan 	u32 bpp = vop2_get_bpp(fb->format);
1143604be855SAndy Yan 	u32 actual_w, actual_h, dsp_w, dsp_h;
1144604be855SAndy Yan 	u32 act_info, dsp_info;
1145604be855SAndy Yan 	u32 format;
1146604be855SAndy Yan 	u32 afbc_format;
1147604be855SAndy Yan 	u32 rb_swap;
1148604be855SAndy Yan 	u32 uv_swap;
1149604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
1150604be855SAndy Yan 	struct drm_rect *dest = &pstate->dst;
1151604be855SAndy Yan 	u32 afbc_tile_num;
1152604be855SAndy Yan 	u32 transform_offset;
1153604be855SAndy Yan 	bool dither_up;
1154604be855SAndy Yan 	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
1155604be855SAndy Yan 	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
1156604be855SAndy Yan 	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
1157604be855SAndy Yan 	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
1158604be855SAndy Yan 	struct rockchip_gem_object *rk_obj;
1159604be855SAndy Yan 	unsigned long offset;
1160bebad6bdSAndy Yan 	bool half_block_en;
1161604be855SAndy Yan 	bool afbc_en;
1162604be855SAndy Yan 	dma_addr_t yrgb_mst;
1163604be855SAndy Yan 	dma_addr_t uv_mst;
1164604be855SAndy Yan 
1165604be855SAndy Yan 	/*
1166604be855SAndy Yan 	 * can't update plane when vop2 is disabled.
1167604be855SAndy Yan 	 */
1168604be855SAndy Yan 	if (WARN_ON(!crtc))
1169604be855SAndy Yan 		return;
1170604be855SAndy Yan 
1171604be855SAndy Yan 	if (!pstate->visible) {
1172604be855SAndy Yan 		vop2_plane_atomic_disable(plane, state);
1173604be855SAndy Yan 		return;
1174604be855SAndy Yan 	}
1175604be855SAndy Yan 
1176604be855SAndy Yan 	afbc_en = rockchip_afbc(plane, fb->modifier);
1177604be855SAndy Yan 
1178604be855SAndy Yan 	offset = (src->x1 >> 16) * fb->format->cpp[0];
1179604be855SAndy Yan 
1180604be855SAndy Yan 	/*
1181604be855SAndy Yan 	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
1182604be855SAndy Yan 	 */
1183604be855SAndy Yan 	if (afbc_en)
1184604be855SAndy Yan 		offset = 0;
1185604be855SAndy Yan 	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
1186604be855SAndy Yan 		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
1187604be855SAndy Yan 	else
1188604be855SAndy Yan 		offset += (src->y1 >> 16) * fb->pitches[0];
1189604be855SAndy Yan 
1190604be855SAndy Yan 	rk_obj = to_rockchip_obj(fb->obj[0]);
1191604be855SAndy Yan 
1192604be855SAndy Yan 	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
1193604be855SAndy Yan 	if (fb->format->is_yuv) {
1194604be855SAndy Yan 		int hsub = fb->format->hsub;
1195604be855SAndy Yan 		int vsub = fb->format->vsub;
1196604be855SAndy Yan 
1197604be855SAndy Yan 		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
1198604be855SAndy Yan 		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
1199604be855SAndy Yan 
1200604be855SAndy Yan 		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
1201604be855SAndy Yan 			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
1202604be855SAndy Yan 
1203604be855SAndy Yan 		rk_obj = to_rockchip_obj(fb->obj[0]);
1204604be855SAndy Yan 		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
1205604be855SAndy Yan 	}
1206604be855SAndy Yan 
1207604be855SAndy Yan 	actual_w = drm_rect_width(src) >> 16;
1208604be855SAndy Yan 	actual_h = drm_rect_height(src) >> 16;
1209604be855SAndy Yan 	dsp_w = drm_rect_width(dest);
1210604be855SAndy Yan 
1211604be855SAndy Yan 	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
1212712ec5deSAndy Yan 		drm_dbg_kms(vop2->drm,
1213712ec5deSAndy Yan 			    "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
1214604be855SAndy Yan 			    vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
1215604be855SAndy Yan 		dsp_w = adjusted_mode->hdisplay - dest->x1;
1216604be855SAndy Yan 		if (dsp_w < 4)
1217604be855SAndy Yan 			dsp_w = 4;
1218604be855SAndy Yan 		actual_w = dsp_w * actual_w / drm_rect_width(dest);
1219604be855SAndy Yan 	}
1220604be855SAndy Yan 
1221604be855SAndy Yan 	dsp_h = drm_rect_height(dest);
1222604be855SAndy Yan 
1223604be855SAndy Yan 	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
1224712ec5deSAndy Yan 		drm_dbg_kms(vop2->drm,
1225712ec5deSAndy Yan 			    "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
1226604be855SAndy Yan 			    vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
1227604be855SAndy Yan 		dsp_h = adjusted_mode->vdisplay - dest->y1;
1228604be855SAndy Yan 		if (dsp_h < 4)
1229604be855SAndy Yan 			dsp_h = 4;
1230604be855SAndy Yan 		actual_h = dsp_h * actual_h / drm_rect_height(dest);
1231604be855SAndy Yan 	}
1232604be855SAndy Yan 
1233604be855SAndy Yan 	/*
1234604be855SAndy Yan 	 * This is workaround solution for IC design:
1235604be855SAndy Yan 	 * esmart can't support scale down when actual_w % 16 == 1.
1236604be855SAndy Yan 	 */
1237604be855SAndy Yan 	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
1238604be855SAndy Yan 		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
1239712ec5deSAndy Yan 			drm_dbg_kms(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
1240604be855SAndy Yan 				    vp->id, win->data->name, actual_w);
1241604be855SAndy Yan 			actual_w -= 1;
1242604be855SAndy Yan 		}
1243604be855SAndy Yan 	}
1244604be855SAndy Yan 
1245604be855SAndy Yan 	if (afbc_en && actual_w % 4) {
1246712ec5deSAndy Yan 		drm_dbg_kms(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
1247604be855SAndy Yan 			    vp->id, win->data->name, actual_w);
1248604be855SAndy Yan 		actual_w = ALIGN_DOWN(actual_w, 4);
1249604be855SAndy Yan 	}
1250604be855SAndy Yan 
1251604be855SAndy Yan 	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
1252604be855SAndy Yan 	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
1253604be855SAndy Yan 
1254604be855SAndy Yan 	format = vop2_convert_format(fb->format->format);
1255bebad6bdSAndy Yan 	half_block_en = vop2_half_block_enable(pstate);
1256604be855SAndy Yan 
1257604be855SAndy Yan 	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
1258604be855SAndy Yan 		vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
1259604be855SAndy Yan 		dest->x1, dest->y1,
1260604be855SAndy Yan 		&fb->format->format,
1261604be855SAndy Yan 		afbc_en ? "AFBC" : "", &yrgb_mst);
1262604be855SAndy Yan 
1263301618edSAndy Yan 	if (vop2->version > VOP_VERSION_RK3568) {
12647b256880SAndy Yan 		vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id);
12657b256880SAndy Yan 		vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id);
12667b256880SAndy Yan 		vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id);
12677b256880SAndy Yan 	}
12687b256880SAndy Yan 
1269944757a4SAndy Yan 	if (vop2->version >= VOP_VERSION_RK3576)
1270944757a4SAndy Yan 		vop2_win_write(win, VOP2_WIN_VP_SEL, vp->id);
1271944757a4SAndy Yan 
1272bebad6bdSAndy Yan 	if (vop2_cluster_window(win))
1273bebad6bdSAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en);
1274bebad6bdSAndy Yan 
1275604be855SAndy Yan 	if (afbc_en) {
1276938fbb16SAndy Yan 		u32 stride, block_w;
1277604be855SAndy Yan 
1278938fbb16SAndy Yan 		/* the afbc superblock is 16 x 16 or 32 x 8 */
1279938fbb16SAndy Yan 		block_w = fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 ? 32 : 16;
1280938fbb16SAndy Yan 
1281604be855SAndy Yan 		afbc_format = vop2_convert_afbc_format(fb->format->format);
1282604be855SAndy Yan 
1283604be855SAndy Yan 		/* Enable color transform for YTR */
1284604be855SAndy Yan 		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
1285604be855SAndy Yan 			afbc_format |= (1 << 4);
1286604be855SAndy Yan 
1287938fbb16SAndy Yan 		afbc_tile_num = ALIGN(actual_w, block_w) / block_w;
1288604be855SAndy Yan 
1289604be855SAndy Yan 		/*
1290604be855SAndy Yan 		 * AFBC pic_vir_width is count by pixel, this is different
1291604be855SAndy Yan 		 * with WIN_VIR_STRIDE.
1292604be855SAndy Yan 		 */
1293604be855SAndy Yan 		stride = (fb->pitches[0] << 3) / bpp;
1294604be855SAndy Yan 		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
1295712ec5deSAndy Yan 			drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
1296604be855SAndy Yan 				    vp->id, win->data->name, stride);
1297604be855SAndy Yan 
1298938fbb16SAndy Yan 		 /* It's for head stride, each head size is 16 byte */
1299938fbb16SAndy Yan 		stride = ALIGN(stride, block_w) / block_w * 16;
1300938fbb16SAndy Yan 
1301604be855SAndy Yan 		uv_swap = vop2_afbc_uv_swap(fb->format->format);
1302604be855SAndy Yan 		/*
1303604be855SAndy Yan 		 * This is a workaround for crazy IC design, Cluster
1304604be855SAndy Yan 		 * and Esmart/Smart use different format configuration map:
1305604be855SAndy Yan 		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
1306604be855SAndy Yan 		 *
1307604be855SAndy Yan 		 * This is one thing we can make the convert simple:
1308604be855SAndy Yan 		 * AFBCD decode all the YUV data to YUV444. So we just
1309604be855SAndy Yan 		 * set all the yuv 10 bit to YUV444_10.
1310604be855SAndy Yan 		 */
1311604be855SAndy Yan 		if (fb->format->is_yuv && bpp == 10)
1312604be855SAndy Yan 			format = VOP2_CLUSTER_YUV444_10;
1313604be855SAndy Yan 
1314604be855SAndy Yan 		if (vop2_cluster_window(win))
1315604be855SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
1316604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
1317604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
13185a028e8fSAndy Yan 		/*
13195a028e8fSAndy Yan 		 * On rk3566/8, this bit is auto gating enable,
13205a028e8fSAndy Yan 		 * but this function is not work well so we need
13215a028e8fSAndy Yan 		 * to disable it for these two platform.
13225a028e8fSAndy Yan 		 * On rk3588, and the following new soc(rk3528/rk3576),
13235a028e8fSAndy Yan 		 * this bit is gating disable, we should write 1 to
13245a028e8fSAndy Yan 		 * disable gating when enable afbc.
13255a028e8fSAndy Yan 		 */
1326301618edSAndy Yan 		if (vop2->version == VOP_VERSION_RK3568)
1327604be855SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
13285a028e8fSAndy Yan 		else
13295a028e8fSAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
13305a028e8fSAndy Yan 
1331938fbb16SAndy Yan 		if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
1332938fbb16SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 1);
1333938fbb16SAndy Yan 		else
1334604be855SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
1335938fbb16SAndy Yan 
1336944757a4SAndy Yan 		if (vop2->version >= VOP_VERSION_RK3576) {
1337944757a4SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET_EN, 1);
1338944757a4SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_PLD_OFFSET, yrgb_mst);
1339944757a4SAndy Yan 		}
1340944757a4SAndy Yan 
1341bebad6bdSAndy Yan 		transform_offset = vop2_afbc_transform_offset(pstate, half_block_en);
1342604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
1343604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
1344838a871aSAndy Yan 		vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, transform_offset);
1345604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
1346604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
1347604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
1348604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
1349604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
1350604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
1351604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
1352604be855SAndy Yan 	} else {
135320529a68SAndy Yan 		if (vop2_cluster_window(win)) {
135420529a68SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 0);
1355838a871aSAndy Yan 			vop2_win_write(win, VOP2_WIN_TRANSFORM_OFFSET, 0);
135620529a68SAndy Yan 		}
135720529a68SAndy Yan 
1358604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
1359604be855SAndy Yan 	}
1360604be855SAndy Yan 
1361604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
1362604be855SAndy Yan 
1363604be855SAndy Yan 	if (rotate_90 || rotate_270) {
1364604be855SAndy Yan 		act_info = swahw32(act_info);
1365604be855SAndy Yan 		actual_w = drm_rect_height(src) >> 16;
1366604be855SAndy Yan 		actual_h = drm_rect_width(src) >> 16;
1367604be855SAndy Yan 	}
1368604be855SAndy Yan 
1369604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_FORMAT, format);
1370604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
1371604be855SAndy Yan 
1372604be855SAndy Yan 	rb_swap = vop2_win_rb_swap(fb->format->format);
1373604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
1374604be855SAndy Yan 	uv_swap = vop2_win_uv_swap(fb->format->format);
1375604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
1376604be855SAndy Yan 
1377604be855SAndy Yan 	if (fb->format->is_yuv) {
1378604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
1379604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
1380604be855SAndy Yan 	}
1381604be855SAndy Yan 
1382604be855SAndy Yan 	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
1383604be855SAndy Yan 	if (!vop2_cluster_window(win))
1384604be855SAndy Yan 		vop2_plane_setup_color_key(plane, 0);
1385604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
1386604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
1387604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
1388604be855SAndy Yan 
1389604be855SAndy Yan 	vop2_setup_csc_mode(vp, win, pstate);
1390604be855SAndy Yan 
1391604be855SAndy Yan 	dither_up = vop2_win_dither_up(fb->format->format);
1392604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
1393604be855SAndy Yan 
1394604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
1395604be855SAndy Yan 
1396604be855SAndy Yan 	if (vop2_cluster_window(win)) {
1397604be855SAndy Yan 		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
1398604be855SAndy Yan 
1399604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
1400604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
1401604be855SAndy Yan 	}
1402604be855SAndy Yan }
1403604be855SAndy Yan 
1404604be855SAndy Yan static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
1405604be855SAndy Yan 	.atomic_check = vop2_plane_atomic_check,
1406604be855SAndy Yan 	.atomic_update = vop2_plane_atomic_update,
1407604be855SAndy Yan 	.atomic_disable = vop2_plane_atomic_disable,
1408604be855SAndy Yan };
1409604be855SAndy Yan 
1410604be855SAndy Yan static const struct drm_plane_funcs vop2_plane_funcs = {
1411604be855SAndy Yan 	.update_plane	= drm_atomic_helper_update_plane,
1412604be855SAndy Yan 	.disable_plane	= drm_atomic_helper_disable_plane,
1413604be855SAndy Yan 	.destroy = drm_plane_cleanup,
1414604be855SAndy Yan 	.reset = drm_atomic_helper_plane_reset,
1415604be855SAndy Yan 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1416604be855SAndy Yan 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
1417604be855SAndy Yan 	.format_mod_supported = rockchip_vop2_mod_supported,
1418604be855SAndy Yan };
1419604be855SAndy Yan 
vop2_crtc_enable_vblank(struct drm_crtc * crtc)1420604be855SAndy Yan static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
1421604be855SAndy Yan {
1422604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1423604be855SAndy Yan 
1424604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
1425604be855SAndy Yan 
1426604be855SAndy Yan 	return 0;
1427604be855SAndy Yan }
1428604be855SAndy Yan 
vop2_crtc_disable_vblank(struct drm_crtc * crtc)1429604be855SAndy Yan static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
1430604be855SAndy Yan {
1431604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1432604be855SAndy Yan 
1433604be855SAndy Yan 	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
1434604be855SAndy Yan }
1435604be855SAndy Yan 
vop2_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adj_mode)1436604be855SAndy Yan static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
1437604be855SAndy Yan 				 const struct drm_display_mode *mode,
1438604be855SAndy Yan 				 struct drm_display_mode *adj_mode)
1439604be855SAndy Yan {
1440604be855SAndy Yan 	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
1441604be855SAndy Yan 					CRTC_STEREO_DOUBLE);
1442604be855SAndy Yan 
1443604be855SAndy Yan 	return true;
1444604be855SAndy Yan }
1445604be855SAndy Yan 
vop2_crtc_write_gamma_lut(struct vop2 * vop2,struct drm_crtc * crtc)14464f537776SPiotr Zalewski static void vop2_crtc_write_gamma_lut(struct vop2 *vop2, struct drm_crtc *crtc)
14474f537776SPiotr Zalewski {
14484f537776SPiotr Zalewski 	const struct vop2_video_port *vp = to_vop2_video_port(crtc);
14494f537776SPiotr Zalewski 	const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id];
14504f537776SPiotr Zalewski 	struct drm_color_lut *lut = crtc->state->gamma_lut->data;
14514f537776SPiotr Zalewski 	unsigned int i, bpc = ilog2(vp_data->gamma_lut_len);
14524f537776SPiotr Zalewski 	u32 word;
14534f537776SPiotr Zalewski 
14544f537776SPiotr Zalewski 	for (i = 0; i < crtc->gamma_size; i++) {
14554f537776SPiotr Zalewski 		word = (drm_color_lut_extract(lut[i].blue, bpc) << (2 * bpc)) |
14564f537776SPiotr Zalewski 		    (drm_color_lut_extract(lut[i].green, bpc) << bpc) |
14574f537776SPiotr Zalewski 		    drm_color_lut_extract(lut[i].red, bpc);
14584f537776SPiotr Zalewski 
14594f537776SPiotr Zalewski 		writel(word, vop2->lut_regs + i * 4);
14604f537776SPiotr Zalewski 	}
14614f537776SPiotr Zalewski }
14624f537776SPiotr Zalewski 
vop2_crtc_atomic_set_gamma_seamless(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc)14634f537776SPiotr Zalewski static void vop2_crtc_atomic_set_gamma_seamless(struct vop2 *vop2,
14644f537776SPiotr Zalewski 						struct vop2_video_port *vp,
14654f537776SPiotr Zalewski 						struct drm_crtc *crtc)
14664f537776SPiotr Zalewski {
14674f537776SPiotr Zalewski 	vop2_writel(vop2, RK3568_LUT_PORT_SEL,
14684f537776SPiotr Zalewski 		    FIELD_PREP(RK3588_LUT_PORT_SEL__GAMMA_AHB_WRITE_SEL, vp->id));
14694f537776SPiotr Zalewski 	vop2_vp_dsp_lut_enable(vp);
14704f537776SPiotr Zalewski 	vop2_crtc_write_gamma_lut(vop2, crtc);
14714f537776SPiotr Zalewski 	vop2_vp_dsp_lut_update_enable(vp);
14724f537776SPiotr Zalewski }
14734f537776SPiotr Zalewski 
vop2_crtc_atomic_set_gamma_rk356x(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc)14744f537776SPiotr Zalewski static void vop2_crtc_atomic_set_gamma_rk356x(struct vop2 *vop2,
14754f537776SPiotr Zalewski 					      struct vop2_video_port *vp,
14764f537776SPiotr Zalewski 					      struct drm_crtc *crtc)
14774f537776SPiotr Zalewski {
14784f537776SPiotr Zalewski 	vop2_vp_dsp_lut_disable(vp);
14794f537776SPiotr Zalewski 	vop2_cfg_done(vp);
14804f537776SPiotr Zalewski 	if (!vop2_vp_dsp_lut_poll_disabled(vp))
14814f537776SPiotr Zalewski 		return;
14824f537776SPiotr Zalewski 
14834f537776SPiotr Zalewski 	vop2_writel(vop2, RK3568_LUT_PORT_SEL, vp->id);
14844f537776SPiotr Zalewski 	vop2_crtc_write_gamma_lut(vop2, crtc);
14854f537776SPiotr Zalewski 	vop2_vp_dsp_lut_enable(vp);
14864f537776SPiotr Zalewski }
14874f537776SPiotr Zalewski 
vop2_crtc_atomic_try_set_gamma(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)14884f537776SPiotr Zalewski static void vop2_crtc_atomic_try_set_gamma(struct vop2 *vop2,
14894f537776SPiotr Zalewski 					   struct vop2_video_port *vp,
14904f537776SPiotr Zalewski 					   struct drm_crtc *crtc,
14914f537776SPiotr Zalewski 					   struct drm_crtc_state *crtc_state)
14924f537776SPiotr Zalewski {
14939c22b6ecSPiotr Zalewski 	if (!vop2->lut_regs)
14944f537776SPiotr Zalewski 		return;
14954f537776SPiotr Zalewski 
14964f537776SPiotr Zalewski 	if (!crtc_state->gamma_lut) {
14974f537776SPiotr Zalewski 		vop2_vp_dsp_lut_disable(vp);
14984f537776SPiotr Zalewski 		return;
14994f537776SPiotr Zalewski 	}
15004f537776SPiotr Zalewski 
15014f537776SPiotr Zalewski 	if (vop2_supports_seamless_gamma_lut_update(vop2))
15024f537776SPiotr Zalewski 		vop2_crtc_atomic_set_gamma_seamless(vop2, vp, crtc);
15034f537776SPiotr Zalewski 	else
15044f537776SPiotr Zalewski 		vop2_crtc_atomic_set_gamma_rk356x(vop2, vp, crtc);
15054f537776SPiotr Zalewski }
15064f537776SPiotr Zalewski 
vop2_crtc_atomic_try_set_gamma_locked(struct vop2 * vop2,struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)15074f537776SPiotr Zalewski static inline void vop2_crtc_atomic_try_set_gamma_locked(struct vop2 *vop2,
15084f537776SPiotr Zalewski 							 struct vop2_video_port *vp,
15094f537776SPiotr Zalewski 							 struct drm_crtc *crtc,
15104f537776SPiotr Zalewski 							 struct drm_crtc_state *crtc_state)
15114f537776SPiotr Zalewski {
15124f537776SPiotr Zalewski 	vop2_lock(vop2);
15134f537776SPiotr Zalewski 	vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
15144f537776SPiotr Zalewski 	vop2_unlock(vop2);
15154f537776SPiotr Zalewski }
15164f537776SPiotr Zalewski 
vop2_dither_setup(struct drm_crtc * crtc,u32 * dsp_ctrl)1517604be855SAndy Yan static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
1518604be855SAndy Yan {
1519604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1520604be855SAndy Yan 
1521604be855SAndy Yan 	switch (vcstate->bus_format) {
1522604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB565_1X16:
1523604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1524604be855SAndy Yan 		break;
1525604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X18:
1526604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
1527604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
1528604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1529604be855SAndy Yan 		*dsp_ctrl |= RGB888_TO_RGB666;
1530604be855SAndy Yan 		break;
1531604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV8_1X24:
1532604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1533604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1534604be855SAndy Yan 		break;
1535604be855SAndy Yan 	default:
1536604be855SAndy Yan 		break;
1537604be855SAndy Yan 	}
1538604be855SAndy Yan 
1539604be855SAndy Yan 	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
1540604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1541604be855SAndy Yan 
1542604be855SAndy Yan 	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
1543604be855SAndy Yan 				DITHER_DOWN_ALLEGRO);
1544604be855SAndy Yan }
1545604be855SAndy Yan 
vop2_post_config(struct drm_crtc * crtc)1546604be855SAndy Yan static void vop2_post_config(struct drm_crtc *crtc)
1547604be855SAndy Yan {
1548604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1549328e6885SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1550604be855SAndy Yan 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1551604be855SAndy Yan 	u16 vtotal = mode->crtc_vtotal;
1552604be855SAndy Yan 	u16 hdisplay = mode->crtc_hdisplay;
1553604be855SAndy Yan 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1554604be855SAndy Yan 	u16 vdisplay = mode->crtc_vdisplay;
1555604be855SAndy Yan 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1556604be855SAndy Yan 	u32 left_margin = 100, right_margin = 100;
1557604be855SAndy Yan 	u32 top_margin = 100, bottom_margin = 100;
1558604be855SAndy Yan 	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
1559604be855SAndy Yan 	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
1560604be855SAndy Yan 	u16 hact_end, vact_end;
1561604be855SAndy Yan 	u32 val;
1562075a5b39SAndy Yan 
1563328e6885SAndy Yan 	vop2->ops->setup_bg_dly(vp);
1564604be855SAndy Yan 
1565604be855SAndy Yan 	vsize = rounddown(vsize, 2);
1566604be855SAndy Yan 	hsize = rounddown(hsize, 2);
1567604be855SAndy Yan 	hact_st += hdisplay * (100 - left_margin) / 200;
1568604be855SAndy Yan 	hact_end = hact_st + hsize;
1569604be855SAndy Yan 	val = hact_st << 16;
1570604be855SAndy Yan 	val |= hact_end;
1571604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
1572604be855SAndy Yan 	vact_st += vdisplay * (100 - top_margin) / 200;
1573604be855SAndy Yan 	vact_end = vact_st + vsize;
1574604be855SAndy Yan 	val = vact_st << 16;
1575604be855SAndy Yan 	val |= vact_end;
1576604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
1577604be855SAndy Yan 	val = scl_cal_scale2(vdisplay, vsize) << 16;
1578604be855SAndy Yan 	val |= scl_cal_scale2(hdisplay, hsize);
1579604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
1580604be855SAndy Yan 
1581604be855SAndy Yan 	val = 0;
1582604be855SAndy Yan 	if (hdisplay != hsize)
1583604be855SAndy Yan 		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
1584604be855SAndy Yan 	if (vdisplay != vsize)
1585604be855SAndy Yan 		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
1586604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
1587604be855SAndy Yan 
1588604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1589604be855SAndy Yan 		u16 vact_st_f1 = vtotal + vact_st + 1;
1590604be855SAndy Yan 		u16 vact_end_f1 = vact_st_f1 + vsize;
1591604be855SAndy Yan 
1592604be855SAndy Yan 		val = vact_st_f1 << 16 | vact_end_f1;
1593604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
1594604be855SAndy Yan 	}
1595604be855SAndy Yan 
1596604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
1597604be855SAndy Yan }
1598604be855SAndy Yan 
us_to_vertical_line(struct drm_display_mode * mode,int us)1599604be855SAndy Yan static int us_to_vertical_line(struct drm_display_mode *mode, int us)
1600604be855SAndy Yan {
1601604be855SAndy Yan 	return us * mode->clock / mode->htotal / 1000;
1602604be855SAndy Yan }
1603604be855SAndy Yan 
vop2_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)1604604be855SAndy Yan static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
1605604be855SAndy Yan 				    struct drm_atomic_state *state)
1606604be855SAndy Yan {
1607604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1608604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1609604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
1610604be855SAndy Yan 	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
1611604be855SAndy Yan 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1612604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1613604be855SAndy Yan 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1614604be855SAndy Yan 	unsigned long clock = mode->crtc_clock * 1000;
1615604be855SAndy Yan 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
1616604be855SAndy Yan 	u16 hdisplay = mode->crtc_hdisplay;
1617604be855SAndy Yan 	u16 htotal = mode->crtc_htotal;
1618604be855SAndy Yan 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1619604be855SAndy Yan 	u16 hact_end = hact_st + hdisplay;
1620604be855SAndy Yan 	u16 vdisplay = mode->crtc_vdisplay;
1621604be855SAndy Yan 	u16 vtotal = mode->crtc_vtotal;
1622604be855SAndy Yan 	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
1623604be855SAndy Yan 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1624604be855SAndy Yan 	u16 vact_end = vact_st + vdisplay;
1625604be855SAndy Yan 	u8 out_mode;
1626604be855SAndy Yan 	u32 dsp_ctrl = 0;
1627604be855SAndy Yan 	int act_end;
1628604be855SAndy Yan 	u32 val, polflags;
1629604be855SAndy Yan 	int ret;
1630604be855SAndy Yan 	struct drm_encoder *encoder;
1631604be855SAndy Yan 
1632604be855SAndy Yan 	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
1633604be855SAndy Yan 		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
1634604be855SAndy Yan 		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
1635604be855SAndy Yan 
1636604be855SAndy Yan 	vop2_lock(vop2);
1637604be855SAndy Yan 
1638604be855SAndy Yan 	ret = clk_prepare_enable(vp->dclk);
1639604be855SAndy Yan 	if (ret < 0) {
1640604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
1641604be855SAndy Yan 			vp->id, ret);
164298526c5bSDan Carpenter 		vop2_unlock(vop2);
1643604be855SAndy Yan 		return;
1644604be855SAndy Yan 	}
1645604be855SAndy Yan 
1646604be855SAndy Yan 	if (!vop2->enable_count)
1647604be855SAndy Yan 		vop2_enable(vop2);
1648604be855SAndy Yan 
1649604be855SAndy Yan 	vop2->enable_count++;
1650604be855SAndy Yan 
1651dd49ee46SAndy Yan 	vcstate->yuv_overlay = is_yuv_output(vcstate->bus_format);
1652dd49ee46SAndy Yan 
1653604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
1654604be855SAndy Yan 
1655604be855SAndy Yan 	polflags = 0;
1656604be855SAndy Yan 	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
1657604be855SAndy Yan 		polflags |= POLFLAG_DCLK_INV;
1658604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
1659604be855SAndy Yan 		polflags |= BIT(HSYNC_POSITIVE);
1660604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
1661604be855SAndy Yan 		polflags |= BIT(VSYNC_POSITIVE);
1662604be855SAndy Yan 
1663604be855SAndy Yan 	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
1664604be855SAndy Yan 		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
1665604be855SAndy Yan 
16665a028e8fSAndy Yan 		/*
16675a028e8fSAndy Yan 		 * for drive a high resolution(4KP120, 8K), vop on rk3588/rk3576 need
16685a028e8fSAndy Yan 		 * process multi(1/2/4/8) pixels per cycle, so the dclk feed by the
16695a028e8fSAndy Yan 		 * system cru may be the 1/2 or 1/4 of mode->clock.
16705a028e8fSAndy Yan 		 */
1671328e6885SAndy Yan 		clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
1672604be855SAndy Yan 	}
1673604be855SAndy Yan 
1674b6ddaa63SHarshit Mogalapalli 	if (!clock) {
1675b6ddaa63SHarshit Mogalapalli 		vop2_unlock(vop2);
16765a028e8fSAndy Yan 		return;
1677b6ddaa63SHarshit Mogalapalli 	}
16785a028e8fSAndy Yan 
1679604be855SAndy Yan 	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
16809d7fe770SAndy Yan 	    !(vp_data->feature & VOP2_VP_FEATURE_OUTPUT_10BIT))
1681604be855SAndy Yan 		out_mode = ROCKCHIP_OUT_MODE_P888;
1682604be855SAndy Yan 	else
1683604be855SAndy Yan 		out_mode = vcstate->output_mode;
1684604be855SAndy Yan 
1685604be855SAndy Yan 	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
1686604be855SAndy Yan 
1687604be855SAndy Yan 	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
1688604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
16895a028e8fSAndy Yan 	if (vop2_output_rg_swap(vop2, vcstate->bus_format))
16905a028e8fSAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RG_SWAP;
1691604be855SAndy Yan 
1692dd49ee46SAndy Yan 	if (vcstate->yuv_overlay)
1693604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
1694604be855SAndy Yan 
1695604be855SAndy Yan 	vop2_dither_setup(crtc, &dsp_ctrl);
1696604be855SAndy Yan 
1697604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
1698604be855SAndy Yan 	val = hact_st << 16;
1699604be855SAndy Yan 	val |= hact_end;
1700604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
1701604be855SAndy Yan 
1702604be855SAndy Yan 	val = vact_st << 16;
1703604be855SAndy Yan 	val |= vact_end;
1704604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
1705604be855SAndy Yan 
1706604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1707604be855SAndy Yan 		u16 vact_st_f1 = vtotal + vact_st + 1;
1708604be855SAndy Yan 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
1709604be855SAndy Yan 
1710604be855SAndy Yan 		val = vact_st_f1 << 16 | vact_end_f1;
1711604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
1712604be855SAndy Yan 
1713604be855SAndy Yan 		val = vtotal << 16 | (vtotal + vsync_len);
1714604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
1715604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
1716604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
1717604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
1718604be855SAndy Yan 		vtotal += vtotal + 1;
1719604be855SAndy Yan 		act_end = vact_end_f1;
1720604be855SAndy Yan 	} else {
1721604be855SAndy Yan 		act_end = vact_end;
1722604be855SAndy Yan 	}
1723604be855SAndy Yan 
1724604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
1725604be855SAndy Yan 		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
1726604be855SAndy Yan 
1727604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
1728604be855SAndy Yan 
1729604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
1730604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
1731604be855SAndy Yan 		clock *= 2;
1732604be855SAndy Yan 	}
1733604be855SAndy Yan 
1734604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
1735604be855SAndy Yan 
17362c1268e7SCristian Ciocaltea 	/*
17372c1268e7SCristian Ciocaltea 	 * Switch to HDMI PHY PLL as DCLK source for display modes up
17382c1268e7SCristian Ciocaltea 	 * to 4K@60Hz, if available, otherwise keep using the system CRU.
17392c1268e7SCristian Ciocaltea 	 */
1740f8dd7fc9SCristian Ciocaltea 	if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) {
17412c1268e7SCristian Ciocaltea 		drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
17422c1268e7SCristian Ciocaltea 			struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
17432c1268e7SCristian Ciocaltea 
17442c1268e7SCristian Ciocaltea 			if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
1745f8dd7fc9SCristian Ciocaltea 				if (!vop2->pll_hdmiphy0)
1746f8dd7fc9SCristian Ciocaltea 					break;
1747f8dd7fc9SCristian Ciocaltea 
17482c1268e7SCristian Ciocaltea 				if (!vp->dclk_src)
17492c1268e7SCristian Ciocaltea 					vp->dclk_src = clk_get_parent(vp->dclk);
17502c1268e7SCristian Ciocaltea 
17512c1268e7SCristian Ciocaltea 				ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
17522c1268e7SCristian Ciocaltea 				if (ret < 0)
17532c1268e7SCristian Ciocaltea 					drm_warn(vop2->drm,
17542c1268e7SCristian Ciocaltea 						 "Could not switch to HDMI0 PHY PLL: %d\n", ret);
17552c1268e7SCristian Ciocaltea 				break;
17562c1268e7SCristian Ciocaltea 			}
1757f8dd7fc9SCristian Ciocaltea 
1758f8dd7fc9SCristian Ciocaltea 			if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
1759f8dd7fc9SCristian Ciocaltea 				if (!vop2->pll_hdmiphy1)
1760f8dd7fc9SCristian Ciocaltea 					break;
1761f8dd7fc9SCristian Ciocaltea 
1762f8dd7fc9SCristian Ciocaltea 				if (!vp->dclk_src)
1763f8dd7fc9SCristian Ciocaltea 					vp->dclk_src = clk_get_parent(vp->dclk);
1764f8dd7fc9SCristian Ciocaltea 
1765f8dd7fc9SCristian Ciocaltea 				ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
1766f8dd7fc9SCristian Ciocaltea 				if (ret < 0)
1767f8dd7fc9SCristian Ciocaltea 					drm_warn(vop2->drm,
1768f8dd7fc9SCristian Ciocaltea 						 "Could not switch to HDMI1 PHY PLL: %d\n", ret);
1769f8dd7fc9SCristian Ciocaltea 				break;
1770f8dd7fc9SCristian Ciocaltea 			}
17712c1268e7SCristian Ciocaltea 		}
17722c1268e7SCristian Ciocaltea 	}
17732c1268e7SCristian Ciocaltea 
1774604be855SAndy Yan 	clk_set_rate(vp->dclk, clock);
1775604be855SAndy Yan 
1776604be855SAndy Yan 	vop2_post_config(crtc);
1777604be855SAndy Yan 
1778604be855SAndy Yan 	vop2_cfg_done(vp);
1779604be855SAndy Yan 
1780604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
1781604be855SAndy Yan 
17824f537776SPiotr Zalewski 	vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
17834f537776SPiotr Zalewski 
1784604be855SAndy Yan 	drm_crtc_vblank_on(crtc);
1785604be855SAndy Yan 
1786604be855SAndy Yan 	vop2_unlock(vop2);
1787604be855SAndy Yan }
1788604be855SAndy Yan 
vop2_crtc_atomic_check_gamma(struct vop2_video_port * vp,struct drm_crtc * crtc,struct drm_atomic_state * state,struct drm_crtc_state * crtc_state)17894f537776SPiotr Zalewski static int vop2_crtc_atomic_check_gamma(struct vop2_video_port *vp,
17904f537776SPiotr Zalewski 					struct drm_crtc *crtc,
17914f537776SPiotr Zalewski 					struct drm_atomic_state *state,
17924f537776SPiotr Zalewski 					struct drm_crtc_state *crtc_state)
17934f537776SPiotr Zalewski {
17944f537776SPiotr Zalewski 	struct vop2 *vop2 = vp->vop2;
17954f537776SPiotr Zalewski 	unsigned int len;
17964f537776SPiotr Zalewski 
17974f537776SPiotr Zalewski 	if (!vp->vop2->lut_regs || !crtc_state->color_mgmt_changed ||
17984f537776SPiotr Zalewski 	    !crtc_state->gamma_lut)
17994f537776SPiotr Zalewski 		return 0;
18004f537776SPiotr Zalewski 
18014f537776SPiotr Zalewski 	len = drm_color_lut_size(crtc_state->gamma_lut);
18024f537776SPiotr Zalewski 	if (len != crtc->gamma_size) {
18034f537776SPiotr Zalewski 		drm_dbg(vop2->drm, "Invalid LUT size; got %d, expected %d\n",
18044f537776SPiotr Zalewski 			len, crtc->gamma_size);
18054f537776SPiotr Zalewski 		return -EINVAL;
18064f537776SPiotr Zalewski 	}
18074f537776SPiotr Zalewski 
18084f537776SPiotr Zalewski 	if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) {
18094f537776SPiotr Zalewski 		drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n");
18104f537776SPiotr Zalewski 		return -EINVAL;
18114f537776SPiotr Zalewski 	}
18124f537776SPiotr Zalewski 
18134f537776SPiotr Zalewski 	return 0;
18144f537776SPiotr Zalewski }
18154f537776SPiotr Zalewski 
vop2_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)1816604be855SAndy Yan static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
1817604be855SAndy Yan 				  struct drm_atomic_state *state)
1818604be855SAndy Yan {
1819604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1820604be855SAndy Yan 	struct drm_plane *plane;
1821604be855SAndy Yan 	int nplanes = 0;
1822604be855SAndy Yan 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
18234f537776SPiotr Zalewski 	int ret;
18244f537776SPiotr Zalewski 
18254f537776SPiotr Zalewski 	ret = vop2_crtc_atomic_check_gamma(vp, crtc, state, crtc_state);
18264f537776SPiotr Zalewski 	if (ret)
18274f537776SPiotr Zalewski 		return ret;
1828604be855SAndy Yan 
1829604be855SAndy Yan 	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
1830604be855SAndy Yan 		nplanes++;
1831604be855SAndy Yan 
1832604be855SAndy Yan 	if (nplanes > vp->nlayers)
1833604be855SAndy Yan 		return -EINVAL;
1834604be855SAndy Yan 
1835604be855SAndy Yan 	return 0;
1836604be855SAndy Yan }
1837604be855SAndy Yan 
vop2_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)1838604be855SAndy Yan static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
1839604be855SAndy Yan 				   struct drm_atomic_state *state)
1840604be855SAndy Yan {
1841604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1842604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1843604be855SAndy Yan 
1844328e6885SAndy Yan 	vop2->ops->setup_overlay(vp);
1845604be855SAndy Yan }
1846604be855SAndy Yan 
vop2_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)1847604be855SAndy Yan static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
1848604be855SAndy Yan 				   struct drm_atomic_state *state)
1849604be855SAndy Yan {
18504f537776SPiotr Zalewski 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1851604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
18524f537776SPiotr Zalewski 	struct vop2 *vop2 = vp->vop2;
18534f537776SPiotr Zalewski 
18544f537776SPiotr Zalewski 	/* In case of modeset, gamma lut update already happened in atomic enable */
18559c22b6ecSPiotr Zalewski 	if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed)
18564f537776SPiotr Zalewski 		vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state);
1857604be855SAndy Yan 
1858604be855SAndy Yan 	vop2_post_config(crtc);
1859604be855SAndy Yan 
1860604be855SAndy Yan 	vop2_cfg_done(vp);
1861604be855SAndy Yan 
1862604be855SAndy Yan 	spin_lock_irq(&crtc->dev->event_lock);
1863604be855SAndy Yan 
1864604be855SAndy Yan 	if (crtc->state->event) {
1865604be855SAndy Yan 		WARN_ON(drm_crtc_vblank_get(crtc));
1866604be855SAndy Yan 		vp->event = crtc->state->event;
1867604be855SAndy Yan 		crtc->state->event = NULL;
1868604be855SAndy Yan 	}
1869604be855SAndy Yan 
1870604be855SAndy Yan 	spin_unlock_irq(&crtc->dev->event_lock);
1871604be855SAndy Yan }
1872604be855SAndy Yan 
1873604be855SAndy Yan static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
1874604be855SAndy Yan 	.mode_fixup = vop2_crtc_mode_fixup,
1875604be855SAndy Yan 	.atomic_check = vop2_crtc_atomic_check,
1876604be855SAndy Yan 	.atomic_begin = vop2_crtc_atomic_begin,
1877604be855SAndy Yan 	.atomic_flush = vop2_crtc_atomic_flush,
1878604be855SAndy Yan 	.atomic_enable = vop2_crtc_atomic_enable,
1879604be855SAndy Yan 	.atomic_disable = vop2_crtc_atomic_disable,
1880604be855SAndy Yan };
1881604be855SAndy Yan 
vop2_dump_connector_on_crtc(struct drm_crtc * crtc,struct seq_file * s)188277996455SAndy Yan static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s)
188377996455SAndy Yan {
188477996455SAndy Yan 	struct drm_connector_list_iter conn_iter;
188577996455SAndy Yan 	struct drm_connector *connector;
188677996455SAndy Yan 
188777996455SAndy Yan 	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
188877996455SAndy Yan 	drm_for_each_connector_iter(connector, &conn_iter) {
188977996455SAndy Yan 		if (crtc->state->connector_mask & drm_connector_mask(connector))
189077996455SAndy Yan 			seq_printf(s, "    Connector: %s\n", connector->name);
189177996455SAndy Yan 	}
189277996455SAndy Yan 	drm_connector_list_iter_end(&conn_iter);
189377996455SAndy Yan }
189477996455SAndy Yan 
vop2_plane_state_dump(struct seq_file * s,struct drm_plane * plane)189577996455SAndy Yan static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane)
189677996455SAndy Yan {
189777996455SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
189877996455SAndy Yan 	struct drm_plane_state *pstate = plane->state;
189977996455SAndy Yan 	struct drm_rect *src, *dst;
190077996455SAndy Yan 	struct drm_framebuffer *fb;
190177996455SAndy Yan 	struct drm_gem_object *obj;
190277996455SAndy Yan 	struct rockchip_gem_object *rk_obj;
190377996455SAndy Yan 	bool xmirror;
190477996455SAndy Yan 	bool ymirror;
190577996455SAndy Yan 	bool rotate_270;
190677996455SAndy Yan 	bool rotate_90;
190777996455SAndy Yan 	dma_addr_t fb_addr;
190877996455SAndy Yan 	int i;
190977996455SAndy Yan 
191077996455SAndy Yan 	seq_printf(s, "    %s: %s\n", win->data->name, !pstate ?
191177996455SAndy Yan 		   "DISABLED" : pstate->crtc ? "ACTIVE" : "DISABLED");
191277996455SAndy Yan 
191377996455SAndy Yan 	if (!pstate || !pstate->fb)
191477996455SAndy Yan 		return 0;
191577996455SAndy Yan 
191677996455SAndy Yan 	fb = pstate->fb;
191777996455SAndy Yan 	src = &pstate->src;
191877996455SAndy Yan 	dst = &pstate->dst;
191977996455SAndy Yan 	xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
192077996455SAndy Yan 	ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
192177996455SAndy Yan 	rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
192277996455SAndy Yan 	rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
192377996455SAndy Yan 
192477996455SAndy Yan 	seq_printf(s, "\twin_id: %d\n", win->win_id);
192577996455SAndy Yan 
192677996455SAndy Yan 	seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n",
192777996455SAndy Yan 		   &fb->format->format,
192877996455SAndy Yan 		   drm_is_afbc(fb->modifier) ? "[AFBC]" : "",
192977996455SAndy Yan 		   pstate->alpha >> 8);
193077996455SAndy Yan 	seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n",
193177996455SAndy Yan 		   xmirror, ymirror, rotate_90, rotate_270);
193277996455SAndy Yan 	seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos);
193377996455SAndy Yan 	seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16,
193477996455SAndy Yan 		   src->y1 >> 16, drm_rect_width(src) >> 16,
193577996455SAndy Yan 		   drm_rect_height(src) >> 16);
193677996455SAndy Yan 	seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1,
193777996455SAndy Yan 		   drm_rect_width(dst), drm_rect_height(dst));
193877996455SAndy Yan 
193977996455SAndy Yan 	for (i = 0; i < fb->format->num_planes; i++) {
194077996455SAndy Yan 		obj = fb->obj[i];
194177996455SAndy Yan 		rk_obj = to_rockchip_obj(obj);
194277996455SAndy Yan 		fb_addr = rk_obj->dma_addr + fb->offsets[i];
194377996455SAndy Yan 
194477996455SAndy Yan 		seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n",
194577996455SAndy Yan 			   i, &fb_addr, fb->pitches[i], fb->offsets[i]);
194677996455SAndy Yan 	}
194777996455SAndy Yan 
194877996455SAndy Yan 	return 0;
194977996455SAndy Yan }
195077996455SAndy Yan 
vop2_crtc_state_dump(struct drm_crtc * crtc,struct seq_file * s)195177996455SAndy Yan static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s)
195277996455SAndy Yan {
195377996455SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
195477996455SAndy Yan 	struct drm_crtc_state *cstate = crtc->state;
195577996455SAndy Yan 	struct rockchip_crtc_state *vcstate;
195677996455SAndy Yan 	struct drm_display_mode *mode;
195777996455SAndy Yan 	struct drm_plane *plane;
195877996455SAndy Yan 	bool interlaced;
195977996455SAndy Yan 
196077996455SAndy Yan 	seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ?
196177996455SAndy Yan 		   "DISABLED" : cstate->active ? "ACTIVE" : "DISABLED");
196277996455SAndy Yan 
196377996455SAndy Yan 	if (!cstate || !cstate->active)
196477996455SAndy Yan 		return 0;
196577996455SAndy Yan 
196677996455SAndy Yan 	mode = &crtc->state->adjusted_mode;
196777996455SAndy Yan 	vcstate = to_rockchip_crtc_state(cstate);
196877996455SAndy Yan 	interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
196977996455SAndy Yan 
197077996455SAndy Yan 	vop2_dump_connector_on_crtc(crtc, s);
197177996455SAndy Yan 	seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format,
197277996455SAndy Yan 		   drm_get_bus_format_name(vcstate->bus_format));
197377996455SAndy Yan 	seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode);
197477996455SAndy Yan 	seq_printf(s, " color_space[%d]\n", vcstate->color_space);
197577996455SAndy Yan 	seq_printf(s, "    Display mode: %dx%d%s%d\n",
197677996455SAndy Yan 		   mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p",
197777996455SAndy Yan 		   drm_mode_vrefresh(mode));
197877996455SAndy Yan 	seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n",
197977996455SAndy Yan 		   mode->clock, mode->crtc_clock, mode->type, mode->flags);
198077996455SAndy Yan 	seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start,
198177996455SAndy Yan 		   mode->hsync_end, mode->htotal);
198277996455SAndy Yan 	seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start,
198377996455SAndy Yan 		   mode->vsync_end, mode->vtotal);
198477996455SAndy Yan 
198577996455SAndy Yan 	drm_atomic_crtc_for_each_plane(plane, crtc) {
198677996455SAndy Yan 		vop2_plane_state_dump(s, plane);
198777996455SAndy Yan 	}
198877996455SAndy Yan 
198977996455SAndy Yan 	return 0;
199077996455SAndy Yan }
199177996455SAndy Yan 
vop2_summary_show(struct seq_file * s,void * data)199277996455SAndy Yan static int vop2_summary_show(struct seq_file *s, void *data)
199377996455SAndy Yan {
199477996455SAndy Yan 	struct drm_info_node *node = s->private;
199577996455SAndy Yan 	struct drm_minor *minor = node->minor;
199677996455SAndy Yan 	struct drm_device *drm_dev = minor->dev;
199777996455SAndy Yan 	struct drm_crtc *crtc;
199877996455SAndy Yan 
199977996455SAndy Yan 	drm_modeset_lock_all(drm_dev);
200077996455SAndy Yan 	drm_for_each_crtc(crtc, drm_dev) {
200177996455SAndy Yan 		vop2_crtc_state_dump(crtc, s);
200277996455SAndy Yan 	}
200377996455SAndy Yan 	drm_modeset_unlock_all(drm_dev);
200477996455SAndy Yan 
200577996455SAndy Yan 	return 0;
200677996455SAndy Yan }
200777996455SAndy Yan 
vop2_regs_print(struct vop2 * vop2,struct seq_file * s,const struct vop2_regs_dump * dump,bool active_only)200877996455SAndy Yan static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s,
200977996455SAndy Yan 			    const struct vop2_regs_dump *dump, bool active_only)
201077996455SAndy Yan {
201177996455SAndy Yan 	resource_size_t start;
201277996455SAndy Yan 	u32 val;
201377996455SAndy Yan 	int i;
201477996455SAndy Yan 
201577996455SAndy Yan 	if (dump->en_mask && active_only) {
201677996455SAndy Yan 		val = vop2_readl(vop2, dump->base + dump->en_reg);
201777996455SAndy Yan 		if ((val & dump->en_mask) != dump->en_val)
201877996455SAndy Yan 			return;
201977996455SAndy Yan 	}
202077996455SAndy Yan 
202177996455SAndy Yan 	seq_printf(s, "\n%s:\n", dump->name);
202277996455SAndy Yan 
202377996455SAndy Yan 	start = vop2->res->start + dump->base;
202477996455SAndy Yan 	for (i = 0; i < dump->size >> 2; i += 4) {
202577996455SAndy Yan 		seq_printf(s, "%08x:  %08x %08x %08x %08x\n", (u32)start + i * 4,
202677996455SAndy Yan 			   vop2_readl(vop2, dump->base + (4 * i)),
202777996455SAndy Yan 			   vop2_readl(vop2, dump->base + (4 * (i + 1))),
202877996455SAndy Yan 			   vop2_readl(vop2, dump->base + (4 * (i + 2))),
202977996455SAndy Yan 			   vop2_readl(vop2, dump->base + (4 * (i + 3))));
203077996455SAndy Yan 	}
203177996455SAndy Yan }
203277996455SAndy Yan 
__vop2_regs_dump(struct seq_file * s,bool active_only)203377996455SAndy Yan static void __vop2_regs_dump(struct seq_file *s, bool active_only)
203477996455SAndy Yan {
203577996455SAndy Yan 	struct drm_info_node *node = s->private;
203677996455SAndy Yan 	struct vop2 *vop2 = node->info_ent->data;
203777996455SAndy Yan 	struct drm_minor *minor = node->minor;
203877996455SAndy Yan 	struct drm_device *drm_dev = minor->dev;
203977996455SAndy Yan 	const struct vop2_regs_dump *dump;
204077996455SAndy Yan 	unsigned int i;
204177996455SAndy Yan 
204277996455SAndy Yan 	drm_modeset_lock_all(drm_dev);
204377996455SAndy Yan 
204477996455SAndy Yan 	regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
204577996455SAndy Yan 
204677996455SAndy Yan 	if (vop2->enable_count) {
204777996455SAndy Yan 		for (i = 0; i < vop2->data->regs_dump_size; i++) {
204877996455SAndy Yan 			dump = &vop2->data->regs_dump[i];
204977996455SAndy Yan 			vop2_regs_print(vop2, s, dump, active_only);
205077996455SAndy Yan 		}
205177996455SAndy Yan 	} else {
205277996455SAndy Yan 		seq_puts(s, "VOP disabled\n");
205377996455SAndy Yan 	}
205477996455SAndy Yan 	drm_modeset_unlock_all(drm_dev);
205577996455SAndy Yan }
205677996455SAndy Yan 
vop2_regs_show(struct seq_file * s,void * arg)205777996455SAndy Yan static int vop2_regs_show(struct seq_file *s, void *arg)
205877996455SAndy Yan {
205977996455SAndy Yan 	__vop2_regs_dump(s, false);
206077996455SAndy Yan 
206177996455SAndy Yan 	return 0;
206277996455SAndy Yan }
206377996455SAndy Yan 
vop2_active_regs_show(struct seq_file * s,void * data)206477996455SAndy Yan static int vop2_active_regs_show(struct seq_file *s, void *data)
206577996455SAndy Yan {
206677996455SAndy Yan 	__vop2_regs_dump(s, true);
206777996455SAndy Yan 
206877996455SAndy Yan 	return 0;
206977996455SAndy Yan }
207077996455SAndy Yan 
207177996455SAndy Yan static struct drm_info_list vop2_debugfs_list[] = {
207277996455SAndy Yan 	{ "summary", vop2_summary_show, 0, NULL },
207377996455SAndy Yan 	{ "active_regs", vop2_active_regs_show,   0, NULL },
207477996455SAndy Yan 	{ "regs", vop2_regs_show,   0, NULL },
207577996455SAndy Yan };
207677996455SAndy Yan 
vop2_debugfs_init(struct vop2 * vop2,struct drm_minor * minor)207777996455SAndy Yan static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor)
207877996455SAndy Yan {
207977996455SAndy Yan 	struct dentry *root;
208077996455SAndy Yan 	unsigned int i;
208177996455SAndy Yan 
208277996455SAndy Yan 	root = debugfs_create_dir("vop2", minor->debugfs_root);
208377996455SAndy Yan 	if (!IS_ERR(root)) {
208477996455SAndy Yan 		for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++)
208577996455SAndy Yan 			vop2_debugfs_list[i].data = vop2;
208677996455SAndy Yan 
208777996455SAndy Yan 		drm_debugfs_create_files(vop2_debugfs_list,
208877996455SAndy Yan 					 ARRAY_SIZE(vop2_debugfs_list),
208977996455SAndy Yan 					 root, minor);
209077996455SAndy Yan 	}
209177996455SAndy Yan }
209277996455SAndy Yan 
vop2_crtc_late_register(struct drm_crtc * crtc)209377996455SAndy Yan static int vop2_crtc_late_register(struct drm_crtc *crtc)
209477996455SAndy Yan {
209577996455SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
209677996455SAndy Yan 	struct vop2 *vop2 = vp->vop2;
209777996455SAndy Yan 
209877996455SAndy Yan 	if (drm_crtc_index(crtc) == 0)
209977996455SAndy Yan 		vop2_debugfs_init(vop2, crtc->dev->primary);
210077996455SAndy Yan 
210177996455SAndy Yan 	return 0;
210277996455SAndy Yan }
210377996455SAndy Yan 
vop2_crtc_duplicate_state(struct drm_crtc * crtc)2104604be855SAndy Yan static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
2105604be855SAndy Yan {
2106342f7e49SJonas Karlman 	struct rockchip_crtc_state *vcstate;
2107604be855SAndy Yan 
2108342f7e49SJonas Karlman 	if (WARN_ON(!crtc->state))
2109342f7e49SJonas Karlman 		return NULL;
2110604be855SAndy Yan 
2111342f7e49SJonas Karlman 	vcstate = kmemdup(to_rockchip_crtc_state(crtc->state),
2112342f7e49SJonas Karlman 			  sizeof(*vcstate), GFP_KERNEL);
2113604be855SAndy Yan 	if (!vcstate)
2114604be855SAndy Yan 		return NULL;
2115604be855SAndy Yan 
2116604be855SAndy Yan 	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
2117604be855SAndy Yan 
2118604be855SAndy Yan 	return &vcstate->base;
2119604be855SAndy Yan }
2120604be855SAndy Yan 
vop2_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)2121604be855SAndy Yan static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
2122604be855SAndy Yan 				    struct drm_crtc_state *state)
2123604be855SAndy Yan {
2124604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
2125604be855SAndy Yan 
2126604be855SAndy Yan 	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
2127604be855SAndy Yan 	kfree(vcstate);
2128604be855SAndy Yan }
2129604be855SAndy Yan 
vop2_crtc_reset(struct drm_crtc * crtc)21304d49d87bSJonas Karlman static void vop2_crtc_reset(struct drm_crtc *crtc)
21314d49d87bSJonas Karlman {
21324d49d87bSJonas Karlman 	struct rockchip_crtc_state *vcstate =
21334d49d87bSJonas Karlman 		kzalloc(sizeof(*vcstate), GFP_KERNEL);
21344d49d87bSJonas Karlman 
21354d49d87bSJonas Karlman 	if (crtc->state)
21364d49d87bSJonas Karlman 		vop2_crtc_destroy_state(crtc, crtc->state);
21374d49d87bSJonas Karlman 
21384d49d87bSJonas Karlman 	if (vcstate)
21394d49d87bSJonas Karlman 		__drm_atomic_helper_crtc_reset(crtc, &vcstate->base);
21404d49d87bSJonas Karlman 	else
21414d49d87bSJonas Karlman 		__drm_atomic_helper_crtc_reset(crtc, NULL);
21424d49d87bSJonas Karlman }
21434d49d87bSJonas Karlman 
2144604be855SAndy Yan static const struct drm_crtc_funcs vop2_crtc_funcs = {
2145604be855SAndy Yan 	.set_config = drm_atomic_helper_set_config,
2146604be855SAndy Yan 	.page_flip = drm_atomic_helper_page_flip,
2147604be855SAndy Yan 	.destroy = drm_crtc_cleanup,
2148604be855SAndy Yan 	.reset = vop2_crtc_reset,
2149604be855SAndy Yan 	.atomic_duplicate_state = vop2_crtc_duplicate_state,
2150604be855SAndy Yan 	.atomic_destroy_state = vop2_crtc_destroy_state,
2151604be855SAndy Yan 	.enable_vblank = vop2_crtc_enable_vblank,
2152604be855SAndy Yan 	.disable_vblank = vop2_crtc_disable_vblank,
215377996455SAndy Yan 	.late_register = vop2_crtc_late_register,
2154604be855SAndy Yan };
2155604be855SAndy Yan 
rk3576_vp_isr(int irq,void * data)2156944757a4SAndy Yan static irqreturn_t rk3576_vp_isr(int irq, void *data)
2157944757a4SAndy Yan {
2158944757a4SAndy Yan 	struct vop2_video_port *vp = data;
2159944757a4SAndy Yan 	struct vop2 *vop2 = vp->vop2;
2160944757a4SAndy Yan 	struct drm_crtc *crtc = &vp->crtc;
2161944757a4SAndy Yan 	uint32_t irqs;
2162944757a4SAndy Yan 	int ret = IRQ_NONE;
2163944757a4SAndy Yan 
2164944757a4SAndy Yan 	if (!pm_runtime_get_if_in_use(vop2->dev))
2165944757a4SAndy Yan 		return IRQ_NONE;
2166944757a4SAndy Yan 
2167944757a4SAndy Yan 	irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
2168944757a4SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
2169944757a4SAndy Yan 
2170944757a4SAndy Yan 	if (irqs & VP_INT_DSP_HOLD_VALID) {
2171944757a4SAndy Yan 		complete(&vp->dsp_hold_completion);
2172944757a4SAndy Yan 		ret = IRQ_HANDLED;
2173944757a4SAndy Yan 	}
2174944757a4SAndy Yan 
2175944757a4SAndy Yan 	if (irqs & VP_INT_FS_FIELD) {
2176944757a4SAndy Yan 		drm_crtc_handle_vblank(crtc);
2177944757a4SAndy Yan 		spin_lock(&crtc->dev->event_lock);
2178944757a4SAndy Yan 		if (vp->event) {
2179944757a4SAndy Yan 			u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
2180944757a4SAndy Yan 
2181944757a4SAndy Yan 			if (!(val & BIT(vp->id))) {
2182944757a4SAndy Yan 				drm_crtc_send_vblank_event(crtc, vp->event);
2183944757a4SAndy Yan 				vp->event = NULL;
2184944757a4SAndy Yan 				drm_crtc_vblank_put(crtc);
2185944757a4SAndy Yan 			}
2186944757a4SAndy Yan 		}
2187944757a4SAndy Yan 		spin_unlock(&crtc->dev->event_lock);
2188944757a4SAndy Yan 
2189944757a4SAndy Yan 		ret = IRQ_HANDLED;
2190944757a4SAndy Yan 	}
2191944757a4SAndy Yan 
2192944757a4SAndy Yan 	if (irqs & VP_INT_POST_BUF_EMPTY) {
2193944757a4SAndy Yan 		drm_err_ratelimited(vop2->drm, "POST_BUF_EMPTY irq err at vp%d\n", vp->id);
2194944757a4SAndy Yan 		ret = IRQ_HANDLED;
2195944757a4SAndy Yan 	}
2196944757a4SAndy Yan 
2197944757a4SAndy Yan 	pm_runtime_put(vop2->dev);
2198944757a4SAndy Yan 
2199944757a4SAndy Yan 	return ret;
2200944757a4SAndy Yan }
2201944757a4SAndy Yan 
vop2_isr(int irq,void * data)2202604be855SAndy Yan static irqreturn_t vop2_isr(int irq, void *data)
2203604be855SAndy Yan {
2204604be855SAndy Yan 	struct vop2 *vop2 = data;
2205604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2206604be855SAndy Yan 	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
2207604be855SAndy Yan 	int ret = IRQ_NONE;
2208604be855SAndy Yan 	int i;
2209604be855SAndy Yan 
2210604be855SAndy Yan 	/*
2211604be855SAndy Yan 	 * The irq is shared with the iommu. If the runtime-pm state of the
2212604be855SAndy Yan 	 * vop2-device is disabled the irq has to be targeted at the iommu.
2213604be855SAndy Yan 	 */
2214604be855SAndy Yan 	if (!pm_runtime_get_if_in_use(vop2->dev))
2215604be855SAndy Yan 		return IRQ_NONE;
2216604be855SAndy Yan 
2217944757a4SAndy Yan 	if (vop2->version < VOP_VERSION_RK3576) {
2218604be855SAndy Yan 		for (i = 0; i < vop2_data->nr_vps; i++) {
2219604be855SAndy Yan 			struct vop2_video_port *vp = &vop2->vps[i];
2220604be855SAndy Yan 			struct drm_crtc *crtc = &vp->crtc;
2221604be855SAndy Yan 			u32 irqs;
2222604be855SAndy Yan 
2223604be855SAndy Yan 			irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
2224604be855SAndy Yan 			vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
2225604be855SAndy Yan 
2226604be855SAndy Yan 			if (irqs & VP_INT_DSP_HOLD_VALID) {
2227604be855SAndy Yan 				complete(&vp->dsp_hold_completion);
2228604be855SAndy Yan 				ret = IRQ_HANDLED;
2229604be855SAndy Yan 			}
2230604be855SAndy Yan 
2231604be855SAndy Yan 			if (irqs & VP_INT_FS_FIELD) {
2232604be855SAndy Yan 				drm_crtc_handle_vblank(crtc);
2233604be855SAndy Yan 				spin_lock(&crtc->dev->event_lock);
2234604be855SAndy Yan 				if (vp->event) {
2235604be855SAndy Yan 					u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
2236604be855SAndy Yan 
2237604be855SAndy Yan 					if (!(val & BIT(vp->id))) {
2238604be855SAndy Yan 						drm_crtc_send_vblank_event(crtc, vp->event);
2239604be855SAndy Yan 						vp->event = NULL;
2240604be855SAndy Yan 						drm_crtc_vblank_put(crtc);
2241604be855SAndy Yan 					}
2242604be855SAndy Yan 				}
2243604be855SAndy Yan 				spin_unlock(&crtc->dev->event_lock);
2244604be855SAndy Yan 
2245604be855SAndy Yan 				ret = IRQ_HANDLED;
2246604be855SAndy Yan 			}
2247604be855SAndy Yan 
2248604be855SAndy Yan 			if (irqs & VP_INT_POST_BUF_EMPTY) {
2249604be855SAndy Yan 				drm_err_ratelimited(vop2->drm,
2250604be855SAndy Yan 						    "POST_BUF_EMPTY irq err at vp%d\n",
2251604be855SAndy Yan 						    vp->id);
2252604be855SAndy Yan 				ret = IRQ_HANDLED;
2253604be855SAndy Yan 			}
2254604be855SAndy Yan 		}
2255944757a4SAndy Yan 	}
2256604be855SAndy Yan 
2257604be855SAndy Yan 	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
2258604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
2259604be855SAndy Yan 	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
2260604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
2261604be855SAndy Yan 
2262604be855SAndy Yan 	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
2263604be855SAndy Yan 		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
2264604be855SAndy Yan 			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
2265604be855SAndy Yan 			ret = IRQ_HANDLED;
2266604be855SAndy Yan 		}
2267604be855SAndy Yan 	}
2268604be855SAndy Yan 
2269604be855SAndy Yan 	pm_runtime_put(vop2->dev);
2270604be855SAndy Yan 
2271604be855SAndy Yan 	return ret;
2272604be855SAndy Yan }
2273604be855SAndy Yan 
vop2_plane_init(struct vop2 * vop2,struct vop2_win * win,unsigned long possible_crtcs)2274604be855SAndy Yan static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
2275604be855SAndy Yan 			   unsigned long possible_crtcs)
2276604be855SAndy Yan {
2277604be855SAndy Yan 	const struct vop2_win_data *win_data = win->data;
2278604be855SAndy Yan 	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
2279604be855SAndy Yan 				  BIT(DRM_MODE_BLEND_PREMULTI) |
2280604be855SAndy Yan 				  BIT(DRM_MODE_BLEND_COVERAGE);
2281604be855SAndy Yan 	int ret;
2282604be855SAndy Yan 
2283604be855SAndy Yan 	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
2284604be855SAndy Yan 				       &vop2_plane_funcs, win_data->formats,
2285604be855SAndy Yan 				       win_data->nformats,
2286604be855SAndy Yan 				       win_data->format_modifiers,
2287604be855SAndy Yan 				       win->type, win_data->name);
2288604be855SAndy Yan 	if (ret) {
2289604be855SAndy Yan 		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
2290604be855SAndy Yan 		return ret;
2291604be855SAndy Yan 	}
2292604be855SAndy Yan 
2293604be855SAndy Yan 	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
2294604be855SAndy Yan 
2295604be855SAndy Yan 	if (win->data->supported_rotations)
2296604be855SAndy Yan 		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
2297604be855SAndy Yan 						   DRM_MODE_ROTATE_0 |
2298604be855SAndy Yan 						   win->data->supported_rotations);
2299604be855SAndy Yan 	drm_plane_create_alpha_property(&win->base);
2300604be855SAndy Yan 	drm_plane_create_blend_mode_property(&win->base, blend_caps);
2301604be855SAndy Yan 	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
2302604be855SAndy Yan 				       vop2->registered_num_wins - 1);
2303604be855SAndy Yan 
2304604be855SAndy Yan 	return 0;
2305604be855SAndy Yan }
2306604be855SAndy Yan 
23076fd4f8a2SAndy Yan /*
23086fd4f8a2SAndy Yan  * On RK3566 these windows don't have an independent
23096fd4f8a2SAndy Yan  * framebuffer. They can only share/mirror the framebuffer
23106fd4f8a2SAndy Yan  * with smart0, esmart0 and cluster0 respectively.
23116fd4f8a2SAndy Yan  * And RK3566 share the same vop version with Rk3568, so we
23126fd4f8a2SAndy Yan  * need to use soc_id for identification here.
23136fd4f8a2SAndy Yan  */
vop2_is_mirror_win(struct vop2_win * win)23146fd4f8a2SAndy Yan static bool vop2_is_mirror_win(struct vop2_win *win)
2315604be855SAndy Yan {
23166fd4f8a2SAndy Yan 	struct vop2 *vop2 = win->vop2;
2317604be855SAndy Yan 
23186fd4f8a2SAndy Yan 	if (vop2->data->soc_id == 3566) {
23196fd4f8a2SAndy Yan 		switch (win->data->phys_id) {
23206fd4f8a2SAndy Yan 		case ROCKCHIP_VOP2_SMART1:
23216fd4f8a2SAndy Yan 		case ROCKCHIP_VOP2_ESMART1:
23226fd4f8a2SAndy Yan 		case ROCKCHIP_VOP2_CLUSTER1:
23236fd4f8a2SAndy Yan 			return true;
23246fd4f8a2SAndy Yan 		default:
23256fd4f8a2SAndy Yan 			return false;
2326604be855SAndy Yan 		}
23276fd4f8a2SAndy Yan 	} else {
23286fd4f8a2SAndy Yan 		return false;
23296fd4f8a2SAndy Yan 	}
2330604be855SAndy Yan }
2331604be855SAndy Yan 
vop2_create_crtcs(struct vop2 * vop2)2332cddddc06SMichael Riesch static int vop2_create_crtcs(struct vop2 *vop2)
2333604be855SAndy Yan {
2334604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2335604be855SAndy Yan 	struct drm_device *drm = vop2->drm;
2336604be855SAndy Yan 	struct device *dev = vop2->dev;
2337604be855SAndy Yan 	struct drm_plane *plane;
2338604be855SAndy Yan 	struct device_node *port;
2339604be855SAndy Yan 	struct vop2_video_port *vp;
23406fd4f8a2SAndy Yan 	struct vop2_win *win;
23416fd4f8a2SAndy Yan 	u32 possible_crtcs;
23426fd4f8a2SAndy Yan 	int i, j, nvp, nvps = 0;
2343604be855SAndy Yan 	int ret;
2344604be855SAndy Yan 
2345604be855SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
2346604be855SAndy Yan 		const struct vop2_video_port_data *vp_data;
2347604be855SAndy Yan 		struct device_node *np;
2348604be855SAndy Yan 		char dclk_name[9];
2349604be855SAndy Yan 
2350604be855SAndy Yan 		vp_data = &vop2_data->vp[i];
2351604be855SAndy Yan 		vp = &vop2->vps[i];
2352604be855SAndy Yan 		vp->vop2 = vop2;
2353604be855SAndy Yan 		vp->id = vp_data->id;
2354604be855SAndy Yan 		vp->data = vp_data;
2355604be855SAndy Yan 
2356604be855SAndy Yan 		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
2357604be855SAndy Yan 		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
2358b06d1ef3SCristian Ciocaltea 		if (IS_ERR(vp->dclk))
2359b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, PTR_ERR(vp->dclk),
2360b06d1ef3SCristian Ciocaltea 					     "failed to get %s\n", dclk_name);
2361604be855SAndy Yan 
2362604be855SAndy Yan 		np = of_graph_get_remote_node(dev->of_node, i, -1);
2363604be855SAndy Yan 		if (!np) {
2364604be855SAndy Yan 			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
2365604be855SAndy Yan 			continue;
2366604be855SAndy Yan 		}
2367604be855SAndy Yan 		of_node_put(np);
2368604be855SAndy Yan 
2369604be855SAndy Yan 		port = of_graph_get_port_by_id(dev->of_node, i);
2370b06d1ef3SCristian Ciocaltea 		if (!port)
2371b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, -ENOENT,
2372b06d1ef3SCristian Ciocaltea 					     "no port node found for video_port%d\n", i);
2373604be855SAndy Yan 		vp->crtc.port = port;
2374604be855SAndy Yan 		nvps++;
2375604be855SAndy Yan 	}
2376604be855SAndy Yan 
2377604be855SAndy Yan 	nvp = 0;
23786fd4f8a2SAndy Yan 	/* Register a primary plane for every crtc */
23796fd4f8a2SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
23806fd4f8a2SAndy Yan 		vp = &vop2->vps[i];
2381604be855SAndy Yan 
23826fd4f8a2SAndy Yan 		if (!vp->crtc.port)
2383604be855SAndy Yan 			continue;
23846fd4f8a2SAndy Yan 
23856fd4f8a2SAndy Yan 		for (j = 0; j < vop2->registered_num_wins; j++) {
23866fd4f8a2SAndy Yan 			win = &vop2->win[j];
23876fd4f8a2SAndy Yan 
23886fd4f8a2SAndy Yan 			/* Aready registered as primary plane */
23896fd4f8a2SAndy Yan 			if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
23906fd4f8a2SAndy Yan 				continue;
23916fd4f8a2SAndy Yan 
2392b90fa71aSAndy Yan 			/* If this win can not attached to this VP */
2393b90fa71aSAndy Yan 			if (!(win->data->possible_vp_mask & BIT(vp->id)))
2394b90fa71aSAndy Yan 				continue;
2395b90fa71aSAndy Yan 
23966fd4f8a2SAndy Yan 			if (vop2_is_mirror_win(win))
23976fd4f8a2SAndy Yan 				continue;
2398604be855SAndy Yan 
2399604be855SAndy Yan 			if (win->type == DRM_PLANE_TYPE_PRIMARY) {
2400604be855SAndy Yan 				possible_crtcs = BIT(nvp);
2401604be855SAndy Yan 				vp->primary_plane = win;
2402604be855SAndy Yan 				ret = vop2_plane_init(vop2, win, possible_crtcs);
2403b06d1ef3SCristian Ciocaltea 				if (ret)
24046fd4f8a2SAndy Yan 					return dev_err_probe(drm->dev, ret,
24056fd4f8a2SAndy Yan 							     "failed to init primary plane %s\n",
24066fd4f8a2SAndy Yan 							     win->data->name);
24076fd4f8a2SAndy Yan 				nvp++;
24086fd4f8a2SAndy Yan 				break;
24096fd4f8a2SAndy Yan 			}
24106fd4f8a2SAndy Yan 		}
2411f9f68bf1SHeiko Stuebner 
2412f9f68bf1SHeiko Stuebner 		if (!vp->primary_plane)
2413f9f68bf1SHeiko Stuebner 			return dev_err_probe(drm->dev, -ENOENT,
2414f9f68bf1SHeiko Stuebner 					     "no primary plane for vp %d\n", i);
24156fd4f8a2SAndy Yan 	}
24166fd4f8a2SAndy Yan 
24176fd4f8a2SAndy Yan 	/* Register all unused window as overlay plane */
24186fd4f8a2SAndy Yan 	for (i = 0; i < vop2->registered_num_wins; i++) {
24196fd4f8a2SAndy Yan 		win = &vop2->win[i];
24206fd4f8a2SAndy Yan 
24216fd4f8a2SAndy Yan 		/* Aready registered as primary plane */
24226fd4f8a2SAndy Yan 		if (win->base.type == DRM_PLANE_TYPE_PRIMARY)
24236fd4f8a2SAndy Yan 			continue;
24246fd4f8a2SAndy Yan 
24256fd4f8a2SAndy Yan 		if (vop2_is_mirror_win(win))
24266fd4f8a2SAndy Yan 			continue;
24276fd4f8a2SAndy Yan 
24286fd4f8a2SAndy Yan 		win->type = DRM_PLANE_TYPE_OVERLAY;
24296fd4f8a2SAndy Yan 
2430b90fa71aSAndy Yan 		possible_crtcs = 0;
2431b90fa71aSAndy Yan 		nvp = 0;
2432b90fa71aSAndy Yan 		for (j = 0; j < vop2_data->nr_vps; j++) {
2433b90fa71aSAndy Yan 			vp = &vop2->vps[j];
2434b90fa71aSAndy Yan 
2435b90fa71aSAndy Yan 			if (!vp->crtc.port)
2436b90fa71aSAndy Yan 				continue;
2437b90fa71aSAndy Yan 
2438b90fa71aSAndy Yan 			if (win->data->possible_vp_mask & BIT(vp->id))
2439b90fa71aSAndy Yan 				possible_crtcs |= BIT(nvp);
2440b90fa71aSAndy Yan 			nvp++;
2441b90fa71aSAndy Yan 		}
2442b90fa71aSAndy Yan 
24436fd4f8a2SAndy Yan 		ret = vop2_plane_init(vop2, win, possible_crtcs);
24446fd4f8a2SAndy Yan 		if (ret)
24456fd4f8a2SAndy Yan 			return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n",
2446b06d1ef3SCristian Ciocaltea 					     win->data->name);
2447604be855SAndy Yan 	}
2448604be855SAndy Yan 
2449604be855SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
2450604be855SAndy Yan 		vp = &vop2->vps[i];
2451604be855SAndy Yan 
2452604be855SAndy Yan 		if (!vp->crtc.port)
2453604be855SAndy Yan 			continue;
2454604be855SAndy Yan 
2455604be855SAndy Yan 		plane = &vp->primary_plane->base;
2456604be855SAndy Yan 
2457604be855SAndy Yan 		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
2458604be855SAndy Yan 						&vop2_crtc_funcs,
2459604be855SAndy Yan 						"video_port%d", vp->id);
2460b06d1ef3SCristian Ciocaltea 		if (ret)
2461b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, ret,
2462b06d1ef3SCristian Ciocaltea 					     "crtc init for video_port%d failed\n", i);
2463604be855SAndy Yan 
2464604be855SAndy Yan 		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
24654f537776SPiotr Zalewski 		if (vop2->lut_regs) {
24664f537776SPiotr Zalewski 			const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
2467604be855SAndy Yan 
24684f537776SPiotr Zalewski 			drm_mode_crtc_set_gamma_size(&vp->crtc, vp_data->gamma_lut_len);
24694f537776SPiotr Zalewski 			drm_crtc_enable_color_mgmt(&vp->crtc, 0, false, vp_data->gamma_lut_len);
24704f537776SPiotr Zalewski 		}
2471604be855SAndy Yan 		init_completion(&vp->dsp_hold_completion);
2472604be855SAndy Yan 	}
2473604be855SAndy Yan 
2474604be855SAndy Yan 	/*
2475604be855SAndy Yan 	 * On the VOP2 it's very hard to change the number of layers on a VP
2476604be855SAndy Yan 	 * during runtime, so we distribute the layers equally over the used
2477604be855SAndy Yan 	 * VPs
2478604be855SAndy Yan 	 */
2479604be855SAndy Yan 	for (i = 0; i < vop2->data->nr_vps; i++) {
2480604be855SAndy Yan 		struct vop2_video_port *vp = &vop2->vps[i];
2481604be855SAndy Yan 
2482604be855SAndy Yan 		if (vp->crtc.port)
2483dc00748aSAndy Yan 			vp->nlayers = vop2_data->win_size / nvps;
2484604be855SAndy Yan 	}
2485604be855SAndy Yan 
2486604be855SAndy Yan 	return 0;
2487604be855SAndy Yan }
2488604be855SAndy Yan 
vop2_destroy_crtcs(struct vop2 * vop2)2489cddddc06SMichael Riesch static void vop2_destroy_crtcs(struct vop2 *vop2)
2490604be855SAndy Yan {
2491cddddc06SMichael Riesch 	struct drm_device *drm = vop2->drm;
2492cddddc06SMichael Riesch 	struct list_head *crtc_list = &drm->mode_config.crtc_list;
2493cddddc06SMichael Riesch 	struct list_head *plane_list = &drm->mode_config.plane_list;
2494cddddc06SMichael Riesch 	struct drm_crtc *crtc, *tmpc;
2495cddddc06SMichael Riesch 	struct drm_plane *plane, *tmpp;
2496cddddc06SMichael Riesch 
2497cddddc06SMichael Riesch 	list_for_each_entry_safe(plane, tmpp, plane_list, head)
2498cddddc06SMichael Riesch 		drm_plane_cleanup(plane);
2499604be855SAndy Yan 
2500604be855SAndy Yan 	/*
2501604be855SAndy Yan 	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
2502604be855SAndy Yan 	 * references the CRTC.
2503604be855SAndy Yan 	 */
2504cddddc06SMichael Riesch 	list_for_each_entry_safe(crtc, tmpc, crtc_list, head) {
2505cddddc06SMichael Riesch 		of_node_put(crtc->port);
2506604be855SAndy Yan 		drm_crtc_cleanup(crtc);
2507604be855SAndy Yan 	}
2508cddddc06SMichael Riesch }
2509604be855SAndy Yan 
vop2_find_rgb_encoder(struct vop2 * vop2)2510c66c6d7cSMichael Riesch static int vop2_find_rgb_encoder(struct vop2 *vop2)
2511c66c6d7cSMichael Riesch {
2512c66c6d7cSMichael Riesch 	struct device_node *node = vop2->dev->of_node;
2513c66c6d7cSMichael Riesch 	struct device_node *endpoint;
2514c66c6d7cSMichael Riesch 	int i;
2515c66c6d7cSMichael Riesch 
2516c66c6d7cSMichael Riesch 	for (i = 0; i < vop2->data->nr_vps; i++) {
2517c66c6d7cSMichael Riesch 		endpoint = of_graph_get_endpoint_by_regs(node, i,
2518c66c6d7cSMichael Riesch 							 ROCKCHIP_VOP2_EP_RGB0);
2519c66c6d7cSMichael Riesch 		if (!endpoint)
2520c66c6d7cSMichael Riesch 			continue;
2521c66c6d7cSMichael Riesch 
2522c66c6d7cSMichael Riesch 		of_node_put(endpoint);
2523c66c6d7cSMichael Riesch 		return i;
2524c66c6d7cSMichael Riesch 	}
2525c66c6d7cSMichael Riesch 
2526c66c6d7cSMichael Riesch 	return -ENOENT;
2527c66c6d7cSMichael Riesch }
2528604be855SAndy Yan 
vop2_regmap_init(struct vop2_win * win,const struct reg_field * regs,int nr_regs)2529145c9b36SAndy Yan static int vop2_regmap_init(struct vop2_win *win, const struct reg_field *regs,
2530145c9b36SAndy Yan 			    int nr_regs)
2531604be855SAndy Yan {
2532604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
2533ff0b6c03SAndy Yan 	int i;
2534604be855SAndy Yan 
2535145c9b36SAndy Yan 	for (i = 0; i < nr_regs; i++) {
2536ff0b6c03SAndy Yan 		const struct reg_field field = {
2537145c9b36SAndy Yan 			.reg = (regs[i].reg != 0xffffffff) ?
2538145c9b36SAndy Yan 				regs[i].reg + win->offset : regs[i].reg,
2539145c9b36SAndy Yan 			.lsb = regs[i].lsb,
2540145c9b36SAndy Yan 			.msb = regs[i].msb
2541604be855SAndy Yan 		};
2542604be855SAndy Yan 
2543ff0b6c03SAndy Yan 		win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field);
2544ff0b6c03SAndy Yan 		if (IS_ERR(win->reg[i]))
2545ff0b6c03SAndy Yan 			return PTR_ERR(win->reg[i]);
2546ff0b6c03SAndy Yan 	}
2547ff0b6c03SAndy Yan 
2548ff0b6c03SAndy Yan 	return 0;
2549ff0b6c03SAndy Yan };
2550ff0b6c03SAndy Yan 
vop2_win_init(struct vop2 * vop2)2551604be855SAndy Yan static int vop2_win_init(struct vop2 *vop2)
2552604be855SAndy Yan {
2553604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2554604be855SAndy Yan 	struct vop2_win *win;
2555604be855SAndy Yan 	int i, ret;
2556604be855SAndy Yan 
2557604be855SAndy Yan 	for (i = 0; i < vop2_data->win_size; i++) {
2558604be855SAndy Yan 		const struct vop2_win_data *win_data = &vop2_data->win[i];
2559604be855SAndy Yan 
2560604be855SAndy Yan 		win = &vop2->win[i];
2561604be855SAndy Yan 		win->data = win_data;
2562604be855SAndy Yan 		win->type = win_data->type;
2563604be855SAndy Yan 		win->offset = win_data->base;
2564604be855SAndy Yan 		win->win_id = i;
2565604be855SAndy Yan 		win->vop2 = vop2;
2566604be855SAndy Yan 		if (vop2_cluster_window(win))
2567145c9b36SAndy Yan 			ret = vop2_regmap_init(win, vop2->data->cluster_reg,
2568145c9b36SAndy Yan 					       vop2->data->nr_cluster_regs);
2569604be855SAndy Yan 		else
2570145c9b36SAndy Yan 			ret = vop2_regmap_init(win, vop2->data->smart_reg,
2571145c9b36SAndy Yan 					       vop2->data->nr_smart_regs);
2572604be855SAndy Yan 		if (ret)
2573604be855SAndy Yan 			return ret;
2574604be855SAndy Yan 	}
2575604be855SAndy Yan 
2576604be855SAndy Yan 	vop2->registered_num_wins = vop2_data->win_size;
2577604be855SAndy Yan 
2578604be855SAndy Yan 	return 0;
2579604be855SAndy Yan }
2580604be855SAndy Yan 
2581604be855SAndy Yan /*
2582604be855SAndy Yan  * The window registers are only updated when config done is written.
2583604be855SAndy Yan  * Until that they read back the old value. As we read-modify-write
2584604be855SAndy Yan  * these registers mark them as non-volatile. This makes sure we read
2585604be855SAndy Yan  * the new values from the regmap register cache.
2586604be855SAndy Yan  */
2587604be855SAndy Yan static const struct regmap_range vop2_nonvolatile_range[] = {
2588604be855SAndy Yan 	regmap_reg_range(0x1000, 0x23ff),
2589604be855SAndy Yan };
2590604be855SAndy Yan 
2591604be855SAndy Yan static const struct regmap_access_table vop2_volatile_table = {
2592604be855SAndy Yan 	.no_ranges = vop2_nonvolatile_range,
2593604be855SAndy Yan 	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
2594604be855SAndy Yan };
2595604be855SAndy Yan 
2596604be855SAndy Yan static const struct regmap_config vop2_regmap_config = {
2597604be855SAndy Yan 	.reg_bits	= 32,
2598604be855SAndy Yan 	.val_bits	= 32,
2599604be855SAndy Yan 	.reg_stride	= 4,
2600604be855SAndy Yan 	.max_register	= 0x3000,
2601604be855SAndy Yan 	.name		= "vop2",
2602604be855SAndy Yan 	.volatile_table	= &vop2_volatile_table,
26033d59c22bSMark Brown 	.cache_type	= REGCACHE_MAPLE,
2604604be855SAndy Yan };
2605604be855SAndy Yan 
vop2_bind(struct device * dev,struct device * master,void * data)2606604be855SAndy Yan static int vop2_bind(struct device *dev, struct device *master, void *data)
2607604be855SAndy Yan {
2608604be855SAndy Yan 	struct platform_device *pdev = to_platform_device(dev);
2609604be855SAndy Yan 	const struct vop2_data *vop2_data;
2610604be855SAndy Yan 	struct drm_device *drm = data;
2611604be855SAndy Yan 	struct vop2 *vop2;
2612604be855SAndy Yan 	struct resource *res;
2613604be855SAndy Yan 	size_t alloc_size;
2614604be855SAndy Yan 	int ret;
2615604be855SAndy Yan 
2616604be855SAndy Yan 	vop2_data = of_device_get_match_data(dev);
2617604be855SAndy Yan 	if (!vop2_data)
2618604be855SAndy Yan 		return -ENODEV;
2619604be855SAndy Yan 
2620604be855SAndy Yan 	/* Allocate vop2 struct and its vop2_win array */
26213b4db36cSJacob Keller 	alloc_size = struct_size(vop2, win, vop2_data->win_size);
2622604be855SAndy Yan 	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
2623604be855SAndy Yan 	if (!vop2)
2624604be855SAndy Yan 		return -ENOMEM;
2625604be855SAndy Yan 
2626604be855SAndy Yan 	vop2->dev = dev;
2627604be855SAndy Yan 	vop2->data = vop2_data;
2628328e6885SAndy Yan 	vop2->ops = vop2_data->ops;
2629301618edSAndy Yan 	vop2->version = vop2_data->version;
2630604be855SAndy Yan 	vop2->drm = drm;
2631604be855SAndy Yan 
2632604be855SAndy Yan 	dev_set_drvdata(dev, vop2);
2633604be855SAndy Yan 
26345ee8c8f9SSascha Hauer 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop");
2635b06d1ef3SCristian Ciocaltea 	if (!res)
2636b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, -EINVAL,
2637b06d1ef3SCristian Ciocaltea 				     "failed to get vop2 register byname\n");
2638604be855SAndy Yan 
263977996455SAndy Yan 	vop2->res = res;
2640604be855SAndy Yan 	vop2->regs = devm_ioremap_resource(dev, res);
2641604be855SAndy Yan 	if (IS_ERR(vop2->regs))
2642604be855SAndy Yan 		return PTR_ERR(vop2->regs);
2643604be855SAndy Yan 	vop2->len = resource_size(res);
2644604be855SAndy Yan 
2645604be855SAndy Yan 	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
26464ab9157cSAlfredo Cruz 	if (IS_ERR(vop2->map))
26474ab9157cSAlfredo Cruz 		return PTR_ERR(vop2->map);
2648604be855SAndy Yan 
2649604be855SAndy Yan 	ret = vop2_win_init(vop2);
2650604be855SAndy Yan 	if (ret)
2651604be855SAndy Yan 		return ret;
2652604be855SAndy Yan 
26535ee8c8f9SSascha Hauer 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma-lut");
2654604be855SAndy Yan 	if (res) {
2655604be855SAndy Yan 		vop2->lut_regs = devm_ioremap_resource(dev, res);
2656604be855SAndy Yan 		if (IS_ERR(vop2->lut_regs))
2657604be855SAndy Yan 			return PTR_ERR(vop2->lut_regs);
2658604be855SAndy Yan 	}
26595a028e8fSAndy Yan 	if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_GRF) {
2660c408af1aSAndy Yan 		vop2->sys_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
26615a028e8fSAndy Yan 		if (IS_ERR(vop2->sys_grf))
2662b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_grf),
2663b06d1ef3SCristian Ciocaltea 					     "cannot get sys_grf\n");
26645a028e8fSAndy Yan 	}
26655a028e8fSAndy Yan 
26665a028e8fSAndy Yan 	if (vop2_data->feature & VOP2_FEATURE_HAS_VOP_GRF) {
26675a028e8fSAndy Yan 		vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf");
26685a028e8fSAndy Yan 		if (IS_ERR(vop2->vop_grf))
2669b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, PTR_ERR(vop2->vop_grf),
2670b06d1ef3SCristian Ciocaltea 					     "cannot get vop_grf\n");
26715a028e8fSAndy Yan 	}
26725a028e8fSAndy Yan 
26735a028e8fSAndy Yan 	if (vop2_data->feature & VOP2_FEATURE_HAS_VO1_GRF) {
26745a028e8fSAndy Yan 		vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf");
26755a028e8fSAndy Yan 		if (IS_ERR(vop2->vo1_grf))
2676b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, PTR_ERR(vop2->vo1_grf),
2677b06d1ef3SCristian Ciocaltea 					     "cannot get vo1_grf\n");
26785a028e8fSAndy Yan 	}
26795a028e8fSAndy Yan 
26805a028e8fSAndy Yan 	if (vop2_data->feature & VOP2_FEATURE_HAS_SYS_PMU) {
26815a028e8fSAndy Yan 		vop2->sys_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pmu");
26825a028e8fSAndy Yan 		if (IS_ERR(vop2->sys_pmu))
2683b06d1ef3SCristian Ciocaltea 			return dev_err_probe(drm->dev, PTR_ERR(vop2->sys_pmu),
2684b06d1ef3SCristian Ciocaltea 					     "cannot get sys_pmu\n");
26855a028e8fSAndy Yan 	}
2686604be855SAndy Yan 
2687604be855SAndy Yan 	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
2688b06d1ef3SCristian Ciocaltea 	if (IS_ERR(vop2->hclk))
2689b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, PTR_ERR(vop2->hclk),
2690b06d1ef3SCristian Ciocaltea 				     "failed to get hclk source\n");
2691604be855SAndy Yan 
2692604be855SAndy Yan 	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
2693b06d1ef3SCristian Ciocaltea 	if (IS_ERR(vop2->aclk))
2694b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, PTR_ERR(vop2->aclk),
2695b06d1ef3SCristian Ciocaltea 				     "failed to get aclk source\n");
2696604be855SAndy Yan 
26975a028e8fSAndy Yan 	vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk_vop");
2698b06d1ef3SCristian Ciocaltea 	if (IS_ERR(vop2->pclk))
2699b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, PTR_ERR(vop2->pclk),
2700b06d1ef3SCristian Ciocaltea 				     "failed to get pclk source\n");
27015a028e8fSAndy Yan 
27022c1268e7SCristian Ciocaltea 	vop2->pll_hdmiphy0 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy0");
2703b06d1ef3SCristian Ciocaltea 	if (IS_ERR(vop2->pll_hdmiphy0))
2704b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy0),
2705b06d1ef3SCristian Ciocaltea 				     "failed to get pll_hdmiphy0\n");
27062c1268e7SCristian Ciocaltea 
2707f8dd7fc9SCristian Ciocaltea 	vop2->pll_hdmiphy1 = devm_clk_get_optional(vop2->dev, "pll_hdmiphy1");
2708f8dd7fc9SCristian Ciocaltea 	if (IS_ERR(vop2->pll_hdmiphy1))
2709f8dd7fc9SCristian Ciocaltea 		return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1),
2710f8dd7fc9SCristian Ciocaltea 				     "failed to get pll_hdmiphy1\n");
2711f8dd7fc9SCristian Ciocaltea 
2712604be855SAndy Yan 	vop2->irq = platform_get_irq(pdev, 0);
2713b06d1ef3SCristian Ciocaltea 	if (vop2->irq < 0)
2714b06d1ef3SCristian Ciocaltea 		return dev_err_probe(drm->dev, vop2->irq, "cannot find irq for vop2\n");
2715604be855SAndy Yan 
2716604be855SAndy Yan 	mutex_init(&vop2->vop2_lock);
2717*3e89a8c6SAndy Yan 	mutex_init(&vop2->ovl_lock);
2718604be855SAndy Yan 
2719604be855SAndy Yan 	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
2720604be855SAndy Yan 	if (ret)
2721604be855SAndy Yan 		return ret;
2722604be855SAndy Yan 
2723cddddc06SMichael Riesch 	ret = vop2_create_crtcs(vop2);
2724604be855SAndy Yan 	if (ret)
2725604be855SAndy Yan 		return ret;
2726604be855SAndy Yan 
2727944757a4SAndy Yan 	if (vop2->version >= VOP_VERSION_RK3576) {
2728944757a4SAndy Yan 		struct drm_crtc *crtc;
2729944757a4SAndy Yan 
2730944757a4SAndy Yan 		drm_for_each_crtc(crtc, drm) {
2731944757a4SAndy Yan 			struct vop2_video_port *vp = to_vop2_video_port(crtc);
2732944757a4SAndy Yan 			int vp_irq;
2733944757a4SAndy Yan 			const char *irq_name = devm_kasprintf(dev, GFP_KERNEL, "vp%d", vp->id);
2734944757a4SAndy Yan 
2735944757a4SAndy Yan 			if (!irq_name)
2736944757a4SAndy Yan 				return -ENOMEM;
2737944757a4SAndy Yan 
2738944757a4SAndy Yan 			vp_irq = platform_get_irq_byname(pdev, irq_name);
2739944757a4SAndy Yan 			if (vp_irq < 0)
2740944757a4SAndy Yan 				return dev_err_probe(drm->dev, vp_irq,
2741944757a4SAndy Yan 						     "cannot find irq for vop2 vp%d\n", vp->id);
2742944757a4SAndy Yan 
2743944757a4SAndy Yan 			ret = devm_request_irq(dev, vp_irq, rk3576_vp_isr, IRQF_SHARED, irq_name,
2744944757a4SAndy Yan 					       vp);
2745944757a4SAndy Yan 			if (ret)
2746944757a4SAndy Yan 				dev_err_probe(drm->dev, ret,
2747944757a4SAndy Yan 					      "request irq for vop2 vp%d failed\n", vp->id);
2748944757a4SAndy Yan 		}
2749944757a4SAndy Yan 	}
2750944757a4SAndy Yan 
2751c66c6d7cSMichael Riesch 	ret = vop2_find_rgb_encoder(vop2);
2752c66c6d7cSMichael Riesch 	if (ret >= 0) {
2753c66c6d7cSMichael Riesch 		vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
2754c66c6d7cSMichael Riesch 					      vop2->drm, ret);
2755c66c6d7cSMichael Riesch 		if (IS_ERR(vop2->rgb)) {
2756c66c6d7cSMichael Riesch 			if (PTR_ERR(vop2->rgb) == -EPROBE_DEFER) {
2757c66c6d7cSMichael Riesch 				ret = PTR_ERR(vop2->rgb);
2758c66c6d7cSMichael Riesch 				goto err_crtcs;
2759c66c6d7cSMichael Riesch 			}
2760c66c6d7cSMichael Riesch 			vop2->rgb = NULL;
2761c66c6d7cSMichael Riesch 		}
2762c66c6d7cSMichael Riesch 	}
2763c66c6d7cSMichael Riesch 
2764604be855SAndy Yan 	rockchip_drm_dma_init_device(vop2->drm, vop2->dev);
2765604be855SAndy Yan 
2766604be855SAndy Yan 	pm_runtime_enable(&pdev->dev);
2767604be855SAndy Yan 
2768604be855SAndy Yan 	return 0;
2769c66c6d7cSMichael Riesch 
2770c66c6d7cSMichael Riesch err_crtcs:
2771c66c6d7cSMichael Riesch 	vop2_destroy_crtcs(vop2);
2772c66c6d7cSMichael Riesch 
2773c66c6d7cSMichael Riesch 	return ret;
2774604be855SAndy Yan }
2775604be855SAndy Yan 
vop2_unbind(struct device * dev,struct device * master,void * data)2776604be855SAndy Yan static void vop2_unbind(struct device *dev, struct device *master, void *data)
2777604be855SAndy Yan {
2778604be855SAndy Yan 	struct vop2 *vop2 = dev_get_drvdata(dev);
2779604be855SAndy Yan 
2780604be855SAndy Yan 	pm_runtime_disable(dev);
2781604be855SAndy Yan 
2782c66c6d7cSMichael Riesch 	if (vop2->rgb)
2783c66c6d7cSMichael Riesch 		rockchip_rgb_fini(vop2->rgb);
2784604be855SAndy Yan 
2785cddddc06SMichael Riesch 	vop2_destroy_crtcs(vop2);
2786604be855SAndy Yan }
2787604be855SAndy Yan 
2788604be855SAndy Yan const struct component_ops vop2_component_ops = {
2789604be855SAndy Yan 	.bind = vop2_bind,
2790604be855SAndy Yan 	.unbind = vop2_unbind,
2791604be855SAndy Yan };
2792604be855SAndy Yan EXPORT_SYMBOL_GPL(vop2_component_ops);
2793