xref: /linux/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c (revision 075a5b3969becb1ebc2f1d4fa1a1fe9163679273)
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>
27604be855SAndy Yan #include <drm/drm_debugfs.h>
28604be855SAndy Yan #include <drm/drm_flip_work.h>
29720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
30604be855SAndy Yan #include <drm/drm_probe_helper.h>
31604be855SAndy Yan #include <drm/drm_vblank.h>
32604be855SAndy Yan 
33604be855SAndy Yan #include <uapi/linux/videodev2.h>
34604be855SAndy Yan #include <dt-bindings/soc/rockchip,vop2.h>
35604be855SAndy Yan 
36604be855SAndy Yan #include "rockchip_drm_drv.h"
37604be855SAndy Yan #include "rockchip_drm_gem.h"
38604be855SAndy Yan #include "rockchip_drm_fb.h"
39604be855SAndy Yan #include "rockchip_drm_vop2.h"
40c66c6d7cSMichael Riesch #include "rockchip_rgb.h"
41604be855SAndy Yan 
42604be855SAndy Yan /*
43604be855SAndy Yan  * VOP2 architecture
44604be855SAndy Yan  *
45604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
46604be855SAndy Yan  |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
47604be855SAndy Yan  |  window0 |   |    Layer0   |                                                        |    RGB    |
48604be855SAndy Yan  +----------+   +-------------+              +---------------+    +-------------+      +-----------+
49604be855SAndy Yan  +----------+   +-------------+              |N from 6 layers|    |             |
50604be855SAndy Yan  |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
51604be855SAndy Yan  |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
52604be855SAndy Yan  +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
53604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
54604be855SAndy Yan  |  Esmart  |   | Sel 1 from 6|
55604be855SAndy Yan  |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
56604be855SAndy Yan  +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
57604be855SAndy Yan  +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
58604be855SAndy Yan  |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
59604be855SAndy Yan  |  Window1 |   |   Layer3    |              +---------------+    +-------------+
60604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
61604be855SAndy Yan  +----------+   +-------------+                                                        | 1 from 3  |
62604be855SAndy Yan  |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
63604be855SAndy Yan  |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
64604be855SAndy Yan  +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
65604be855SAndy Yan  +----------+   +-------------+              |               |    |             |      +-----------+
66604be855SAndy Yan  |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
67604be855SAndy Yan  |  Window1 |   |    Layer5   |                                                        |    eDP    |
68604be855SAndy Yan  +----------+   +-------------+                                                        +-----------+
69604be855SAndy Yan  *
70604be855SAndy Yan  */
71604be855SAndy Yan 
72604be855SAndy Yan enum vop2_data_format {
73604be855SAndy Yan 	VOP2_FMT_ARGB8888 = 0,
74604be855SAndy Yan 	VOP2_FMT_RGB888,
75604be855SAndy Yan 	VOP2_FMT_RGB565,
76604be855SAndy Yan 	VOP2_FMT_XRGB101010,
77604be855SAndy Yan 	VOP2_FMT_YUV420SP,
78604be855SAndy Yan 	VOP2_FMT_YUV422SP,
79604be855SAndy Yan 	VOP2_FMT_YUV444SP,
80604be855SAndy Yan 	VOP2_FMT_YUYV422 = 8,
81604be855SAndy Yan 	VOP2_FMT_YUYV420,
82604be855SAndy Yan 	VOP2_FMT_VYUY422,
83604be855SAndy Yan 	VOP2_FMT_VYUY420,
84604be855SAndy Yan 	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
85604be855SAndy Yan 	VOP2_FMT_YUV420SP_TILE_16x2,
86604be855SAndy Yan 	VOP2_FMT_YUV422SP_TILE_8x4,
87604be855SAndy Yan 	VOP2_FMT_YUV422SP_TILE_16x2,
88604be855SAndy Yan 	VOP2_FMT_YUV420SP_10,
89604be855SAndy Yan 	VOP2_FMT_YUV422SP_10,
90604be855SAndy Yan 	VOP2_FMT_YUV444SP_10,
91604be855SAndy Yan };
92604be855SAndy Yan 
93604be855SAndy Yan enum vop2_afbc_format {
94604be855SAndy Yan 	VOP2_AFBC_FMT_RGB565,
95604be855SAndy Yan 	VOP2_AFBC_FMT_ARGB2101010 = 2,
96604be855SAndy Yan 	VOP2_AFBC_FMT_YUV420_10BIT,
97604be855SAndy Yan 	VOP2_AFBC_FMT_RGB888,
98604be855SAndy Yan 	VOP2_AFBC_FMT_ARGB8888,
99604be855SAndy Yan 	VOP2_AFBC_FMT_YUV420 = 9,
100604be855SAndy Yan 	VOP2_AFBC_FMT_YUV422 = 0xb,
101604be855SAndy Yan 	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
102604be855SAndy Yan 	VOP2_AFBC_FMT_INVALID = -1,
103604be855SAndy Yan };
104604be855SAndy Yan 
105604be855SAndy Yan union vop2_alpha_ctrl {
106604be855SAndy Yan 	u32 val;
107604be855SAndy Yan 	struct {
108604be855SAndy Yan 		/* [0:1] */
109604be855SAndy Yan 		u32 color_mode:1;
110604be855SAndy Yan 		u32 alpha_mode:1;
111604be855SAndy Yan 		/* [2:3] */
112604be855SAndy Yan 		u32 blend_mode:2;
113604be855SAndy Yan 		u32 alpha_cal_mode:1;
114604be855SAndy Yan 		/* [5:7] */
115604be855SAndy Yan 		u32 factor_mode:3;
116604be855SAndy Yan 		/* [8:9] */
117604be855SAndy Yan 		u32 alpha_en:1;
118604be855SAndy Yan 		u32 src_dst_swap:1;
119604be855SAndy Yan 		u32 reserved:6;
120604be855SAndy Yan 		/* [16:23] */
121604be855SAndy Yan 		u32 glb_alpha:8;
122604be855SAndy Yan 	} bits;
123604be855SAndy Yan };
124604be855SAndy Yan 
125604be855SAndy Yan struct vop2_alpha {
126604be855SAndy Yan 	union vop2_alpha_ctrl src_color_ctrl;
127604be855SAndy Yan 	union vop2_alpha_ctrl dst_color_ctrl;
128604be855SAndy Yan 	union vop2_alpha_ctrl src_alpha_ctrl;
129604be855SAndy Yan 	union vop2_alpha_ctrl dst_alpha_ctrl;
130604be855SAndy Yan };
131604be855SAndy Yan 
132604be855SAndy Yan struct vop2_alpha_config {
133604be855SAndy Yan 	bool src_premulti_en;
134604be855SAndy Yan 	bool dst_premulti_en;
135604be855SAndy Yan 	bool src_pixel_alpha_en;
136604be855SAndy Yan 	bool dst_pixel_alpha_en;
137604be855SAndy Yan 	u16 src_glb_alpha_value;
138604be855SAndy Yan 	u16 dst_glb_alpha_value;
139604be855SAndy Yan };
140604be855SAndy Yan 
141604be855SAndy Yan struct vop2_win {
142604be855SAndy Yan 	struct vop2 *vop2;
143604be855SAndy Yan 	struct drm_plane base;
144604be855SAndy Yan 	const struct vop2_win_data *data;
145604be855SAndy Yan 	struct regmap_field *reg[VOP2_WIN_MAX_REG];
146604be855SAndy Yan 
147604be855SAndy Yan 	/**
148604be855SAndy Yan 	 * @win_id: graphic window id, a cluster may be split into two
149604be855SAndy Yan 	 * graphics windows.
150604be855SAndy Yan 	 */
151604be855SAndy Yan 	u8 win_id;
152604be855SAndy Yan 	u8 delay;
153604be855SAndy Yan 	u32 offset;
154604be855SAndy Yan 
155604be855SAndy Yan 	enum drm_plane_type type;
156604be855SAndy Yan };
157604be855SAndy Yan 
158604be855SAndy Yan struct vop2_video_port {
159604be855SAndy Yan 	struct drm_crtc crtc;
160604be855SAndy Yan 	struct vop2 *vop2;
161604be855SAndy Yan 	struct clk *dclk;
162604be855SAndy Yan 	unsigned int id;
163604be855SAndy Yan 	const struct vop2_video_port_data *data;
164604be855SAndy Yan 
165604be855SAndy Yan 	struct completion dsp_hold_completion;
166604be855SAndy Yan 
167604be855SAndy Yan 	/**
168604be855SAndy Yan 	 * @win_mask: Bitmask of windows attached to the video port;
169604be855SAndy Yan 	 */
170604be855SAndy Yan 	u32 win_mask;
171604be855SAndy Yan 
172604be855SAndy Yan 	struct vop2_win *primary_plane;
173604be855SAndy Yan 	struct drm_pending_vblank_event *event;
174604be855SAndy Yan 
175604be855SAndy Yan 	unsigned int nlayers;
176604be855SAndy Yan };
177604be855SAndy Yan 
178604be855SAndy Yan struct vop2 {
179604be855SAndy Yan 	struct device *dev;
180604be855SAndy Yan 	struct drm_device *drm;
181604be855SAndy Yan 	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
182604be855SAndy Yan 
183604be855SAndy Yan 	const struct vop2_data *data;
184604be855SAndy Yan 	/*
185604be855SAndy Yan 	 * Number of windows that are registered as plane, may be less than the
186604be855SAndy Yan 	 * total number of hardware windows.
187604be855SAndy Yan 	 */
188604be855SAndy Yan 	u32 registered_num_wins;
189604be855SAndy Yan 
190604be855SAndy Yan 	void __iomem *regs;
191604be855SAndy Yan 	struct regmap *map;
192604be855SAndy Yan 
193604be855SAndy Yan 	struct regmap *grf;
194604be855SAndy Yan 
195604be855SAndy Yan 	/* physical map length of vop2 register */
196604be855SAndy Yan 	u32 len;
197604be855SAndy Yan 
198604be855SAndy Yan 	void __iomem *lut_regs;
199604be855SAndy Yan 
200604be855SAndy Yan 	/* protects crtc enable/disable */
201604be855SAndy Yan 	struct mutex vop2_lock;
202604be855SAndy Yan 
203604be855SAndy Yan 	int irq;
204604be855SAndy Yan 
205604be855SAndy Yan 	/*
206604be855SAndy Yan 	 * Some global resources are shared between all video ports(crtcs), so
207604be855SAndy Yan 	 * we need a ref counter here.
208604be855SAndy Yan 	 */
209604be855SAndy Yan 	unsigned int enable_count;
210604be855SAndy Yan 	struct clk *hclk;
211604be855SAndy Yan 	struct clk *aclk;
212604be855SAndy Yan 
213c66c6d7cSMichael Riesch 	/* optional internal rgb encoder */
214c66c6d7cSMichael Riesch 	struct rockchip_rgb *rgb;
215c66c6d7cSMichael Riesch 
216604be855SAndy Yan 	/* must be put at the end of the struct */
217604be855SAndy Yan 	struct vop2_win win[];
218604be855SAndy Yan };
219604be855SAndy Yan 
22081a06f1dSAndy Yan static const struct regmap_config vop2_regmap_config;
22181a06f1dSAndy Yan 
222604be855SAndy Yan static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
223604be855SAndy Yan {
224604be855SAndy Yan 	return container_of(crtc, struct vop2_video_port, crtc);
225604be855SAndy Yan }
226604be855SAndy Yan 
227604be855SAndy Yan static struct vop2_win *to_vop2_win(struct drm_plane *p)
228604be855SAndy Yan {
229604be855SAndy Yan 	return container_of(p, struct vop2_win, base);
230604be855SAndy Yan }
231604be855SAndy Yan 
232604be855SAndy Yan static void vop2_lock(struct vop2 *vop2)
233604be855SAndy Yan {
234604be855SAndy Yan 	mutex_lock(&vop2->vop2_lock);
235604be855SAndy Yan }
236604be855SAndy Yan 
237604be855SAndy Yan static void vop2_unlock(struct vop2 *vop2)
238604be855SAndy Yan {
239604be855SAndy Yan 	mutex_unlock(&vop2->vop2_lock);
240604be855SAndy Yan }
241604be855SAndy Yan 
242604be855SAndy Yan static void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
243604be855SAndy Yan {
244604be855SAndy Yan 	regmap_write(vop2->map, offset, v);
245604be855SAndy Yan }
246604be855SAndy Yan 
247604be855SAndy Yan static void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
248604be855SAndy Yan {
249604be855SAndy Yan 	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
250604be855SAndy Yan }
251604be855SAndy Yan 
252604be855SAndy Yan static u32 vop2_readl(struct vop2 *vop2, u32 offset)
253604be855SAndy Yan {
254604be855SAndy Yan 	u32 val;
255604be855SAndy Yan 
256604be855SAndy Yan 	regmap_read(vop2->map, offset, &val);
257604be855SAndy Yan 
258604be855SAndy Yan 	return val;
259604be855SAndy Yan }
260604be855SAndy Yan 
261604be855SAndy Yan static void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
262604be855SAndy Yan {
263604be855SAndy Yan 	regmap_field_write(win->reg[reg], v);
264604be855SAndy Yan }
265604be855SAndy Yan 
266604be855SAndy Yan static bool vop2_cluster_window(const struct vop2_win *win)
267604be855SAndy Yan {
268604be855SAndy Yan 	return win->data->feature & WIN_FEATURE_CLUSTER;
269604be855SAndy Yan }
270604be855SAndy Yan 
271d1f8faceSAndy Yan /*
272d1f8faceSAndy Yan  * Note:
273d1f8faceSAndy Yan  * The write mask function is documented but missing on rk3566/8, writes
274d1f8faceSAndy Yan  * to these bits have no effect. For newer soc(rk3588 and following) the
275d1f8faceSAndy Yan  * write mask is needed for register writes.
276d1f8faceSAndy Yan  *
277d1f8faceSAndy Yan  * GLB_CFG_DONE_EN has no write mask bit.
278d1f8faceSAndy Yan  *
279d1f8faceSAndy Yan  */
280604be855SAndy Yan static void vop2_cfg_done(struct vop2_video_port *vp)
281604be855SAndy Yan {
282604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
283d1f8faceSAndy Yan 	u32 val = RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN;
284604be855SAndy Yan 
285d1f8faceSAndy Yan 	val |= BIT(vp->id) | (BIT(vp->id) << 16);
286d1f8faceSAndy Yan 
287d1f8faceSAndy Yan 	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, val);
288604be855SAndy Yan }
289604be855SAndy Yan 
290604be855SAndy Yan static void vop2_win_disable(struct vop2_win *win)
291604be855SAndy Yan {
292604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
293604be855SAndy Yan 
294604be855SAndy Yan 	if (vop2_cluster_window(win))
295604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
296604be855SAndy Yan }
297604be855SAndy Yan 
29845ad07c7SAndy Yan static u32 vop2_get_bpp(const struct drm_format_info *format)
29945ad07c7SAndy Yan {
30045ad07c7SAndy Yan 	switch (format->format) {
30145ad07c7SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
30245ad07c7SAndy Yan 		return 12;
30345ad07c7SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
30445ad07c7SAndy Yan 		return 15;
30545ad07c7SAndy Yan 	case DRM_FORMAT_VUY101010:
30645ad07c7SAndy Yan 		return 30;
30745ad07c7SAndy Yan 	default:
30845ad07c7SAndy Yan 		return drm_format_info_bpp(format, 0);
30945ad07c7SAndy Yan 	}
31045ad07c7SAndy Yan }
31145ad07c7SAndy Yan 
312604be855SAndy Yan static enum vop2_data_format vop2_convert_format(u32 format)
313604be855SAndy Yan {
314604be855SAndy Yan 	switch (format) {
315bfd8a5c2SAndy Yan 	case DRM_FORMAT_XRGB2101010:
316bfd8a5c2SAndy Yan 	case DRM_FORMAT_ARGB2101010:
317bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
318bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
319bfd8a5c2SAndy Yan 		return VOP2_FMT_XRGB101010;
320604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
321604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
322604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
323604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
324604be855SAndy Yan 		return VOP2_FMT_ARGB8888;
325604be855SAndy Yan 	case DRM_FORMAT_RGB888:
326604be855SAndy Yan 	case DRM_FORMAT_BGR888:
327604be855SAndy Yan 		return VOP2_FMT_RGB888;
328604be855SAndy Yan 	case DRM_FORMAT_RGB565:
329604be855SAndy Yan 	case DRM_FORMAT_BGR565:
330604be855SAndy Yan 		return VOP2_FMT_RGB565;
331604be855SAndy Yan 	case DRM_FORMAT_NV12:
332bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV21:
333bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
334604be855SAndy Yan 		return VOP2_FMT_YUV420SP;
335bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV15:
336bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
337bfd8a5c2SAndy Yan 		return VOP2_FMT_YUV420SP_10;
338604be855SAndy Yan 	case DRM_FORMAT_NV16:
339bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV61:
340604be855SAndy Yan 		return VOP2_FMT_YUV422SP;
3415fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV20:
342bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
343bfd8a5c2SAndy Yan 		return VOP2_FMT_YUV422SP_10;
344604be855SAndy Yan 	case DRM_FORMAT_NV24:
345bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV42:
346604be855SAndy Yan 		return VOP2_FMT_YUV444SP;
3475fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV30:
3485fc6aa7dSJonas Karlman 		return VOP2_FMT_YUV444SP_10;
349604be855SAndy Yan 	case DRM_FORMAT_YUYV:
350604be855SAndy Yan 	case DRM_FORMAT_YVYU:
351604be855SAndy Yan 		return VOP2_FMT_VYUY422;
352604be855SAndy Yan 	case DRM_FORMAT_VYUY:
353604be855SAndy Yan 	case DRM_FORMAT_UYVY:
354604be855SAndy Yan 		return VOP2_FMT_YUYV422;
355604be855SAndy Yan 	default:
356604be855SAndy Yan 		DRM_ERROR("unsupported format[%08x]\n", format);
357604be855SAndy Yan 		return -EINVAL;
358604be855SAndy Yan 	}
359604be855SAndy Yan }
360604be855SAndy Yan 
361604be855SAndy Yan static enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
362604be855SAndy Yan {
363604be855SAndy Yan 	switch (format) {
364bfd8a5c2SAndy Yan 	case DRM_FORMAT_XRGB2101010:
365bfd8a5c2SAndy Yan 	case DRM_FORMAT_ARGB2101010:
366bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
367bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
368bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_ARGB2101010;
369604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
370604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
371604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
372604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
373604be855SAndy Yan 		return VOP2_AFBC_FMT_ARGB8888;
374604be855SAndy Yan 	case DRM_FORMAT_RGB888:
375604be855SAndy Yan 	case DRM_FORMAT_BGR888:
376604be855SAndy Yan 		return VOP2_AFBC_FMT_RGB888;
377604be855SAndy Yan 	case DRM_FORMAT_RGB565:
378604be855SAndy Yan 	case DRM_FORMAT_BGR565:
379604be855SAndy Yan 		return VOP2_AFBC_FMT_RGB565;
380bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
381bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV420;
382bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
383bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV420_10BIT;
384bfd8a5c2SAndy Yan 	case DRM_FORMAT_YVYU:
385bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
386bfd8a5c2SAndy Yan 	case DRM_FORMAT_VYUY:
387bfd8a5c2SAndy Yan 	case DRM_FORMAT_UYVY:
388bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV422;
389bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
390bfd8a5c2SAndy Yan 		return VOP2_AFBC_FMT_YUV422_10BIT;
391604be855SAndy Yan 	default:
392604be855SAndy Yan 		return VOP2_AFBC_FMT_INVALID;
393604be855SAndy Yan 	}
394604be855SAndy Yan 
395604be855SAndy Yan 	return VOP2_AFBC_FMT_INVALID;
396604be855SAndy Yan }
397604be855SAndy Yan 
398604be855SAndy Yan static bool vop2_win_rb_swap(u32 format)
399604be855SAndy Yan {
400604be855SAndy Yan 	switch (format) {
401bfd8a5c2SAndy Yan 	case DRM_FORMAT_XBGR2101010:
402bfd8a5c2SAndy Yan 	case DRM_FORMAT_ABGR2101010:
403604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
404604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
405604be855SAndy Yan 	case DRM_FORMAT_BGR888:
406604be855SAndy Yan 	case DRM_FORMAT_BGR565:
407604be855SAndy Yan 		return true;
408604be855SAndy Yan 	default:
409604be855SAndy Yan 		return false;
410604be855SAndy Yan 	}
411604be855SAndy Yan }
412604be855SAndy Yan 
413604be855SAndy Yan static bool vop2_afbc_uv_swap(u32 format)
414604be855SAndy Yan {
415bfd8a5c2SAndy Yan 	switch (format) {
416bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
417bfd8a5c2SAndy Yan 	case DRM_FORMAT_Y210:
418bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_8BIT:
419bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUV420_10BIT:
420bfd8a5c2SAndy Yan 		return true;
421bfd8a5c2SAndy Yan 	default:
422604be855SAndy Yan 		return false;
423604be855SAndy Yan 	}
424bfd8a5c2SAndy Yan }
425604be855SAndy Yan 
426604be855SAndy Yan static bool vop2_win_uv_swap(u32 format)
427604be855SAndy Yan {
428604be855SAndy Yan 	switch (format) {
429604be855SAndy Yan 	case DRM_FORMAT_NV12:
430604be855SAndy Yan 	case DRM_FORMAT_NV16:
431604be855SAndy Yan 	case DRM_FORMAT_NV24:
432bfd8a5c2SAndy Yan 	case DRM_FORMAT_NV15:
4335fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV20:
4345fc6aa7dSJonas Karlman 	case DRM_FORMAT_NV30:
435bfd8a5c2SAndy Yan 	case DRM_FORMAT_YUYV:
436bfd8a5c2SAndy Yan 	case DRM_FORMAT_UYVY:
437604be855SAndy Yan 		return true;
438604be855SAndy Yan 	default:
439604be855SAndy Yan 		return false;
440604be855SAndy Yan 	}
441604be855SAndy Yan }
442604be855SAndy Yan 
443604be855SAndy Yan static bool vop2_win_dither_up(u32 format)
444604be855SAndy Yan {
445604be855SAndy Yan 	switch (format) {
446604be855SAndy Yan 	case DRM_FORMAT_BGR565:
447604be855SAndy Yan 	case DRM_FORMAT_RGB565:
448604be855SAndy Yan 		return true;
449604be855SAndy Yan 	default:
450604be855SAndy Yan 		return false;
451604be855SAndy Yan 	}
452604be855SAndy Yan }
453604be855SAndy Yan 
454604be855SAndy Yan static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
455604be855SAndy Yan {
456604be855SAndy Yan 	/*
457604be855SAndy Yan 	 * FIXME:
458604be855SAndy Yan 	 *
459604be855SAndy Yan 	 * There is no media type for YUV444 output,
460604be855SAndy Yan 	 * so when out_mode is AAAA or P888, assume output is YUV444 on
461604be855SAndy Yan 	 * yuv format.
462604be855SAndy Yan 	 *
463604be855SAndy Yan 	 * From H/W testing, YUV444 mode need a rb swap.
464604be855SAndy Yan 	 */
465604be855SAndy Yan 	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
466604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
467604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
468604be855SAndy Yan 	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
469604be855SAndy Yan 	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
470604be855SAndy Yan 	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
471604be855SAndy Yan 	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
472604be855SAndy Yan 	      output_mode == ROCKCHIP_OUT_MODE_P888)))
473604be855SAndy Yan 		return true;
474604be855SAndy Yan 	else
475604be855SAndy Yan 		return false;
476604be855SAndy Yan }
477604be855SAndy Yan 
478604be855SAndy Yan static bool is_yuv_output(u32 bus_format)
479604be855SAndy Yan {
480604be855SAndy Yan 	switch (bus_format) {
481604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV8_1X24:
482604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV10_1X30:
483604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
484604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
485604be855SAndy Yan 	case MEDIA_BUS_FMT_YUYV8_2X8:
486604be855SAndy Yan 	case MEDIA_BUS_FMT_YVYU8_2X8:
487604be855SAndy Yan 	case MEDIA_BUS_FMT_UYVY8_2X8:
488604be855SAndy Yan 	case MEDIA_BUS_FMT_VYUY8_2X8:
489604be855SAndy Yan 	case MEDIA_BUS_FMT_YUYV8_1X16:
490604be855SAndy Yan 	case MEDIA_BUS_FMT_YVYU8_1X16:
491604be855SAndy Yan 	case MEDIA_BUS_FMT_UYVY8_1X16:
492604be855SAndy Yan 	case MEDIA_BUS_FMT_VYUY8_1X16:
493604be855SAndy Yan 		return true;
494604be855SAndy Yan 	default:
495604be855SAndy Yan 		return false;
496604be855SAndy Yan 	}
497604be855SAndy Yan }
498604be855SAndy Yan 
499604be855SAndy Yan static bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
500604be855SAndy Yan {
501604be855SAndy Yan 	int i;
502604be855SAndy Yan 
503604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_LINEAR)
504604be855SAndy Yan 		return false;
505604be855SAndy Yan 
506604be855SAndy Yan 	for (i = 0 ; i < plane->modifier_count; i++)
507604be855SAndy Yan 		if (plane->modifiers[i] == modifier)
508604be855SAndy Yan 			return true;
509604be855SAndy Yan 
510604be855SAndy Yan 	return false;
511604be855SAndy Yan }
512604be855SAndy Yan 
513604be855SAndy Yan static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
514604be855SAndy Yan 					u64 modifier)
515604be855SAndy Yan {
516604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
517604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
518604be855SAndy Yan 
519604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_INVALID)
520604be855SAndy Yan 		return false;
521604be855SAndy Yan 
522604be855SAndy Yan 	if (modifier == DRM_FORMAT_MOD_LINEAR)
523604be855SAndy Yan 		return true;
524604be855SAndy Yan 
525604be855SAndy Yan 	if (!rockchip_afbc(plane, modifier)) {
526eb23cffdSMichael Tretter 		drm_dbg_kms(vop2->drm, "Unsupported format modifier 0x%llx\n",
527604be855SAndy Yan 			    modifier);
528604be855SAndy Yan 
529604be855SAndy Yan 		return false;
530604be855SAndy Yan 	}
531604be855SAndy Yan 
532604be855SAndy Yan 	return vop2_convert_afbc_format(format) >= 0;
533604be855SAndy Yan }
534604be855SAndy Yan 
535bebad6bdSAndy Yan /*
536bebad6bdSAndy Yan  * 0: Full mode, 16 lines for one tail
537bebad6bdSAndy Yan  * 1: half block mode, 8 lines one tail
538bebad6bdSAndy Yan  */
539bebad6bdSAndy Yan static bool vop2_half_block_enable(struct drm_plane_state *pstate)
540bebad6bdSAndy Yan {
541bebad6bdSAndy Yan 	if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90))
542bebad6bdSAndy Yan 		return false;
543bebad6bdSAndy Yan 	else
544bebad6bdSAndy Yan 		return true;
545bebad6bdSAndy Yan }
546bebad6bdSAndy Yan 
547604be855SAndy Yan static u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
548604be855SAndy Yan 				      bool afbc_half_block_en)
549604be855SAndy Yan {
550604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
551604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
55245ad07c7SAndy Yan 	u32 bpp = vop2_get_bpp(fb->format);
553604be855SAndy Yan 	u32 vir_width = (fb->pitches[0] << 3) / bpp;
554604be855SAndy Yan 	u32 width = drm_rect_width(src) >> 16;
555604be855SAndy Yan 	u32 height = drm_rect_height(src) >> 16;
556604be855SAndy Yan 	u32 act_xoffset = src->x1 >> 16;
557604be855SAndy Yan 	u32 act_yoffset = src->y1 >> 16;
558604be855SAndy Yan 	u32 align16_crop = 0;
559604be855SAndy Yan 	u32 align64_crop = 0;
560604be855SAndy Yan 	u32 height_tmp;
561604be855SAndy Yan 	u8 tx, ty;
562604be855SAndy Yan 	u8 bottom_crop_line_num = 0;
563604be855SAndy Yan 
564604be855SAndy Yan 	/* 16 pixel align */
565604be855SAndy Yan 	if (height & 0xf)
566604be855SAndy Yan 		align16_crop = 16 - (height & 0xf);
567604be855SAndy Yan 
568604be855SAndy Yan 	height_tmp = height + align16_crop;
569604be855SAndy Yan 
570604be855SAndy Yan 	/* 64 pixel align */
571604be855SAndy Yan 	if (height_tmp & 0x3f)
572604be855SAndy Yan 		align64_crop = 64 - (height_tmp & 0x3f);
573604be855SAndy Yan 
574604be855SAndy Yan 	bottom_crop_line_num = align16_crop + align64_crop;
575604be855SAndy Yan 
576604be855SAndy Yan 	switch (pstate->rotation &
577604be855SAndy Yan 		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
578604be855SAndy Yan 		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
579604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
580604be855SAndy Yan 		tx = 16 - ((act_xoffset + width) & 0xf);
581604be855SAndy Yan 		ty = bottom_crop_line_num - act_yoffset;
582604be855SAndy Yan 		break;
583604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
584604be855SAndy Yan 		tx = bottom_crop_line_num - act_yoffset;
585604be855SAndy Yan 		ty = vir_width - width - act_xoffset;
586604be855SAndy Yan 		break;
587604be855SAndy Yan 	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
588604be855SAndy Yan 		tx = act_yoffset;
589604be855SAndy Yan 		ty = act_xoffset;
590604be855SAndy Yan 		break;
591604be855SAndy Yan 	case DRM_MODE_REFLECT_X:
592604be855SAndy Yan 		tx = 16 - ((act_xoffset + width) & 0xf);
593604be855SAndy Yan 		ty = act_yoffset;
594604be855SAndy Yan 		break;
595604be855SAndy Yan 	case DRM_MODE_REFLECT_Y:
596604be855SAndy Yan 		tx = act_xoffset;
597604be855SAndy Yan 		ty = bottom_crop_line_num - act_yoffset;
598604be855SAndy Yan 		break;
599604be855SAndy Yan 	case DRM_MODE_ROTATE_90:
600604be855SAndy Yan 		tx = bottom_crop_line_num - act_yoffset;
601604be855SAndy Yan 		ty = act_xoffset;
602604be855SAndy Yan 		break;
603604be855SAndy Yan 	case DRM_MODE_ROTATE_270:
604604be855SAndy Yan 		tx = act_yoffset;
605604be855SAndy Yan 		ty = vir_width - width - act_xoffset;
606604be855SAndy Yan 		break;
607604be855SAndy Yan 	case 0:
608604be855SAndy Yan 		tx = act_xoffset;
609604be855SAndy Yan 		ty = act_yoffset;
610604be855SAndy Yan 		break;
611604be855SAndy Yan 	}
612604be855SAndy Yan 
613604be855SAndy Yan 	if (afbc_half_block_en)
614604be855SAndy Yan 		ty &= 0x7f;
615604be855SAndy Yan 
616604be855SAndy Yan #define TRANSFORM_XOFFSET GENMASK(7, 0)
617604be855SAndy Yan #define TRANSFORM_YOFFSET GENMASK(23, 16)
618604be855SAndy Yan 	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
619604be855SAndy Yan 		FIELD_PREP(TRANSFORM_YOFFSET, ty);
620604be855SAndy Yan }
621604be855SAndy Yan 
622604be855SAndy Yan /*
623604be855SAndy Yan  * A Cluster window has 2048 x 16 line buffer, which can
624604be855SAndy Yan  * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
625604be855SAndy Yan  * for Cluster_lb_mode register:
626604be855SAndy Yan  * 0: half mode, for plane input width range 2048 ~ 4096
627604be855SAndy Yan  * 1: half mode, for cluster work at 2 * 2048 plane mode
628604be855SAndy Yan  * 2: half mode, for rotate_90/270 mode
629604be855SAndy Yan  *
630604be855SAndy Yan  */
631604be855SAndy Yan static int vop2_get_cluster_lb_mode(struct vop2_win *win,
632604be855SAndy Yan 				    struct drm_plane_state *pstate)
633604be855SAndy Yan {
634604be855SAndy Yan 	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
635604be855SAndy Yan 	    (pstate->rotation & DRM_MODE_ROTATE_90))
636604be855SAndy Yan 		return 2;
637604be855SAndy Yan 	else
638604be855SAndy Yan 		return 0;
639604be855SAndy Yan }
640604be855SAndy Yan 
641604be855SAndy Yan static u16 vop2_scale_factor(u32 src, u32 dst)
642604be855SAndy Yan {
643604be855SAndy Yan 	u32 fac;
644604be855SAndy Yan 	int shift;
645604be855SAndy Yan 
646604be855SAndy Yan 	if (src == dst)
647604be855SAndy Yan 		return 0;
648604be855SAndy Yan 
649604be855SAndy Yan 	if (dst < 2)
650604be855SAndy Yan 		return U16_MAX;
651604be855SAndy Yan 
652604be855SAndy Yan 	if (src < 2)
653604be855SAndy Yan 		return 0;
654604be855SAndy Yan 
655604be855SAndy Yan 	if (src > dst)
656604be855SAndy Yan 		shift = 12;
657604be855SAndy Yan 	else
658604be855SAndy Yan 		shift = 16;
659604be855SAndy Yan 
660604be855SAndy Yan 	src--;
661604be855SAndy Yan 	dst--;
662604be855SAndy Yan 
663604be855SAndy Yan 	fac = DIV_ROUND_UP(src << shift, dst) - 1;
664604be855SAndy Yan 
665604be855SAndy Yan 	if (fac > U16_MAX)
666604be855SAndy Yan 		return U16_MAX;
667604be855SAndy Yan 
668604be855SAndy Yan 	return fac;
669604be855SAndy Yan }
670604be855SAndy Yan 
671604be855SAndy Yan static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
672604be855SAndy Yan 			     u32 src_w, u32 src_h, u32 dst_w,
673604be855SAndy Yan 			     u32 dst_h, u32 pixel_format)
674604be855SAndy Yan {
675604be855SAndy Yan 	const struct drm_format_info *info;
676604be855SAndy Yan 	u16 hor_scl_mode, ver_scl_mode;
677604be855SAndy Yan 	u16 hscl_filter_mode, vscl_filter_mode;
678604be855SAndy Yan 	u8 gt2 = 0;
679604be855SAndy Yan 	u8 gt4 = 0;
680604be855SAndy Yan 	u32 val;
681604be855SAndy Yan 
682604be855SAndy Yan 	info = drm_format_info(pixel_format);
683604be855SAndy Yan 
684604be855SAndy Yan 	if (src_h >= (4 * dst_h)) {
685604be855SAndy Yan 		gt4 = 1;
686604be855SAndy Yan 		src_h >>= 2;
687604be855SAndy Yan 	} else if (src_h >= (2 * dst_h)) {
688604be855SAndy Yan 		gt2 = 1;
689604be855SAndy Yan 		src_h >>= 1;
690604be855SAndy Yan 	}
691604be855SAndy Yan 
692604be855SAndy Yan 	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
693604be855SAndy Yan 	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
694604be855SAndy Yan 
695604be855SAndy Yan 	if (hor_scl_mode == SCALE_UP)
696604be855SAndy Yan 		hscl_filter_mode = VOP2_SCALE_UP_BIC;
697604be855SAndy Yan 	else
698604be855SAndy Yan 		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
699604be855SAndy Yan 
700604be855SAndy Yan 	if (ver_scl_mode == SCALE_UP)
701604be855SAndy Yan 		vscl_filter_mode = VOP2_SCALE_UP_BIL;
702604be855SAndy Yan 	else
703604be855SAndy Yan 		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
704604be855SAndy Yan 
705604be855SAndy Yan 	/*
706604be855SAndy Yan 	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
707604be855SAndy Yan 	 * at scale down mode
708604be855SAndy Yan 	 */
709604be855SAndy Yan 	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
710604be855SAndy Yan 		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
711604be855SAndy Yan 			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
712604be855SAndy Yan 				win->data->name, dst_w);
713604be855SAndy Yan 			dst_w++;
714604be855SAndy Yan 		}
715604be855SAndy Yan 	}
716604be855SAndy Yan 
717604be855SAndy Yan 	val = vop2_scale_factor(src_w, dst_w);
718604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
719604be855SAndy Yan 	val = vop2_scale_factor(src_h, dst_h);
720604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
721604be855SAndy Yan 
722604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
723604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
724604be855SAndy Yan 
725604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
726604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
727604be855SAndy Yan 
728604be855SAndy Yan 	if (vop2_cluster_window(win))
729604be855SAndy Yan 		return;
730604be855SAndy Yan 
731604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
732604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
733604be855SAndy Yan 
734604be855SAndy Yan 	if (info->is_yuv) {
735604be855SAndy Yan 		src_w /= info->hsub;
736604be855SAndy Yan 		src_h /= info->vsub;
737604be855SAndy Yan 
738604be855SAndy Yan 		gt4 = 0;
739604be855SAndy Yan 		gt2 = 0;
740604be855SAndy Yan 
741604be855SAndy Yan 		if (src_h >= (4 * dst_h)) {
742604be855SAndy Yan 			gt4 = 1;
743604be855SAndy Yan 			src_h >>= 2;
744604be855SAndy Yan 		} else if (src_h >= (2 * dst_h)) {
745604be855SAndy Yan 			gt2 = 1;
746604be855SAndy Yan 			src_h >>= 1;
747604be855SAndy Yan 		}
748604be855SAndy Yan 
749604be855SAndy Yan 		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
750604be855SAndy Yan 		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
751604be855SAndy Yan 
752604be855SAndy Yan 		val = vop2_scale_factor(src_w, dst_w);
753604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
754604be855SAndy Yan 
755604be855SAndy Yan 		val = vop2_scale_factor(src_h, dst_h);
756604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
757604be855SAndy Yan 
758604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
759604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
760604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
761604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
762604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
763604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
764604be855SAndy Yan 	}
765604be855SAndy Yan }
766604be855SAndy Yan 
767604be855SAndy Yan static int vop2_convert_csc_mode(int csc_mode)
768604be855SAndy Yan {
769604be855SAndy Yan 	switch (csc_mode) {
770604be855SAndy Yan 	case V4L2_COLORSPACE_SMPTE170M:
771604be855SAndy Yan 	case V4L2_COLORSPACE_470_SYSTEM_M:
772604be855SAndy Yan 	case V4L2_COLORSPACE_470_SYSTEM_BG:
773604be855SAndy Yan 		return CSC_BT601L;
774604be855SAndy Yan 	case V4L2_COLORSPACE_REC709:
775604be855SAndy Yan 	case V4L2_COLORSPACE_SMPTE240M:
776604be855SAndy Yan 	case V4L2_COLORSPACE_DEFAULT:
777604be855SAndy Yan 		return CSC_BT709L;
778604be855SAndy Yan 	case V4L2_COLORSPACE_JPEG:
779604be855SAndy Yan 		return CSC_BT601F;
780604be855SAndy Yan 	case V4L2_COLORSPACE_BT2020:
781604be855SAndy Yan 		return CSC_BT2020;
782604be855SAndy Yan 	default:
783604be855SAndy Yan 		return CSC_BT709L;
784604be855SAndy Yan 	}
785604be855SAndy Yan }
786604be855SAndy Yan 
787604be855SAndy Yan /*
788604be855SAndy Yan  * colorspace path:
789604be855SAndy Yan  *      Input        Win csc                     Output
790604be855SAndy Yan  * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
791604be855SAndy Yan  *    RGB        --> R2Y                  __/
792604be855SAndy Yan  *
793604be855SAndy Yan  * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
794604be855SAndy Yan  *    RGB        --> 709To2020->R2Y       __/
795604be855SAndy Yan  *
796604be855SAndy Yan  * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
797604be855SAndy Yan  *    RGB        --> R2Y                  __/
798604be855SAndy Yan  *
799604be855SAndy Yan  * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
800604be855SAndy Yan  *    RGB        --> 709To2020->R2Y       __/
801604be855SAndy Yan  *
802604be855SAndy Yan  * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
803604be855SAndy Yan  *    RGB        --> R2Y                  __/
804604be855SAndy Yan  *
805604be855SAndy Yan  * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
806604be855SAndy Yan  *    RGB        --> R2Y(601)             __/
807604be855SAndy Yan  *
808604be855SAndy Yan  * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
809604be855SAndy Yan  *    RGB        --> bypass               __/
810604be855SAndy Yan  *
811604be855SAndy Yan  * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
812604be855SAndy Yan  *
813604be855SAndy Yan  * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
814604be855SAndy Yan  *
815604be855SAndy Yan  * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
816604be855SAndy Yan  *
817604be855SAndy Yan  * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
818604be855SAndy Yan  */
819604be855SAndy Yan 
820604be855SAndy Yan static void vop2_setup_csc_mode(struct vop2_video_port *vp,
821604be855SAndy Yan 				struct vop2_win *win,
822604be855SAndy Yan 				struct drm_plane_state *pstate)
823604be855SAndy Yan {
824604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
825604be855SAndy Yan 	int is_input_yuv = pstate->fb->format->is_yuv;
826604be855SAndy Yan 	int is_output_yuv = is_yuv_output(vcstate->bus_format);
827604be855SAndy Yan 	int input_csc = V4L2_COLORSPACE_DEFAULT;
828604be855SAndy Yan 	int output_csc = vcstate->color_space;
829604be855SAndy Yan 	bool r2y_en, y2r_en;
830604be855SAndy Yan 	int csc_mode;
831604be855SAndy Yan 
832604be855SAndy Yan 	if (is_input_yuv && !is_output_yuv) {
833604be855SAndy Yan 		y2r_en = true;
834604be855SAndy Yan 		r2y_en = false;
835604be855SAndy Yan 		csc_mode = vop2_convert_csc_mode(input_csc);
836604be855SAndy Yan 	} else if (!is_input_yuv && is_output_yuv) {
837604be855SAndy Yan 		y2r_en = false;
838604be855SAndy Yan 		r2y_en = true;
839604be855SAndy Yan 		csc_mode = vop2_convert_csc_mode(output_csc);
840604be855SAndy Yan 	} else {
841604be855SAndy Yan 		y2r_en = false;
842604be855SAndy Yan 		r2y_en = false;
843604be855SAndy Yan 		csc_mode = false;
844604be855SAndy Yan 	}
845604be855SAndy Yan 
846604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
847604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
848604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
849604be855SAndy Yan }
850604be855SAndy Yan 
851604be855SAndy Yan static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
852604be855SAndy Yan {
853604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
854604be855SAndy Yan 
855604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
856604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
857604be855SAndy Yan }
858604be855SAndy Yan 
859604be855SAndy Yan static void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
860604be855SAndy Yan {
861604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
862604be855SAndy Yan 
863604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
864604be855SAndy Yan }
865604be855SAndy Yan 
866604be855SAndy Yan static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
867604be855SAndy Yan {
868604be855SAndy Yan 	int ret;
869604be855SAndy Yan 
870604be855SAndy Yan 	ret = clk_prepare_enable(vop2->hclk);
871604be855SAndy Yan 	if (ret < 0) {
872604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
873604be855SAndy Yan 		return ret;
874604be855SAndy Yan 	}
875604be855SAndy Yan 
876604be855SAndy Yan 	ret = clk_prepare_enable(vop2->aclk);
877604be855SAndy Yan 	if (ret < 0) {
878604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
879604be855SAndy Yan 		goto err;
880604be855SAndy Yan 	}
881604be855SAndy Yan 
882604be855SAndy Yan 	return 0;
883604be855SAndy Yan err:
884604be855SAndy Yan 	clk_disable_unprepare(vop2->hclk);
885604be855SAndy Yan 
886604be855SAndy Yan 	return ret;
887604be855SAndy Yan }
888604be855SAndy Yan 
889604be855SAndy Yan static void vop2_enable(struct vop2 *vop2)
890604be855SAndy Yan {
891604be855SAndy Yan 	int ret;
892604be855SAndy Yan 
893e3558747SYuan Can 	ret = pm_runtime_resume_and_get(vop2->dev);
894604be855SAndy Yan 	if (ret < 0) {
895604be855SAndy Yan 		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
896604be855SAndy Yan 		return;
897604be855SAndy Yan 	}
898604be855SAndy Yan 
899604be855SAndy Yan 	ret = vop2_core_clks_prepare_enable(vop2);
900604be855SAndy Yan 	if (ret) {
901604be855SAndy Yan 		pm_runtime_put_sync(vop2->dev);
902604be855SAndy Yan 		return;
903604be855SAndy Yan 	}
904604be855SAndy Yan 
905604be855SAndy Yan 	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
906604be855SAndy Yan 	if (ret) {
907604be855SAndy Yan 		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
908604be855SAndy Yan 		return;
909604be855SAndy Yan 	}
910604be855SAndy Yan 
91181a06f1dSAndy Yan 	ret = regmap_reinit_cache(vop2->map, &vop2_regmap_config);
91281a06f1dSAndy Yan 	if (ret) {
91381a06f1dSAndy Yan 		drm_err(vop2->drm, "failed to reinit cache: %d\n", ret);
91481a06f1dSAndy Yan 		return;
91581a06f1dSAndy Yan 	}
916afa965a4SSascha Hauer 
917604be855SAndy Yan 	if (vop2->data->soc_id == 3566)
918604be855SAndy Yan 		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
919604be855SAndy Yan 
920604be855SAndy Yan 	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
921604be855SAndy Yan 
922604be855SAndy Yan 	/*
923604be855SAndy Yan 	 * Disable auto gating, this is a workaround to
924604be855SAndy Yan 	 * avoid display image shift when a window enabled.
925604be855SAndy Yan 	 */
926604be855SAndy Yan 	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
927604be855SAndy Yan 			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
928604be855SAndy Yan 
929604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
930604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
931604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_EN,
932604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
933604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
934604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
935604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_EN,
936604be855SAndy Yan 		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
937604be855SAndy Yan }
938604be855SAndy Yan 
939604be855SAndy Yan static void vop2_disable(struct vop2 *vop2)
940604be855SAndy Yan {
941604be855SAndy Yan 	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
942604be855SAndy Yan 
943604be855SAndy Yan 	pm_runtime_put_sync(vop2->dev);
944604be855SAndy Yan 
945604be855SAndy Yan 	clk_disable_unprepare(vop2->aclk);
946604be855SAndy Yan 	clk_disable_unprepare(vop2->hclk);
947604be855SAndy Yan }
948604be855SAndy Yan 
949604be855SAndy Yan static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
950604be855SAndy Yan 				     struct drm_atomic_state *state)
951604be855SAndy Yan {
952604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
953604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
954447fb14bSMichael Tretter 	struct drm_crtc_state *old_crtc_state;
955604be855SAndy Yan 	int ret;
956604be855SAndy Yan 
957604be855SAndy Yan 	vop2_lock(vop2);
958604be855SAndy Yan 
959447fb14bSMichael Tretter 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
960447fb14bSMichael Tretter 	drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
961447fb14bSMichael Tretter 
962604be855SAndy Yan 	drm_crtc_vblank_off(crtc);
963604be855SAndy Yan 
964604be855SAndy Yan 	/*
965604be855SAndy Yan 	 * Vop standby will take effect at end of current frame,
966604be855SAndy Yan 	 * if dsp hold valid irq happen, it means standby complete.
967604be855SAndy Yan 	 *
968604be855SAndy Yan 	 * we must wait standby complete when we want to disable aclk,
969604be855SAndy Yan 	 * if not, memory bus maybe dead.
970604be855SAndy Yan 	 */
971604be855SAndy Yan 	reinit_completion(&vp->dsp_hold_completion);
972604be855SAndy Yan 
973604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
974604be855SAndy Yan 
975604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
976604be855SAndy Yan 
977604be855SAndy Yan 	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
978604be855SAndy Yan 					  msecs_to_jiffies(50));
979604be855SAndy Yan 	if (!ret)
980604be855SAndy Yan 		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
981604be855SAndy Yan 
982604be855SAndy Yan 	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
983604be855SAndy Yan 
984604be855SAndy Yan 	clk_disable_unprepare(vp->dclk);
985604be855SAndy Yan 
986604be855SAndy Yan 	vop2->enable_count--;
987604be855SAndy Yan 
988604be855SAndy Yan 	if (!vop2->enable_count)
989604be855SAndy Yan 		vop2_disable(vop2);
990604be855SAndy Yan 
991604be855SAndy Yan 	vop2_unlock(vop2);
992604be855SAndy Yan 
993604be855SAndy Yan 	if (crtc->state->event && !crtc->state->active) {
994604be855SAndy Yan 		spin_lock_irq(&crtc->dev->event_lock);
995604be855SAndy Yan 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
996604be855SAndy Yan 		spin_unlock_irq(&crtc->dev->event_lock);
997604be855SAndy Yan 
998604be855SAndy Yan 		crtc->state->event = NULL;
999604be855SAndy Yan 	}
1000604be855SAndy Yan }
1001604be855SAndy Yan 
1002604be855SAndy Yan static int vop2_plane_atomic_check(struct drm_plane *plane,
1003604be855SAndy Yan 				   struct drm_atomic_state *astate)
1004604be855SAndy Yan {
1005604be855SAndy Yan 	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
1006604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
1007604be855SAndy Yan 	struct drm_crtc *crtc = pstate->crtc;
1008604be855SAndy Yan 	struct drm_crtc_state *cstate;
1009604be855SAndy Yan 	struct vop2_video_port *vp;
1010604be855SAndy Yan 	struct vop2 *vop2;
1011604be855SAndy Yan 	const struct vop2_data *vop2_data;
1012604be855SAndy Yan 	struct drm_rect *dest = &pstate->dst;
1013604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
1014604be855SAndy Yan 	int min_scale = FRAC_16_16(1, 8);
1015604be855SAndy Yan 	int max_scale = FRAC_16_16(8, 1);
1016604be855SAndy Yan 	int format;
1017604be855SAndy Yan 	int ret;
1018604be855SAndy Yan 
1019604be855SAndy Yan 	if (!crtc)
1020604be855SAndy Yan 		return 0;
1021604be855SAndy Yan 
1022604be855SAndy Yan 	vp = to_vop2_video_port(crtc);
1023604be855SAndy Yan 	vop2 = vp->vop2;
1024604be855SAndy Yan 	vop2_data = vop2->data;
1025604be855SAndy Yan 
1026604be855SAndy Yan 	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
1027604be855SAndy Yan 	if (WARN_ON(!cstate))
1028604be855SAndy Yan 		return -EINVAL;
1029604be855SAndy Yan 
1030604be855SAndy Yan 	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
1031604be855SAndy Yan 						  min_scale, max_scale,
1032604be855SAndy Yan 						  true, true);
1033604be855SAndy Yan 	if (ret)
1034604be855SAndy Yan 		return ret;
1035604be855SAndy Yan 
1036604be855SAndy Yan 	if (!pstate->visible)
1037604be855SAndy Yan 		return 0;
1038604be855SAndy Yan 
1039604be855SAndy Yan 	format = vop2_convert_format(fb->format->format);
1040604be855SAndy Yan 	if (format < 0)
1041604be855SAndy Yan 		return format;
1042604be855SAndy Yan 
1043604be855SAndy Yan 	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
1044604be855SAndy Yan 	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
1045604be855SAndy Yan 		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
1046604be855SAndy Yan 			drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
1047604be855SAndy Yan 			drm_rect_width(dest), drm_rect_height(dest));
1048604be855SAndy Yan 		pstate->visible = false;
1049604be855SAndy Yan 		return 0;
1050604be855SAndy Yan 	}
1051604be855SAndy Yan 
1052604be855SAndy Yan 	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
1053604be855SAndy Yan 	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
1054604be855SAndy Yan 		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
1055604be855SAndy Yan 			drm_rect_width(src) >> 16,
1056604be855SAndy Yan 			drm_rect_height(src) >> 16,
1057604be855SAndy Yan 			vop2_data->max_input.width,
1058604be855SAndy Yan 			vop2_data->max_input.height);
1059604be855SAndy Yan 		return -EINVAL;
1060604be855SAndy Yan 	}
1061604be855SAndy Yan 
1062604be855SAndy Yan 	/*
1063604be855SAndy Yan 	 * Src.x1 can be odd when do clip, but yuv plane start point
1064604be855SAndy Yan 	 * need align with 2 pixel.
1065604be855SAndy Yan 	 */
1066604be855SAndy Yan 	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
1067604be855SAndy Yan 		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
1068604be855SAndy Yan 		return -EINVAL;
1069604be855SAndy Yan 	}
1070604be855SAndy Yan 
1071604be855SAndy Yan 	return 0;
1072604be855SAndy Yan }
1073604be855SAndy Yan 
1074604be855SAndy Yan static void vop2_plane_atomic_disable(struct drm_plane *plane,
1075604be855SAndy Yan 				      struct drm_atomic_state *state)
1076604be855SAndy Yan {
1077471bf240SMichael Tretter 	struct drm_plane_state *old_pstate = NULL;
1078604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1079604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
1080604be855SAndy Yan 
1081604be855SAndy Yan 	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
1082604be855SAndy Yan 
1083471bf240SMichael Tretter 	if (state)
1084471bf240SMichael Tretter 		old_pstate = drm_atomic_get_old_plane_state(state, plane);
1085471bf240SMichael Tretter 	if (old_pstate && !old_pstate->crtc)
1086604be855SAndy Yan 		return;
1087604be855SAndy Yan 
1088604be855SAndy Yan 	vop2_win_disable(win);
1089604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
1090604be855SAndy Yan }
1091604be855SAndy Yan 
1092604be855SAndy Yan /*
1093604be855SAndy Yan  * The color key is 10 bit, so all format should
1094604be855SAndy Yan  * convert to 10 bit here.
1095604be855SAndy Yan  */
1096604be855SAndy Yan static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
1097604be855SAndy Yan {
1098604be855SAndy Yan 	struct drm_plane_state *pstate = plane->state;
1099604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
1100604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1101604be855SAndy Yan 	u32 color_key_en = 0;
1102604be855SAndy Yan 	u32 r = 0;
1103604be855SAndy Yan 	u32 g = 0;
1104604be855SAndy Yan 	u32 b = 0;
1105604be855SAndy Yan 
1106604be855SAndy Yan 	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
1107604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
1108604be855SAndy Yan 		return;
1109604be855SAndy Yan 	}
1110604be855SAndy Yan 
1111604be855SAndy Yan 	switch (fb->format->format) {
1112604be855SAndy Yan 	case DRM_FORMAT_RGB565:
1113604be855SAndy Yan 	case DRM_FORMAT_BGR565:
1114604be855SAndy Yan 		r = (color_key & 0xf800) >> 11;
1115604be855SAndy Yan 		g = (color_key & 0x7e0) >> 5;
1116604be855SAndy Yan 		b = (color_key & 0x1f);
1117604be855SAndy Yan 		r <<= 5;
1118604be855SAndy Yan 		g <<= 4;
1119604be855SAndy Yan 		b <<= 5;
1120604be855SAndy Yan 		color_key_en = 1;
1121604be855SAndy Yan 		break;
1122604be855SAndy Yan 	case DRM_FORMAT_XRGB8888:
1123604be855SAndy Yan 	case DRM_FORMAT_ARGB8888:
1124604be855SAndy Yan 	case DRM_FORMAT_XBGR8888:
1125604be855SAndy Yan 	case DRM_FORMAT_ABGR8888:
1126604be855SAndy Yan 	case DRM_FORMAT_RGB888:
1127604be855SAndy Yan 	case DRM_FORMAT_BGR888:
1128604be855SAndy Yan 		r = (color_key & 0xff0000) >> 16;
1129604be855SAndy Yan 		g = (color_key & 0xff00) >> 8;
1130604be855SAndy Yan 		b = (color_key & 0xff);
1131604be855SAndy Yan 		r <<= 2;
1132604be855SAndy Yan 		g <<= 2;
1133604be855SAndy Yan 		b <<= 2;
1134604be855SAndy Yan 		color_key_en = 1;
1135604be855SAndy Yan 		break;
1136604be855SAndy Yan 	}
1137604be855SAndy Yan 
1138604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
1139604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
1140604be855SAndy Yan }
1141604be855SAndy Yan 
1142604be855SAndy Yan static void vop2_plane_atomic_update(struct drm_plane *plane,
1143604be855SAndy Yan 				     struct drm_atomic_state *state)
1144604be855SAndy Yan {
1145604be855SAndy Yan 	struct drm_plane_state *pstate = plane->state;
1146604be855SAndy Yan 	struct drm_crtc *crtc = pstate->crtc;
1147604be855SAndy Yan 	struct vop2_win *win = to_vop2_win(plane);
1148604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1149604be855SAndy Yan 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
1150604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
1151604be855SAndy Yan 	struct drm_framebuffer *fb = pstate->fb;
115245ad07c7SAndy Yan 	u32 bpp = vop2_get_bpp(fb->format);
1153604be855SAndy Yan 	u32 actual_w, actual_h, dsp_w, dsp_h;
1154604be855SAndy Yan 	u32 act_info, dsp_info;
1155604be855SAndy Yan 	u32 format;
1156604be855SAndy Yan 	u32 afbc_format;
1157604be855SAndy Yan 	u32 rb_swap;
1158604be855SAndy Yan 	u32 uv_swap;
1159604be855SAndy Yan 	struct drm_rect *src = &pstate->src;
1160604be855SAndy Yan 	struct drm_rect *dest = &pstate->dst;
1161604be855SAndy Yan 	u32 afbc_tile_num;
1162604be855SAndy Yan 	u32 transform_offset;
1163604be855SAndy Yan 	bool dither_up;
1164604be855SAndy Yan 	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
1165604be855SAndy Yan 	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
1166604be855SAndy Yan 	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
1167604be855SAndy Yan 	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
1168604be855SAndy Yan 	struct rockchip_gem_object *rk_obj;
1169604be855SAndy Yan 	unsigned long offset;
1170bebad6bdSAndy Yan 	bool half_block_en;
1171604be855SAndy Yan 	bool afbc_en;
1172604be855SAndy Yan 	dma_addr_t yrgb_mst;
1173604be855SAndy Yan 	dma_addr_t uv_mst;
1174604be855SAndy Yan 
1175604be855SAndy Yan 	/*
1176604be855SAndy Yan 	 * can't update plane when vop2 is disabled.
1177604be855SAndy Yan 	 */
1178604be855SAndy Yan 	if (WARN_ON(!crtc))
1179604be855SAndy Yan 		return;
1180604be855SAndy Yan 
1181604be855SAndy Yan 	if (!pstate->visible) {
1182604be855SAndy Yan 		vop2_plane_atomic_disable(plane, state);
1183604be855SAndy Yan 		return;
1184604be855SAndy Yan 	}
1185604be855SAndy Yan 
1186604be855SAndy Yan 	afbc_en = rockchip_afbc(plane, fb->modifier);
1187604be855SAndy Yan 
1188604be855SAndy Yan 	offset = (src->x1 >> 16) * fb->format->cpp[0];
1189604be855SAndy Yan 
1190604be855SAndy Yan 	/*
1191604be855SAndy Yan 	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
1192604be855SAndy Yan 	 */
1193604be855SAndy Yan 	if (afbc_en)
1194604be855SAndy Yan 		offset = 0;
1195604be855SAndy Yan 	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
1196604be855SAndy Yan 		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
1197604be855SAndy Yan 	else
1198604be855SAndy Yan 		offset += (src->y1 >> 16) * fb->pitches[0];
1199604be855SAndy Yan 
1200604be855SAndy Yan 	rk_obj = to_rockchip_obj(fb->obj[0]);
1201604be855SAndy Yan 
1202604be855SAndy Yan 	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
1203604be855SAndy Yan 	if (fb->format->is_yuv) {
1204604be855SAndy Yan 		int hsub = fb->format->hsub;
1205604be855SAndy Yan 		int vsub = fb->format->vsub;
1206604be855SAndy Yan 
1207604be855SAndy Yan 		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
1208604be855SAndy Yan 		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
1209604be855SAndy Yan 
1210604be855SAndy Yan 		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
1211604be855SAndy Yan 			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
1212604be855SAndy Yan 
1213604be855SAndy Yan 		rk_obj = to_rockchip_obj(fb->obj[0]);
1214604be855SAndy Yan 		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
1215604be855SAndy Yan 	}
1216604be855SAndy Yan 
1217604be855SAndy Yan 	actual_w = drm_rect_width(src) >> 16;
1218604be855SAndy Yan 	actual_h = drm_rect_height(src) >> 16;
1219604be855SAndy Yan 	dsp_w = drm_rect_width(dest);
1220604be855SAndy Yan 
1221604be855SAndy Yan 	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
1222604be855SAndy Yan 		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
1223604be855SAndy Yan 			vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
1224604be855SAndy Yan 		dsp_w = adjusted_mode->hdisplay - dest->x1;
1225604be855SAndy Yan 		if (dsp_w < 4)
1226604be855SAndy Yan 			dsp_w = 4;
1227604be855SAndy Yan 		actual_w = dsp_w * actual_w / drm_rect_width(dest);
1228604be855SAndy Yan 	}
1229604be855SAndy Yan 
1230604be855SAndy Yan 	dsp_h = drm_rect_height(dest);
1231604be855SAndy Yan 
1232604be855SAndy Yan 	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
1233604be855SAndy Yan 		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
1234604be855SAndy Yan 			vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
1235604be855SAndy Yan 		dsp_h = adjusted_mode->vdisplay - dest->y1;
1236604be855SAndy Yan 		if (dsp_h < 4)
1237604be855SAndy Yan 			dsp_h = 4;
1238604be855SAndy Yan 		actual_h = dsp_h * actual_h / drm_rect_height(dest);
1239604be855SAndy Yan 	}
1240604be855SAndy Yan 
1241604be855SAndy Yan 	/*
1242604be855SAndy Yan 	 * This is workaround solution for IC design:
1243604be855SAndy Yan 	 * esmart can't support scale down when actual_w % 16 == 1.
1244604be855SAndy Yan 	 */
1245604be855SAndy Yan 	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
1246604be855SAndy Yan 		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
1247604be855SAndy Yan 			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
1248604be855SAndy Yan 				vp->id, win->data->name, actual_w);
1249604be855SAndy Yan 			actual_w -= 1;
1250604be855SAndy Yan 		}
1251604be855SAndy Yan 	}
1252604be855SAndy Yan 
1253604be855SAndy Yan 	if (afbc_en && actual_w % 4) {
1254604be855SAndy Yan 		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
1255604be855SAndy Yan 			vp->id, win->data->name, actual_w);
1256604be855SAndy Yan 		actual_w = ALIGN_DOWN(actual_w, 4);
1257604be855SAndy Yan 	}
1258604be855SAndy Yan 
1259604be855SAndy Yan 	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
1260604be855SAndy Yan 	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
1261604be855SAndy Yan 
1262604be855SAndy Yan 	format = vop2_convert_format(fb->format->format);
1263bebad6bdSAndy Yan 	half_block_en = vop2_half_block_enable(pstate);
1264604be855SAndy Yan 
1265604be855SAndy Yan 	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
1266604be855SAndy Yan 		vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
1267604be855SAndy Yan 		dest->x1, dest->y1,
1268604be855SAndy Yan 		&fb->format->format,
1269604be855SAndy Yan 		afbc_en ? "AFBC" : "", &yrgb_mst);
1270604be855SAndy Yan 
1271bebad6bdSAndy Yan 	if (vop2_cluster_window(win))
1272bebad6bdSAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en);
1273bebad6bdSAndy Yan 
1274604be855SAndy Yan 	if (afbc_en) {
1275604be855SAndy Yan 		u32 stride;
1276604be855SAndy Yan 
1277604be855SAndy Yan 		/* the afbc superblock is 16 x 16 */
1278604be855SAndy Yan 		afbc_format = vop2_convert_afbc_format(fb->format->format);
1279604be855SAndy Yan 
1280604be855SAndy Yan 		/* Enable color transform for YTR */
1281604be855SAndy Yan 		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
1282604be855SAndy Yan 			afbc_format |= (1 << 4);
1283604be855SAndy Yan 
1284604be855SAndy Yan 		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
1285604be855SAndy Yan 
1286604be855SAndy Yan 		/*
1287604be855SAndy Yan 		 * AFBC pic_vir_width is count by pixel, this is different
1288604be855SAndy Yan 		 * with WIN_VIR_STRIDE.
1289604be855SAndy Yan 		 */
1290604be855SAndy Yan 		stride = (fb->pitches[0] << 3) / bpp;
1291604be855SAndy Yan 		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
1292f0eaf60cSColin Ian King 			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
1293604be855SAndy Yan 				vp->id, win->data->name, stride);
1294604be855SAndy Yan 
1295604be855SAndy Yan 		uv_swap = vop2_afbc_uv_swap(fb->format->format);
1296604be855SAndy Yan 		/*
1297604be855SAndy Yan 		 * This is a workaround for crazy IC design, Cluster
1298604be855SAndy Yan 		 * and Esmart/Smart use different format configuration map:
1299604be855SAndy Yan 		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
1300604be855SAndy Yan 		 *
1301604be855SAndy Yan 		 * This is one thing we can make the convert simple:
1302604be855SAndy Yan 		 * AFBCD decode all the YUV data to YUV444. So we just
1303604be855SAndy Yan 		 * set all the yuv 10 bit to YUV444_10.
1304604be855SAndy Yan 		 */
1305604be855SAndy Yan 		if (fb->format->is_yuv && bpp == 10)
1306604be855SAndy Yan 			format = VOP2_CLUSTER_YUV444_10;
1307604be855SAndy Yan 
1308604be855SAndy Yan 		if (vop2_cluster_window(win))
1309604be855SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
1310604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
1311604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
1312604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
1313604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
1314bebad6bdSAndy Yan 		transform_offset = vop2_afbc_transform_offset(pstate, half_block_en);
1315604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
1316604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
1317604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
1318604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
1319604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
1320604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
1321604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
1322604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
1323604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
1324604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
1325604be855SAndy Yan 	} else {
132620529a68SAndy Yan 		if (vop2_cluster_window(win)) {
132720529a68SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 0);
132820529a68SAndy Yan 			vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, 0);
132920529a68SAndy Yan 		}
133020529a68SAndy Yan 
1331604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
1332604be855SAndy Yan 	}
1333604be855SAndy Yan 
1334604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
1335604be855SAndy Yan 
1336604be855SAndy Yan 	if (rotate_90 || rotate_270) {
1337604be855SAndy Yan 		act_info = swahw32(act_info);
1338604be855SAndy Yan 		actual_w = drm_rect_height(src) >> 16;
1339604be855SAndy Yan 		actual_h = drm_rect_width(src) >> 16;
1340604be855SAndy Yan 	}
1341604be855SAndy Yan 
1342604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_FORMAT, format);
1343604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
1344604be855SAndy Yan 
1345604be855SAndy Yan 	rb_swap = vop2_win_rb_swap(fb->format->format);
1346604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
1347604be855SAndy Yan 	if (!vop2_cluster_window(win)) {
1348604be855SAndy Yan 		uv_swap = vop2_win_uv_swap(fb->format->format);
1349604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
1350604be855SAndy Yan 	}
1351604be855SAndy Yan 
1352604be855SAndy Yan 	if (fb->format->is_yuv) {
1353604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
1354604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
1355604be855SAndy Yan 	}
1356604be855SAndy Yan 
1357604be855SAndy Yan 	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
1358604be855SAndy Yan 	if (!vop2_cluster_window(win))
1359604be855SAndy Yan 		vop2_plane_setup_color_key(plane, 0);
1360604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
1361604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
1362604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
1363604be855SAndy Yan 
1364604be855SAndy Yan 	vop2_setup_csc_mode(vp, win, pstate);
1365604be855SAndy Yan 
1366604be855SAndy Yan 	dither_up = vop2_win_dither_up(fb->format->format);
1367604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
1368604be855SAndy Yan 
1369604be855SAndy Yan 	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
1370604be855SAndy Yan 
1371604be855SAndy Yan 	if (vop2_cluster_window(win)) {
1372604be855SAndy Yan 		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
1373604be855SAndy Yan 
1374604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
1375604be855SAndy Yan 		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
1376604be855SAndy Yan 	}
1377604be855SAndy Yan }
1378604be855SAndy Yan 
1379604be855SAndy Yan static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
1380604be855SAndy Yan 	.atomic_check = vop2_plane_atomic_check,
1381604be855SAndy Yan 	.atomic_update = vop2_plane_atomic_update,
1382604be855SAndy Yan 	.atomic_disable = vop2_plane_atomic_disable,
1383604be855SAndy Yan };
1384604be855SAndy Yan 
1385604be855SAndy Yan static const struct drm_plane_funcs vop2_plane_funcs = {
1386604be855SAndy Yan 	.update_plane	= drm_atomic_helper_update_plane,
1387604be855SAndy Yan 	.disable_plane	= drm_atomic_helper_disable_plane,
1388604be855SAndy Yan 	.destroy = drm_plane_cleanup,
1389604be855SAndy Yan 	.reset = drm_atomic_helper_plane_reset,
1390604be855SAndy Yan 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1391604be855SAndy Yan 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
1392604be855SAndy Yan 	.format_mod_supported = rockchip_vop2_mod_supported,
1393604be855SAndy Yan };
1394604be855SAndy Yan 
1395604be855SAndy Yan static int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
1396604be855SAndy Yan {
1397604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1398604be855SAndy Yan 
1399604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
1400604be855SAndy Yan 
1401604be855SAndy Yan 	return 0;
1402604be855SAndy Yan }
1403604be855SAndy Yan 
1404604be855SAndy Yan static void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
1405604be855SAndy Yan {
1406604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1407604be855SAndy Yan 
1408604be855SAndy Yan 	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
1409604be855SAndy Yan }
1410604be855SAndy Yan 
1411604be855SAndy Yan static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
1412604be855SAndy Yan 				 const struct drm_display_mode *mode,
1413604be855SAndy Yan 				 struct drm_display_mode *adj_mode)
1414604be855SAndy Yan {
1415604be855SAndy Yan 	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
1416604be855SAndy Yan 					CRTC_STEREO_DOUBLE);
1417604be855SAndy Yan 
1418604be855SAndy Yan 	return true;
1419604be855SAndy Yan }
1420604be855SAndy Yan 
1421604be855SAndy Yan static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
1422604be855SAndy Yan {
1423604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1424604be855SAndy Yan 
1425604be855SAndy Yan 	switch (vcstate->bus_format) {
1426604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB565_1X16:
1427604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1428604be855SAndy Yan 		break;
1429604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X18:
1430604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
1431604be855SAndy Yan 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
1432604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
1433604be855SAndy Yan 		*dsp_ctrl |= RGB888_TO_RGB666;
1434604be855SAndy Yan 		break;
1435604be855SAndy Yan 	case MEDIA_BUS_FMT_YUV8_1X24:
1436604be855SAndy Yan 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1437604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1438604be855SAndy Yan 		break;
1439604be855SAndy Yan 	default:
1440604be855SAndy Yan 		break;
1441604be855SAndy Yan 	}
1442604be855SAndy Yan 
1443604be855SAndy Yan 	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
1444604be855SAndy Yan 		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
1445604be855SAndy Yan 
1446604be855SAndy Yan 	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
1447604be855SAndy Yan 				DITHER_DOWN_ALLEGRO);
1448604be855SAndy Yan }
1449604be855SAndy Yan 
1450604be855SAndy Yan static void vop2_post_config(struct drm_crtc *crtc)
1451604be855SAndy Yan {
1452604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1453604be855SAndy Yan 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1454604be855SAndy Yan 	u16 vtotal = mode->crtc_vtotal;
1455604be855SAndy Yan 	u16 hdisplay = mode->crtc_hdisplay;
1456604be855SAndy Yan 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1457604be855SAndy Yan 	u16 vdisplay = mode->crtc_vdisplay;
1458604be855SAndy Yan 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1459604be855SAndy Yan 	u32 left_margin = 100, right_margin = 100;
1460604be855SAndy Yan 	u32 top_margin = 100, bottom_margin = 100;
1461604be855SAndy Yan 	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
1462604be855SAndy Yan 	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
1463075a5b39SAndy Yan 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
1464604be855SAndy Yan 	u16 hact_end, vact_end;
1465604be855SAndy Yan 	u32 val;
1466075a5b39SAndy Yan 	u32 bg_dly;
1467075a5b39SAndy Yan 	u32 pre_scan_dly;
1468075a5b39SAndy Yan 
1469075a5b39SAndy Yan 	bg_dly = vp->data->pre_scan_max_dly[3];
1470075a5b39SAndy Yan 	vop2_writel(vp->vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
1471075a5b39SAndy Yan 		    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
1472075a5b39SAndy Yan 
1473075a5b39SAndy Yan 	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
1474075a5b39SAndy Yan 	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
1475604be855SAndy Yan 
1476604be855SAndy Yan 	vsize = rounddown(vsize, 2);
1477604be855SAndy Yan 	hsize = rounddown(hsize, 2);
1478604be855SAndy Yan 	hact_st += hdisplay * (100 - left_margin) / 200;
1479604be855SAndy Yan 	hact_end = hact_st + hsize;
1480604be855SAndy Yan 	val = hact_st << 16;
1481604be855SAndy Yan 	val |= hact_end;
1482604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
1483604be855SAndy Yan 	vact_st += vdisplay * (100 - top_margin) / 200;
1484604be855SAndy Yan 	vact_end = vact_st + vsize;
1485604be855SAndy Yan 	val = vact_st << 16;
1486604be855SAndy Yan 	val |= vact_end;
1487604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
1488604be855SAndy Yan 	val = scl_cal_scale2(vdisplay, vsize) << 16;
1489604be855SAndy Yan 	val |= scl_cal_scale2(hdisplay, hsize);
1490604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
1491604be855SAndy Yan 
1492604be855SAndy Yan 	val = 0;
1493604be855SAndy Yan 	if (hdisplay != hsize)
1494604be855SAndy Yan 		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
1495604be855SAndy Yan 	if (vdisplay != vsize)
1496604be855SAndy Yan 		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
1497604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
1498604be855SAndy Yan 
1499604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1500604be855SAndy Yan 		u16 vact_st_f1 = vtotal + vact_st + 1;
1501604be855SAndy Yan 		u16 vact_end_f1 = vact_st_f1 + vsize;
1502604be855SAndy Yan 
1503604be855SAndy Yan 		val = vact_st_f1 << 16 | vact_end_f1;
1504604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
1505604be855SAndy Yan 	}
1506604be855SAndy Yan 
1507604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
1508604be855SAndy Yan }
1509604be855SAndy Yan 
1510604be855SAndy Yan static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
1511604be855SAndy Yan 				u32 polflags)
1512604be855SAndy Yan {
1513604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1514604be855SAndy Yan 	u32 die, dip;
1515604be855SAndy Yan 
1516604be855SAndy Yan 	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
1517604be855SAndy Yan 	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
1518604be855SAndy Yan 
1519604be855SAndy Yan 	switch (id) {
1520604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_RGB0:
1521604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
1522604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
1523604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
152466ab5757SGerald Loacker 		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
152566ab5757SGerald Loacker 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
1526604be855SAndy Yan 		if (polflags & POLFLAG_DCLK_INV)
1527604be855SAndy Yan 			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
1528604be855SAndy Yan 		else
1529604be855SAndy Yan 			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
1530604be855SAndy Yan 		break;
1531604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_HDMI0:
1532604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
1533604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
1534604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
153535b513a7SSascha Hauer 		dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL;
153635b513a7SSascha Hauer 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags);
1537604be855SAndy Yan 		break;
1538604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_EDP0:
1539604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
1540604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
1541604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
154235b513a7SSascha Hauer 		dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL;
154335b513a7SSascha Hauer 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags);
1544604be855SAndy Yan 		break;
1545604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_MIPI0:
1546604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
1547604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
1548604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
1549604be855SAndy Yan 		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
1550604be855SAndy Yan 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
1551604be855SAndy Yan 		break;
1552604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_MIPI1:
1553604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
1554604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
1555604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
1556604be855SAndy Yan 		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
1557604be855SAndy Yan 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
1558604be855SAndy Yan 		break;
1559604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_LVDS0:
1560604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
1561604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
1562604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
1563604be855SAndy Yan 		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
1564604be855SAndy Yan 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
1565604be855SAndy Yan 		break;
1566604be855SAndy Yan 	case ROCKCHIP_VOP2_EP_LVDS1:
1567604be855SAndy Yan 		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
1568604be855SAndy Yan 		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
1569604be855SAndy Yan 			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
1570604be855SAndy Yan 		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
1571604be855SAndy Yan 		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
1572604be855SAndy Yan 		break;
1573604be855SAndy Yan 	default:
1574604be855SAndy Yan 		drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
1575604be855SAndy Yan 		return;
1576813bb91dSYang Li 	}
1577604be855SAndy Yan 
1578604be855SAndy Yan 	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
1579604be855SAndy Yan 
1580604be855SAndy Yan 	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
1581604be855SAndy Yan 	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
1582604be855SAndy Yan }
1583604be855SAndy Yan 
1584604be855SAndy Yan static int us_to_vertical_line(struct drm_display_mode *mode, int us)
1585604be855SAndy Yan {
1586604be855SAndy Yan 	return us * mode->clock / mode->htotal / 1000;
1587604be855SAndy Yan }
1588604be855SAndy Yan 
1589604be855SAndy Yan static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
1590604be855SAndy Yan 				    struct drm_atomic_state *state)
1591604be855SAndy Yan {
1592604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1593604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1594604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
1595604be855SAndy Yan 	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
1596604be855SAndy Yan 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1597604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
1598604be855SAndy Yan 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1599604be855SAndy Yan 	unsigned long clock = mode->crtc_clock * 1000;
1600604be855SAndy Yan 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
1601604be855SAndy Yan 	u16 hdisplay = mode->crtc_hdisplay;
1602604be855SAndy Yan 	u16 htotal = mode->crtc_htotal;
1603604be855SAndy Yan 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1604604be855SAndy Yan 	u16 hact_end = hact_st + hdisplay;
1605604be855SAndy Yan 	u16 vdisplay = mode->crtc_vdisplay;
1606604be855SAndy Yan 	u16 vtotal = mode->crtc_vtotal;
1607604be855SAndy Yan 	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
1608604be855SAndy Yan 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1609604be855SAndy Yan 	u16 vact_end = vact_st + vdisplay;
1610604be855SAndy Yan 	u8 out_mode;
1611604be855SAndy Yan 	u32 dsp_ctrl = 0;
1612604be855SAndy Yan 	int act_end;
1613604be855SAndy Yan 	u32 val, polflags;
1614604be855SAndy Yan 	int ret;
1615604be855SAndy Yan 	struct drm_encoder *encoder;
1616604be855SAndy Yan 
1617604be855SAndy Yan 	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
1618604be855SAndy Yan 		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
1619604be855SAndy Yan 		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
1620604be855SAndy Yan 
1621604be855SAndy Yan 	vop2_lock(vop2);
1622604be855SAndy Yan 
1623604be855SAndy Yan 	ret = clk_prepare_enable(vp->dclk);
1624604be855SAndy Yan 	if (ret < 0) {
1625604be855SAndy Yan 		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
1626604be855SAndy Yan 			vp->id, ret);
162798526c5bSDan Carpenter 		vop2_unlock(vop2);
1628604be855SAndy Yan 		return;
1629604be855SAndy Yan 	}
1630604be855SAndy Yan 
1631604be855SAndy Yan 	if (!vop2->enable_count)
1632604be855SAndy Yan 		vop2_enable(vop2);
1633604be855SAndy Yan 
1634604be855SAndy Yan 	vop2->enable_count++;
1635604be855SAndy Yan 
1636dd49ee46SAndy Yan 	vcstate->yuv_overlay = is_yuv_output(vcstate->bus_format);
1637dd49ee46SAndy Yan 
1638604be855SAndy Yan 	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
1639604be855SAndy Yan 
1640604be855SAndy Yan 	polflags = 0;
1641604be855SAndy Yan 	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
1642604be855SAndy Yan 		polflags |= POLFLAG_DCLK_INV;
1643604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
1644604be855SAndy Yan 		polflags |= BIT(HSYNC_POSITIVE);
1645604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
1646604be855SAndy Yan 		polflags |= BIT(VSYNC_POSITIVE);
1647604be855SAndy Yan 
1648604be855SAndy Yan 	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
1649604be855SAndy Yan 		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
1650604be855SAndy Yan 
1651604be855SAndy Yan 		rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
1652604be855SAndy Yan 	}
1653604be855SAndy Yan 
1654604be855SAndy Yan 	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
1655604be855SAndy Yan 	    !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
1656604be855SAndy Yan 		out_mode = ROCKCHIP_OUT_MODE_P888;
1657604be855SAndy Yan 	else
1658604be855SAndy Yan 		out_mode = vcstate->output_mode;
1659604be855SAndy Yan 
1660604be855SAndy Yan 	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
1661604be855SAndy Yan 
1662604be855SAndy Yan 	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
1663604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
1664604be855SAndy Yan 
1665dd49ee46SAndy Yan 	if (vcstate->yuv_overlay)
1666604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
1667604be855SAndy Yan 
1668604be855SAndy Yan 	vop2_dither_setup(crtc, &dsp_ctrl);
1669604be855SAndy Yan 
1670604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
1671604be855SAndy Yan 	val = hact_st << 16;
1672604be855SAndy Yan 	val |= hact_end;
1673604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
1674604be855SAndy Yan 
1675604be855SAndy Yan 	val = vact_st << 16;
1676604be855SAndy Yan 	val |= vact_end;
1677604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
1678604be855SAndy Yan 
1679604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1680604be855SAndy Yan 		u16 vact_st_f1 = vtotal + vact_st + 1;
1681604be855SAndy Yan 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
1682604be855SAndy Yan 
1683604be855SAndy Yan 		val = vact_st_f1 << 16 | vact_end_f1;
1684604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
1685604be855SAndy Yan 
1686604be855SAndy Yan 		val = vtotal << 16 | (vtotal + vsync_len);
1687604be855SAndy Yan 		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
1688604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
1689604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
1690604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
1691604be855SAndy Yan 		vtotal += vtotal + 1;
1692604be855SAndy Yan 		act_end = vact_end_f1;
1693604be855SAndy Yan 	} else {
1694604be855SAndy Yan 		act_end = vact_end;
1695604be855SAndy Yan 	}
1696604be855SAndy Yan 
1697604be855SAndy Yan 	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
1698604be855SAndy Yan 		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
1699604be855SAndy Yan 
1700604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
1701604be855SAndy Yan 
1702604be855SAndy Yan 	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
1703604be855SAndy Yan 		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
1704604be855SAndy Yan 		clock *= 2;
1705604be855SAndy Yan 	}
1706604be855SAndy Yan 
1707604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
1708604be855SAndy Yan 
1709604be855SAndy Yan 	clk_set_rate(vp->dclk, clock);
1710604be855SAndy Yan 
1711604be855SAndy Yan 	vop2_post_config(crtc);
1712604be855SAndy Yan 
1713604be855SAndy Yan 	vop2_cfg_done(vp);
1714604be855SAndy Yan 
1715604be855SAndy Yan 	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
1716604be855SAndy Yan 
1717604be855SAndy Yan 	drm_crtc_vblank_on(crtc);
1718604be855SAndy Yan 
1719604be855SAndy Yan 	vop2_unlock(vop2);
1720604be855SAndy Yan }
1721604be855SAndy Yan 
1722604be855SAndy Yan static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
1723604be855SAndy Yan 				  struct drm_atomic_state *state)
1724604be855SAndy Yan {
1725604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
1726604be855SAndy Yan 	struct drm_plane *plane;
1727604be855SAndy Yan 	int nplanes = 0;
1728604be855SAndy Yan 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1729604be855SAndy Yan 
1730604be855SAndy Yan 	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
1731604be855SAndy Yan 		nplanes++;
1732604be855SAndy Yan 
1733604be855SAndy Yan 	if (nplanes > vp->nlayers)
1734604be855SAndy Yan 		return -EINVAL;
1735604be855SAndy Yan 
1736604be855SAndy Yan 	return 0;
1737604be855SAndy Yan }
1738604be855SAndy Yan 
1739604be855SAndy Yan static bool is_opaque(u16 alpha)
1740604be855SAndy Yan {
1741604be855SAndy Yan 	return (alpha >> 8) == 0xff;
1742604be855SAndy Yan }
1743604be855SAndy Yan 
1744604be855SAndy Yan static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
1745604be855SAndy Yan 			     struct vop2_alpha *alpha)
1746604be855SAndy Yan {
1747604be855SAndy Yan 	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
1748604be855SAndy Yan 	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
1749604be855SAndy Yan 	int src_color_mode = alpha_config->src_premulti_en ?
1750604be855SAndy Yan 				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
1751604be855SAndy Yan 	int dst_color_mode = alpha_config->dst_premulti_en ?
1752604be855SAndy Yan 				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
1753604be855SAndy Yan 
1754604be855SAndy Yan 	alpha->src_color_ctrl.val = 0;
1755604be855SAndy Yan 	alpha->dst_color_ctrl.val = 0;
1756604be855SAndy Yan 	alpha->src_alpha_ctrl.val = 0;
1757604be855SAndy Yan 	alpha->dst_alpha_ctrl.val = 0;
1758604be855SAndy Yan 
1759604be855SAndy Yan 	if (!alpha_config->src_pixel_alpha_en)
1760604be855SAndy Yan 		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
1761604be855SAndy Yan 	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
1762604be855SAndy Yan 		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
1763604be855SAndy Yan 	else
1764604be855SAndy Yan 		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
1765604be855SAndy Yan 
1766604be855SAndy Yan 	alpha->src_color_ctrl.bits.alpha_en = 1;
1767604be855SAndy Yan 
1768604be855SAndy Yan 	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
1769604be855SAndy Yan 		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
1770604be855SAndy Yan 		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
1771604be855SAndy Yan 	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
1772604be855SAndy Yan 		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
1773604be855SAndy Yan 		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
1774604be855SAndy Yan 	} else {
1775604be855SAndy Yan 		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
1776604be855SAndy Yan 		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
1777604be855SAndy Yan 	}
1778604be855SAndy Yan 	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
1779604be855SAndy Yan 	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
1780604be855SAndy Yan 	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
1781604be855SAndy Yan 
1782604be855SAndy Yan 	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
1783604be855SAndy Yan 	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
1784604be855SAndy Yan 	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
1785604be855SAndy Yan 	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
1786604be855SAndy Yan 	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
1787604be855SAndy Yan 	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
1788604be855SAndy Yan 
1789604be855SAndy Yan 	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
1790604be855SAndy Yan 	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
1791604be855SAndy Yan 	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
1792604be855SAndy Yan 	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
1793604be855SAndy Yan 
1794604be855SAndy Yan 	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
1795604be855SAndy Yan 	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
1796604be855SAndy Yan 		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
1797604be855SAndy Yan 	else
1798604be855SAndy Yan 		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
1799604be855SAndy Yan 	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
1800604be855SAndy Yan 	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
1801604be855SAndy Yan }
1802604be855SAndy Yan 
1803604be855SAndy Yan static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
1804604be855SAndy Yan {
1805604be855SAndy Yan 	struct vop2_video_port *vp;
1806604be855SAndy Yan 	int used_layer = 0;
1807604be855SAndy Yan 	int i;
1808604be855SAndy Yan 
1809604be855SAndy Yan 	for (i = 0; i < port_id; i++) {
1810604be855SAndy Yan 		vp = &vop2->vps[i];
1811604be855SAndy Yan 		used_layer += hweight32(vp->win_mask);
1812604be855SAndy Yan 	}
1813604be855SAndy Yan 
1814604be855SAndy Yan 	return used_layer;
1815604be855SAndy Yan }
1816604be855SAndy Yan 
1817604be855SAndy Yan static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
1818604be855SAndy Yan {
1819604be855SAndy Yan 	u32 offset = (main_win->data->phys_id * 0x10);
1820604be855SAndy Yan 	struct vop2_alpha_config alpha_config;
1821604be855SAndy Yan 	struct vop2_alpha alpha;
1822604be855SAndy Yan 	struct drm_plane_state *bottom_win_pstate;
1823604be855SAndy Yan 	bool src_pixel_alpha_en = false;
1824604be855SAndy Yan 	u16 src_glb_alpha_val, dst_glb_alpha_val;
1825604be855SAndy Yan 	bool premulti_en = false;
1826604be855SAndy Yan 	bool swap = false;
1827604be855SAndy Yan 
1828604be855SAndy Yan 	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
1829604be855SAndy Yan 	bottom_win_pstate = main_win->base.state;
1830604be855SAndy Yan 	src_glb_alpha_val = 0;
1831604be855SAndy Yan 	dst_glb_alpha_val = main_win->base.state->alpha;
1832604be855SAndy Yan 
1833604be855SAndy Yan 	if (!bottom_win_pstate->fb)
1834604be855SAndy Yan 		return;
1835604be855SAndy Yan 
1836604be855SAndy Yan 	alpha_config.src_premulti_en = premulti_en;
1837604be855SAndy Yan 	alpha_config.dst_premulti_en = false;
1838604be855SAndy Yan 	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
1839604be855SAndy Yan 	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
1840604be855SAndy Yan 	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
1841604be855SAndy Yan 	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
1842604be855SAndy Yan 	vop2_parse_alpha(&alpha_config, &alpha);
1843604be855SAndy Yan 
1844604be855SAndy Yan 	alpha.src_color_ctrl.bits.src_dst_swap = swap;
1845604be855SAndy Yan 	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
1846604be855SAndy Yan 		    alpha.src_color_ctrl.val);
1847604be855SAndy Yan 	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
1848604be855SAndy Yan 		    alpha.dst_color_ctrl.val);
1849604be855SAndy Yan 	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
1850604be855SAndy Yan 		    alpha.src_alpha_ctrl.val);
1851604be855SAndy Yan 	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
1852604be855SAndy Yan 		    alpha.dst_alpha_ctrl.val);
1853604be855SAndy Yan }
1854604be855SAndy Yan 
1855604be855SAndy Yan static void vop2_setup_alpha(struct vop2_video_port *vp)
1856604be855SAndy Yan {
1857604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1858604be855SAndy Yan 	struct drm_framebuffer *fb;
1859604be855SAndy Yan 	struct vop2_alpha_config alpha_config;
1860604be855SAndy Yan 	struct vop2_alpha alpha;
1861604be855SAndy Yan 	struct drm_plane *plane;
1862604be855SAndy Yan 	int pixel_alpha_en;
1863604be855SAndy Yan 	int premulti_en, gpremulti_en = 0;
1864604be855SAndy Yan 	int mixer_id;
1865604be855SAndy Yan 	u32 offset;
1866604be855SAndy Yan 	bool bottom_layer_alpha_en = false;
1867604be855SAndy Yan 	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
1868604be855SAndy Yan 
1869604be855SAndy Yan 	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
1870604be855SAndy Yan 	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
1871604be855SAndy Yan 
1872604be855SAndy Yan 	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
1873604be855SAndy Yan 		struct vop2_win *win = to_vop2_win(plane);
1874604be855SAndy Yan 
1875604be855SAndy Yan 		if (plane->state->normalized_zpos == 0 &&
1876604be855SAndy Yan 		    !is_opaque(plane->state->alpha) &&
1877604be855SAndy Yan 		    !vop2_cluster_window(win)) {
1878604be855SAndy Yan 			/*
1879604be855SAndy Yan 			 * If bottom layer have global alpha effect [except cluster layer,
1880604be855SAndy Yan 			 * because cluster have deal with bottom layer global alpha value
1881604be855SAndy Yan 			 * at cluster mix], bottom layer mix need deal with global alpha.
1882604be855SAndy Yan 			 */
1883604be855SAndy Yan 			bottom_layer_alpha_en = true;
1884604be855SAndy Yan 			dst_global_alpha = plane->state->alpha;
1885604be855SAndy Yan 		}
1886604be855SAndy Yan 	}
1887604be855SAndy Yan 
1888604be855SAndy Yan 	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
1889604be855SAndy Yan 		struct vop2_win *win = to_vop2_win(plane);
1890604be855SAndy Yan 		int zpos = plane->state->normalized_zpos;
1891604be855SAndy Yan 
1892604be855SAndy Yan 		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
1893604be855SAndy Yan 			premulti_en = 1;
1894604be855SAndy Yan 		else
1895604be855SAndy Yan 			premulti_en = 0;
1896604be855SAndy Yan 
1897604be855SAndy Yan 		plane = &win->base;
1898604be855SAndy Yan 		fb = plane->state->fb;
1899604be855SAndy Yan 
1900604be855SAndy Yan 		pixel_alpha_en = fb->format->has_alpha;
1901604be855SAndy Yan 
1902604be855SAndy Yan 		alpha_config.src_premulti_en = premulti_en;
1903604be855SAndy Yan 
1904604be855SAndy Yan 		if (bottom_layer_alpha_en && zpos == 1) {
1905604be855SAndy Yan 			gpremulti_en = premulti_en;
1906604be855SAndy Yan 			/* Cd = Cs + (1 - As) * Cd * Agd */
1907604be855SAndy Yan 			alpha_config.dst_premulti_en = false;
1908604be855SAndy Yan 			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
1909604be855SAndy Yan 			alpha_config.src_glb_alpha_value = plane->state->alpha;
1910604be855SAndy Yan 			alpha_config.dst_glb_alpha_value = dst_global_alpha;
1911604be855SAndy Yan 		} else if (vop2_cluster_window(win)) {
1912604be855SAndy Yan 			/* Mix output data only have pixel alpha */
1913604be855SAndy Yan 			alpha_config.dst_premulti_en = true;
1914604be855SAndy Yan 			alpha_config.src_pixel_alpha_en = true;
1915604be855SAndy Yan 			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
1916604be855SAndy Yan 			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
1917604be855SAndy Yan 		} else {
1918604be855SAndy Yan 			/* Cd = Cs + (1 - As) * Cd */
1919604be855SAndy Yan 			alpha_config.dst_premulti_en = true;
1920604be855SAndy Yan 			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
1921604be855SAndy Yan 			alpha_config.src_glb_alpha_value = plane->state->alpha;
1922604be855SAndy Yan 			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
1923604be855SAndy Yan 		}
1924604be855SAndy Yan 
1925604be855SAndy Yan 		vop2_parse_alpha(&alpha_config, &alpha);
1926604be855SAndy Yan 
1927604be855SAndy Yan 		offset = (mixer_id + zpos - 1) * 0x10;
1928604be855SAndy Yan 		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
1929604be855SAndy Yan 			    alpha.src_color_ctrl.val);
1930604be855SAndy Yan 		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
1931604be855SAndy Yan 			    alpha.dst_color_ctrl.val);
1932604be855SAndy Yan 		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
1933604be855SAndy Yan 			    alpha.src_alpha_ctrl.val);
1934604be855SAndy Yan 		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
1935604be855SAndy Yan 			    alpha.dst_alpha_ctrl.val);
1936604be855SAndy Yan 	}
1937604be855SAndy Yan 
1938604be855SAndy Yan 	if (vp->id == 0) {
1939604be855SAndy Yan 		if (bottom_layer_alpha_en) {
1940604be855SAndy Yan 			/* Transfer pixel alpha to hdr mix */
1941604be855SAndy Yan 			alpha_config.src_premulti_en = gpremulti_en;
1942604be855SAndy Yan 			alpha_config.dst_premulti_en = true;
1943604be855SAndy Yan 			alpha_config.src_pixel_alpha_en = true;
1944604be855SAndy Yan 			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
1945604be855SAndy Yan 			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
1946604be855SAndy Yan 			vop2_parse_alpha(&alpha_config, &alpha);
1947604be855SAndy Yan 
1948604be855SAndy Yan 			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
1949604be855SAndy Yan 				    alpha.src_color_ctrl.val);
1950604be855SAndy Yan 			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
1951604be855SAndy Yan 				    alpha.dst_color_ctrl.val);
1952604be855SAndy Yan 			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
1953604be855SAndy Yan 				    alpha.src_alpha_ctrl.val);
1954604be855SAndy Yan 			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
1955604be855SAndy Yan 				    alpha.dst_alpha_ctrl.val);
1956604be855SAndy Yan 		} else {
1957604be855SAndy Yan 			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
1958604be855SAndy Yan 		}
1959604be855SAndy Yan 	}
1960604be855SAndy Yan }
1961604be855SAndy Yan 
1962604be855SAndy Yan static void vop2_setup_layer_mixer(struct vop2_video_port *vp)
1963604be855SAndy Yan {
1964604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
1965604be855SAndy Yan 	struct drm_plane *plane;
1966604be855SAndy Yan 	u32 layer_sel = 0;
1967604be855SAndy Yan 	u32 port_sel;
1968604be855SAndy Yan 	unsigned int nlayer, ofs;
1969dd49ee46SAndy Yan 	u32 ovl_ctrl;
1970604be855SAndy Yan 	int i;
1971604be855SAndy Yan 	struct vop2_video_port *vp0 = &vop2->vps[0];
1972604be855SAndy Yan 	struct vop2_video_port *vp1 = &vop2->vps[1];
1973604be855SAndy Yan 	struct vop2_video_port *vp2 = &vop2->vps[2];
1974dd49ee46SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
1975604be855SAndy Yan 
1976dd49ee46SAndy Yan 	ovl_ctrl = vop2_readl(vop2, RK3568_OVL_CTRL);
1977dd49ee46SAndy Yan 	ovl_ctrl |= RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD;
1978dd49ee46SAndy Yan 	if (vcstate->yuv_overlay)
1979dd49ee46SAndy Yan 		ovl_ctrl |= RK3568_OVL_CTRL__YUV_MODE(vp->id);
1980dd49ee46SAndy Yan 	else
1981dd49ee46SAndy Yan 		ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id);
1982dd49ee46SAndy Yan 
1983dd49ee46SAndy Yan 	vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl);
1984dd49ee46SAndy Yan 
1985604be855SAndy Yan 	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
1986604be855SAndy Yan 	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
1987604be855SAndy Yan 
1988604be855SAndy Yan 	if (vp0->nlayers)
1989604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
1990604be855SAndy Yan 				     vp0->nlayers - 1);
1991604be855SAndy Yan 	else
1992604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
1993604be855SAndy Yan 
1994604be855SAndy Yan 	if (vp1->nlayers)
1995604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
1996604be855SAndy Yan 				     (vp0->nlayers + vp1->nlayers - 1));
1997604be855SAndy Yan 	else
1998604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
1999604be855SAndy Yan 
2000604be855SAndy Yan 	if (vp2->nlayers)
2001604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
2002604be855SAndy Yan 			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
2003604be855SAndy Yan 	else
2004604be855SAndy Yan 		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
2005604be855SAndy Yan 
2006604be855SAndy Yan 	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
2007604be855SAndy Yan 
2008604be855SAndy Yan 	ofs = 0;
2009604be855SAndy Yan 	for (i = 0; i < vp->id; i++)
2010604be855SAndy Yan 		ofs += vop2->vps[i].nlayers;
2011604be855SAndy Yan 
2012604be855SAndy Yan 	nlayer = 0;
2013604be855SAndy Yan 	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
2014604be855SAndy Yan 		struct vop2_win *win = to_vop2_win(plane);
2015604be855SAndy Yan 
2016604be855SAndy Yan 		switch (win->data->phys_id) {
2017604be855SAndy Yan 		case ROCKCHIP_VOP2_CLUSTER0:
2018604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
2019604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
2020604be855SAndy Yan 			break;
2021604be855SAndy Yan 		case ROCKCHIP_VOP2_CLUSTER1:
2022604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
2023604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
2024604be855SAndy Yan 			break;
2025604be855SAndy Yan 		case ROCKCHIP_VOP2_ESMART0:
2026604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
2027604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
2028604be855SAndy Yan 			break;
2029604be855SAndy Yan 		case ROCKCHIP_VOP2_ESMART1:
2030604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
2031604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
2032604be855SAndy Yan 			break;
2033604be855SAndy Yan 		case ROCKCHIP_VOP2_SMART0:
2034604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
2035604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
2036604be855SAndy Yan 			break;
2037604be855SAndy Yan 		case ROCKCHIP_VOP2_SMART1:
2038604be855SAndy Yan 			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
2039604be855SAndy Yan 			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
2040604be855SAndy Yan 			break;
2041604be855SAndy Yan 		}
2042604be855SAndy Yan 
2043604be855SAndy Yan 		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs,
2044604be855SAndy Yan 							  0x7);
2045604be855SAndy Yan 		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs,
2046604be855SAndy Yan 							 win->data->layer_sel_id);
2047604be855SAndy Yan 		nlayer++;
2048604be855SAndy Yan 	}
2049604be855SAndy Yan 
2050604be855SAndy Yan 	/* configure unused layers to 0x5 (reserved) */
2051604be855SAndy Yan 	for (; nlayer < vp->nlayers; nlayer++) {
2052604be855SAndy Yan 		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
2053604be855SAndy Yan 		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
2054604be855SAndy Yan 	}
2055604be855SAndy Yan 
2056604be855SAndy Yan 	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
2057604be855SAndy Yan 	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
2058604be855SAndy Yan }
2059604be855SAndy Yan 
2060604be855SAndy Yan static void vop2_setup_dly_for_windows(struct vop2 *vop2)
2061604be855SAndy Yan {
2062604be855SAndy Yan 	struct vop2_win *win;
2063604be855SAndy Yan 	int i = 0;
2064604be855SAndy Yan 	u32 cdly = 0, sdly = 0;
2065604be855SAndy Yan 
2066604be855SAndy Yan 	for (i = 0; i < vop2->data->win_size; i++) {
2067604be855SAndy Yan 		u32 dly;
2068604be855SAndy Yan 
2069604be855SAndy Yan 		win = &vop2->win[i];
2070604be855SAndy Yan 		dly = win->delay;
2071604be855SAndy Yan 
2072604be855SAndy Yan 		switch (win->data->phys_id) {
2073604be855SAndy Yan 		case ROCKCHIP_VOP2_CLUSTER0:
2074604be855SAndy Yan 			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
2075604be855SAndy Yan 			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
2076604be855SAndy Yan 			break;
2077604be855SAndy Yan 		case ROCKCHIP_VOP2_CLUSTER1:
2078604be855SAndy Yan 			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
2079604be855SAndy Yan 			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
2080604be855SAndy Yan 			break;
2081604be855SAndy Yan 		case ROCKCHIP_VOP2_ESMART0:
2082604be855SAndy Yan 			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
2083604be855SAndy Yan 			break;
2084604be855SAndy Yan 		case ROCKCHIP_VOP2_ESMART1:
2085604be855SAndy Yan 			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
2086604be855SAndy Yan 			break;
2087604be855SAndy Yan 		case ROCKCHIP_VOP2_SMART0:
2088604be855SAndy Yan 			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
2089604be855SAndy Yan 			break;
2090604be855SAndy Yan 		case ROCKCHIP_VOP2_SMART1:
2091604be855SAndy Yan 			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
2092604be855SAndy Yan 			break;
2093604be855SAndy Yan 		}
2094604be855SAndy Yan 	}
2095604be855SAndy Yan 
2096604be855SAndy Yan 	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
2097604be855SAndy Yan 	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
2098604be855SAndy Yan }
2099604be855SAndy Yan 
2100604be855SAndy Yan static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
2101604be855SAndy Yan 				   struct drm_atomic_state *state)
2102604be855SAndy Yan {
2103604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
2104604be855SAndy Yan 	struct vop2 *vop2 = vp->vop2;
2105604be855SAndy Yan 	struct drm_plane *plane;
2106604be855SAndy Yan 
2107604be855SAndy Yan 	vp->win_mask = 0;
2108604be855SAndy Yan 
2109604be855SAndy Yan 	drm_atomic_crtc_for_each_plane(plane, crtc) {
2110604be855SAndy Yan 		struct vop2_win *win = to_vop2_win(plane);
2111604be855SAndy Yan 
2112604be855SAndy Yan 		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
2113604be855SAndy Yan 
2114604be855SAndy Yan 		vp->win_mask |= BIT(win->data->phys_id);
2115604be855SAndy Yan 
2116604be855SAndy Yan 		if (vop2_cluster_window(win))
2117604be855SAndy Yan 			vop2_setup_cluster_alpha(vop2, win);
2118604be855SAndy Yan 	}
2119604be855SAndy Yan 
2120604be855SAndy Yan 	if (!vp->win_mask)
2121604be855SAndy Yan 		return;
2122604be855SAndy Yan 
2123604be855SAndy Yan 	vop2_setup_layer_mixer(vp);
2124604be855SAndy Yan 	vop2_setup_alpha(vp);
2125604be855SAndy Yan 	vop2_setup_dly_for_windows(vop2);
2126604be855SAndy Yan }
2127604be855SAndy Yan 
2128604be855SAndy Yan static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
2129604be855SAndy Yan 				   struct drm_atomic_state *state)
2130604be855SAndy Yan {
2131604be855SAndy Yan 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
2132604be855SAndy Yan 
2133604be855SAndy Yan 	vop2_post_config(crtc);
2134604be855SAndy Yan 
2135604be855SAndy Yan 	vop2_cfg_done(vp);
2136604be855SAndy Yan 
2137604be855SAndy Yan 	spin_lock_irq(&crtc->dev->event_lock);
2138604be855SAndy Yan 
2139604be855SAndy Yan 	if (crtc->state->event) {
2140604be855SAndy Yan 		WARN_ON(drm_crtc_vblank_get(crtc));
2141604be855SAndy Yan 		vp->event = crtc->state->event;
2142604be855SAndy Yan 		crtc->state->event = NULL;
2143604be855SAndy Yan 	}
2144604be855SAndy Yan 
2145604be855SAndy Yan 	spin_unlock_irq(&crtc->dev->event_lock);
2146604be855SAndy Yan }
2147604be855SAndy Yan 
2148604be855SAndy Yan static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
2149604be855SAndy Yan 	.mode_fixup = vop2_crtc_mode_fixup,
2150604be855SAndy Yan 	.atomic_check = vop2_crtc_atomic_check,
2151604be855SAndy Yan 	.atomic_begin = vop2_crtc_atomic_begin,
2152604be855SAndy Yan 	.atomic_flush = vop2_crtc_atomic_flush,
2153604be855SAndy Yan 	.atomic_enable = vop2_crtc_atomic_enable,
2154604be855SAndy Yan 	.atomic_disable = vop2_crtc_atomic_disable,
2155604be855SAndy Yan };
2156604be855SAndy Yan 
2157604be855SAndy Yan static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
2158604be855SAndy Yan {
2159342f7e49SJonas Karlman 	struct rockchip_crtc_state *vcstate;
2160604be855SAndy Yan 
2161342f7e49SJonas Karlman 	if (WARN_ON(!crtc->state))
2162342f7e49SJonas Karlman 		return NULL;
2163604be855SAndy Yan 
2164342f7e49SJonas Karlman 	vcstate = kmemdup(to_rockchip_crtc_state(crtc->state),
2165342f7e49SJonas Karlman 			  sizeof(*vcstate), GFP_KERNEL);
2166604be855SAndy Yan 	if (!vcstate)
2167604be855SAndy Yan 		return NULL;
2168604be855SAndy Yan 
2169604be855SAndy Yan 	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
2170604be855SAndy Yan 
2171604be855SAndy Yan 	return &vcstate->base;
2172604be855SAndy Yan }
2173604be855SAndy Yan 
2174604be855SAndy Yan static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
2175604be855SAndy Yan 				    struct drm_crtc_state *state)
2176604be855SAndy Yan {
2177604be855SAndy Yan 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
2178604be855SAndy Yan 
2179604be855SAndy Yan 	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
2180604be855SAndy Yan 	kfree(vcstate);
2181604be855SAndy Yan }
2182604be855SAndy Yan 
21834d49d87bSJonas Karlman static void vop2_crtc_reset(struct drm_crtc *crtc)
21844d49d87bSJonas Karlman {
21854d49d87bSJonas Karlman 	struct rockchip_crtc_state *vcstate =
21864d49d87bSJonas Karlman 		kzalloc(sizeof(*vcstate), GFP_KERNEL);
21874d49d87bSJonas Karlman 
21884d49d87bSJonas Karlman 	if (crtc->state)
21894d49d87bSJonas Karlman 		vop2_crtc_destroy_state(crtc, crtc->state);
21904d49d87bSJonas Karlman 
21914d49d87bSJonas Karlman 	if (vcstate)
21924d49d87bSJonas Karlman 		__drm_atomic_helper_crtc_reset(crtc, &vcstate->base);
21934d49d87bSJonas Karlman 	else
21944d49d87bSJonas Karlman 		__drm_atomic_helper_crtc_reset(crtc, NULL);
21954d49d87bSJonas Karlman }
21964d49d87bSJonas Karlman 
2197604be855SAndy Yan static const struct drm_crtc_funcs vop2_crtc_funcs = {
2198604be855SAndy Yan 	.set_config = drm_atomic_helper_set_config,
2199604be855SAndy Yan 	.page_flip = drm_atomic_helper_page_flip,
2200604be855SAndy Yan 	.destroy = drm_crtc_cleanup,
2201604be855SAndy Yan 	.reset = vop2_crtc_reset,
2202604be855SAndy Yan 	.atomic_duplicate_state = vop2_crtc_duplicate_state,
2203604be855SAndy Yan 	.atomic_destroy_state = vop2_crtc_destroy_state,
2204604be855SAndy Yan 	.enable_vblank = vop2_crtc_enable_vblank,
2205604be855SAndy Yan 	.disable_vblank = vop2_crtc_disable_vblank,
2206604be855SAndy Yan };
2207604be855SAndy Yan 
2208604be855SAndy Yan static irqreturn_t vop2_isr(int irq, void *data)
2209604be855SAndy Yan {
2210604be855SAndy Yan 	struct vop2 *vop2 = data;
2211604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2212604be855SAndy Yan 	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
2213604be855SAndy Yan 	int ret = IRQ_NONE;
2214604be855SAndy Yan 	int i;
2215604be855SAndy Yan 
2216604be855SAndy Yan 	/*
2217604be855SAndy Yan 	 * The irq is shared with the iommu. If the runtime-pm state of the
2218604be855SAndy Yan 	 * vop2-device is disabled the irq has to be targeted at the iommu.
2219604be855SAndy Yan 	 */
2220604be855SAndy Yan 	if (!pm_runtime_get_if_in_use(vop2->dev))
2221604be855SAndy Yan 		return IRQ_NONE;
2222604be855SAndy Yan 
2223604be855SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
2224604be855SAndy Yan 		struct vop2_video_port *vp = &vop2->vps[i];
2225604be855SAndy Yan 		struct drm_crtc *crtc = &vp->crtc;
2226604be855SAndy Yan 		u32 irqs;
2227604be855SAndy Yan 
2228604be855SAndy Yan 		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
2229604be855SAndy Yan 		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
2230604be855SAndy Yan 
2231604be855SAndy Yan 		if (irqs & VP_INT_DSP_HOLD_VALID) {
2232604be855SAndy Yan 			complete(&vp->dsp_hold_completion);
2233604be855SAndy Yan 			ret = IRQ_HANDLED;
2234604be855SAndy Yan 		}
2235604be855SAndy Yan 
2236604be855SAndy Yan 		if (irqs & VP_INT_FS_FIELD) {
2237604be855SAndy Yan 			drm_crtc_handle_vblank(crtc);
2238604be855SAndy Yan 			spin_lock(&crtc->dev->event_lock);
2239604be855SAndy Yan 			if (vp->event) {
2240604be855SAndy Yan 				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
2241604be855SAndy Yan 
2242604be855SAndy Yan 				if (!(val & BIT(vp->id))) {
2243604be855SAndy Yan 					drm_crtc_send_vblank_event(crtc, vp->event);
2244604be855SAndy Yan 					vp->event = NULL;
2245604be855SAndy Yan 					drm_crtc_vblank_put(crtc);
2246604be855SAndy Yan 				}
2247604be855SAndy Yan 			}
2248604be855SAndy Yan 			spin_unlock(&crtc->dev->event_lock);
2249604be855SAndy Yan 
2250604be855SAndy Yan 			ret = IRQ_HANDLED;
2251604be855SAndy Yan 		}
2252604be855SAndy Yan 
2253604be855SAndy Yan 		if (irqs & VP_INT_POST_BUF_EMPTY) {
2254604be855SAndy Yan 			drm_err_ratelimited(vop2->drm,
2255604be855SAndy Yan 					    "POST_BUF_EMPTY irq err at vp%d\n",
2256604be855SAndy Yan 					    vp->id);
2257604be855SAndy Yan 			ret = IRQ_HANDLED;
2258604be855SAndy Yan 		}
2259604be855SAndy Yan 	}
2260604be855SAndy Yan 
2261604be855SAndy Yan 	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
2262604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
2263604be855SAndy Yan 	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
2264604be855SAndy Yan 	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
2265604be855SAndy Yan 
2266604be855SAndy Yan 	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
2267604be855SAndy Yan 		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
2268604be855SAndy Yan 			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
2269604be855SAndy Yan 			ret = IRQ_HANDLED;
2270604be855SAndy Yan 		}
2271604be855SAndy Yan 	}
2272604be855SAndy Yan 
2273604be855SAndy Yan 	pm_runtime_put(vop2->dev);
2274604be855SAndy Yan 
2275604be855SAndy Yan 	return ret;
2276604be855SAndy Yan }
2277604be855SAndy Yan 
2278604be855SAndy Yan static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
2279604be855SAndy Yan 			   unsigned long possible_crtcs)
2280604be855SAndy Yan {
2281604be855SAndy Yan 	const struct vop2_win_data *win_data = win->data;
2282604be855SAndy Yan 	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
2283604be855SAndy Yan 				  BIT(DRM_MODE_BLEND_PREMULTI) |
2284604be855SAndy Yan 				  BIT(DRM_MODE_BLEND_COVERAGE);
2285604be855SAndy Yan 	int ret;
2286604be855SAndy Yan 
2287604be855SAndy Yan 	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
2288604be855SAndy Yan 				       &vop2_plane_funcs, win_data->formats,
2289604be855SAndy Yan 				       win_data->nformats,
2290604be855SAndy Yan 				       win_data->format_modifiers,
2291604be855SAndy Yan 				       win->type, win_data->name);
2292604be855SAndy Yan 	if (ret) {
2293604be855SAndy Yan 		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
2294604be855SAndy Yan 		return ret;
2295604be855SAndy Yan 	}
2296604be855SAndy Yan 
2297604be855SAndy Yan 	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
2298604be855SAndy Yan 
2299604be855SAndy Yan 	if (win->data->supported_rotations)
2300604be855SAndy Yan 		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
2301604be855SAndy Yan 						   DRM_MODE_ROTATE_0 |
2302604be855SAndy Yan 						   win->data->supported_rotations);
2303604be855SAndy Yan 	drm_plane_create_alpha_property(&win->base);
2304604be855SAndy Yan 	drm_plane_create_blend_mode_property(&win->base, blend_caps);
2305604be855SAndy Yan 	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
2306604be855SAndy Yan 				       vop2->registered_num_wins - 1);
2307604be855SAndy Yan 
2308604be855SAndy Yan 	return 0;
2309604be855SAndy Yan }
2310604be855SAndy Yan 
2311604be855SAndy Yan static struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2)
2312604be855SAndy Yan {
2313604be855SAndy Yan 	int i;
2314604be855SAndy Yan 
2315604be855SAndy Yan 	for (i = 0; i < vop2->data->nr_vps; i++) {
2316604be855SAndy Yan 		struct vop2_video_port *vp = &vop2->vps[i];
2317604be855SAndy Yan 
2318604be855SAndy Yan 		if (!vp->crtc.port)
2319604be855SAndy Yan 			continue;
2320604be855SAndy Yan 		if (vp->primary_plane)
2321604be855SAndy Yan 			continue;
2322604be855SAndy Yan 
2323604be855SAndy Yan 		return vp;
2324604be855SAndy Yan 	}
2325604be855SAndy Yan 
2326604be855SAndy Yan 	return NULL;
2327604be855SAndy Yan }
2328604be855SAndy Yan 
2329cddddc06SMichael Riesch static int vop2_create_crtcs(struct vop2 *vop2)
2330604be855SAndy Yan {
2331604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2332604be855SAndy Yan 	struct drm_device *drm = vop2->drm;
2333604be855SAndy Yan 	struct device *dev = vop2->dev;
2334604be855SAndy Yan 	struct drm_plane *plane;
2335604be855SAndy Yan 	struct device_node *port;
2336604be855SAndy Yan 	struct vop2_video_port *vp;
2337604be855SAndy Yan 	int i, nvp, nvps = 0;
2338604be855SAndy Yan 	int ret;
2339604be855SAndy Yan 
2340604be855SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
2341604be855SAndy Yan 		const struct vop2_video_port_data *vp_data;
2342604be855SAndy Yan 		struct device_node *np;
2343604be855SAndy Yan 		char dclk_name[9];
2344604be855SAndy Yan 
2345604be855SAndy Yan 		vp_data = &vop2_data->vp[i];
2346604be855SAndy Yan 		vp = &vop2->vps[i];
2347604be855SAndy Yan 		vp->vop2 = vop2;
2348604be855SAndy Yan 		vp->id = vp_data->id;
2349604be855SAndy Yan 		vp->data = vp_data;
2350604be855SAndy Yan 
2351604be855SAndy Yan 		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
2352604be855SAndy Yan 		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
2353604be855SAndy Yan 		if (IS_ERR(vp->dclk)) {
2354604be855SAndy Yan 			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
2355604be855SAndy Yan 			return PTR_ERR(vp->dclk);
2356604be855SAndy Yan 		}
2357604be855SAndy Yan 
2358604be855SAndy Yan 		np = of_graph_get_remote_node(dev->of_node, i, -1);
2359604be855SAndy Yan 		if (!np) {
2360604be855SAndy Yan 			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
2361604be855SAndy Yan 			continue;
2362604be855SAndy Yan 		}
2363604be855SAndy Yan 		of_node_put(np);
2364604be855SAndy Yan 
2365604be855SAndy Yan 		port = of_graph_get_port_by_id(dev->of_node, i);
2366604be855SAndy Yan 		if (!port) {
2367604be855SAndy Yan 			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
2368604be855SAndy Yan 			return -ENOENT;
2369604be855SAndy Yan 		}
2370604be855SAndy Yan 
2371604be855SAndy Yan 		vp->crtc.port = port;
2372604be855SAndy Yan 		nvps++;
2373604be855SAndy Yan 	}
2374604be855SAndy Yan 
2375604be855SAndy Yan 	nvp = 0;
2376604be855SAndy Yan 	for (i = 0; i < vop2->registered_num_wins; i++) {
2377604be855SAndy Yan 		struct vop2_win *win = &vop2->win[i];
2378e88adb4aSTom Rix 		u32 possible_crtcs = 0;
2379604be855SAndy Yan 
2380604be855SAndy Yan 		if (vop2->data->soc_id == 3566) {
2381604be855SAndy Yan 			/*
2382604be855SAndy Yan 			 * On RK3566 these windows don't have an independent
2383604be855SAndy Yan 			 * framebuffer. They share the framebuffer with smart0,
2384604be855SAndy Yan 			 * esmart0 and cluster0 respectively.
2385604be855SAndy Yan 			 */
2386604be855SAndy Yan 			switch (win->data->phys_id) {
2387604be855SAndy Yan 			case ROCKCHIP_VOP2_SMART1:
2388604be855SAndy Yan 			case ROCKCHIP_VOP2_ESMART1:
2389604be855SAndy Yan 			case ROCKCHIP_VOP2_CLUSTER1:
2390604be855SAndy Yan 				continue;
2391604be855SAndy Yan 			}
2392604be855SAndy Yan 		}
2393604be855SAndy Yan 
2394604be855SAndy Yan 		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
2395604be855SAndy Yan 			vp = find_vp_without_primary(vop2);
2396604be855SAndy Yan 			if (vp) {
2397604be855SAndy Yan 				possible_crtcs = BIT(nvp);
2398604be855SAndy Yan 				vp->primary_plane = win;
2399604be855SAndy Yan 				nvp++;
2400604be855SAndy Yan 			} else {
2401604be855SAndy Yan 				/* change the unused primary window to overlay window */
2402604be855SAndy Yan 				win->type = DRM_PLANE_TYPE_OVERLAY;
2403604be855SAndy Yan 			}
2404604be855SAndy Yan 		}
2405604be855SAndy Yan 
2406604be855SAndy Yan 		if (win->type == DRM_PLANE_TYPE_OVERLAY)
2407604be855SAndy Yan 			possible_crtcs = (1 << nvps) - 1;
2408604be855SAndy Yan 
2409604be855SAndy Yan 		ret = vop2_plane_init(vop2, win, possible_crtcs);
2410604be855SAndy Yan 		if (ret) {
2411604be855SAndy Yan 			drm_err(vop2->drm, "failed to init plane %s: %d\n",
2412604be855SAndy Yan 				win->data->name, ret);
2413604be855SAndy Yan 			return ret;
2414604be855SAndy Yan 		}
2415604be855SAndy Yan 	}
2416604be855SAndy Yan 
2417604be855SAndy Yan 	for (i = 0; i < vop2_data->nr_vps; i++) {
2418604be855SAndy Yan 		vp = &vop2->vps[i];
2419604be855SAndy Yan 
2420604be855SAndy Yan 		if (!vp->crtc.port)
2421604be855SAndy Yan 			continue;
2422604be855SAndy Yan 
2423604be855SAndy Yan 		plane = &vp->primary_plane->base;
2424604be855SAndy Yan 
2425604be855SAndy Yan 		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
2426604be855SAndy Yan 						&vop2_crtc_funcs,
2427604be855SAndy Yan 						"video_port%d", vp->id);
2428604be855SAndy Yan 		if (ret) {
2429604be855SAndy Yan 			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
2430604be855SAndy Yan 			return ret;
2431604be855SAndy Yan 		}
2432604be855SAndy Yan 
2433604be855SAndy Yan 		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
2434604be855SAndy Yan 
2435604be855SAndy Yan 		init_completion(&vp->dsp_hold_completion);
2436604be855SAndy Yan 	}
2437604be855SAndy Yan 
2438604be855SAndy Yan 	/*
2439604be855SAndy Yan 	 * On the VOP2 it's very hard to change the number of layers on a VP
2440604be855SAndy Yan 	 * during runtime, so we distribute the layers equally over the used
2441604be855SAndy Yan 	 * VPs
2442604be855SAndy Yan 	 */
2443604be855SAndy Yan 	for (i = 0; i < vop2->data->nr_vps; i++) {
2444604be855SAndy Yan 		struct vop2_video_port *vp = &vop2->vps[i];
2445604be855SAndy Yan 
2446604be855SAndy Yan 		if (vp->crtc.port)
2447dc00748aSAndy Yan 			vp->nlayers = vop2_data->win_size / nvps;
2448604be855SAndy Yan 	}
2449604be855SAndy Yan 
2450604be855SAndy Yan 	return 0;
2451604be855SAndy Yan }
2452604be855SAndy Yan 
2453cddddc06SMichael Riesch static void vop2_destroy_crtcs(struct vop2 *vop2)
2454604be855SAndy Yan {
2455cddddc06SMichael Riesch 	struct drm_device *drm = vop2->drm;
2456cddddc06SMichael Riesch 	struct list_head *crtc_list = &drm->mode_config.crtc_list;
2457cddddc06SMichael Riesch 	struct list_head *plane_list = &drm->mode_config.plane_list;
2458cddddc06SMichael Riesch 	struct drm_crtc *crtc, *tmpc;
2459cddddc06SMichael Riesch 	struct drm_plane *plane, *tmpp;
2460cddddc06SMichael Riesch 
2461cddddc06SMichael Riesch 	list_for_each_entry_safe(plane, tmpp, plane_list, head)
2462cddddc06SMichael Riesch 		drm_plane_cleanup(plane);
2463604be855SAndy Yan 
2464604be855SAndy Yan 	/*
2465604be855SAndy Yan 	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
2466604be855SAndy Yan 	 * references the CRTC.
2467604be855SAndy Yan 	 */
2468cddddc06SMichael Riesch 	list_for_each_entry_safe(crtc, tmpc, crtc_list, head) {
2469cddddc06SMichael Riesch 		of_node_put(crtc->port);
2470604be855SAndy Yan 		drm_crtc_cleanup(crtc);
2471604be855SAndy Yan 	}
2472cddddc06SMichael Riesch }
2473604be855SAndy Yan 
2474c66c6d7cSMichael Riesch static int vop2_find_rgb_encoder(struct vop2 *vop2)
2475c66c6d7cSMichael Riesch {
2476c66c6d7cSMichael Riesch 	struct device_node *node = vop2->dev->of_node;
2477c66c6d7cSMichael Riesch 	struct device_node *endpoint;
2478c66c6d7cSMichael Riesch 	int i;
2479c66c6d7cSMichael Riesch 
2480c66c6d7cSMichael Riesch 	for (i = 0; i < vop2->data->nr_vps; i++) {
2481c66c6d7cSMichael Riesch 		endpoint = of_graph_get_endpoint_by_regs(node, i,
2482c66c6d7cSMichael Riesch 							 ROCKCHIP_VOP2_EP_RGB0);
2483c66c6d7cSMichael Riesch 		if (!endpoint)
2484c66c6d7cSMichael Riesch 			continue;
2485c66c6d7cSMichael Riesch 
2486c66c6d7cSMichael Riesch 		of_node_put(endpoint);
2487c66c6d7cSMichael Riesch 		return i;
2488c66c6d7cSMichael Riesch 	}
2489c66c6d7cSMichael Riesch 
2490c66c6d7cSMichael Riesch 	return -ENOENT;
2491c66c6d7cSMichael Riesch }
2492604be855SAndy Yan 
2493604be855SAndy Yan static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
2494604be855SAndy Yan 	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
2495604be855SAndy Yan 	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
2496604be855SAndy Yan 	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
2497604be855SAndy Yan 	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
2498604be855SAndy Yan 	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
2499604be855SAndy Yan 	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
2500604be855SAndy Yan 	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
2501604be855SAndy Yan 	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
2502604be855SAndy Yan 	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
2503604be855SAndy Yan 	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
2504604be855SAndy Yan 	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
2505604be855SAndy Yan 	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
2506604be855SAndy Yan 	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
2507604be855SAndy Yan 	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
2508604be855SAndy Yan 	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
2509604be855SAndy Yan 
2510604be855SAndy Yan 	/* Scale */
2511604be855SAndy Yan 	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
2512604be855SAndy Yan 	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
2513604be855SAndy Yan 	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
2514604be855SAndy Yan 	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
2515604be855SAndy Yan 	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
2516604be855SAndy Yan 	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
2517604be855SAndy Yan 	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
2518604be855SAndy Yan 
2519604be855SAndy Yan 	/* cluster regs */
2520604be855SAndy Yan 	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
2521604be855SAndy Yan 	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
2522604be855SAndy Yan 	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
2523604be855SAndy Yan 
2524604be855SAndy Yan 	/* afbc regs */
2525604be855SAndy Yan 	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
2526604be855SAndy Yan 	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
2527604be855SAndy Yan 	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
2528604be855SAndy Yan 	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
2529604be855SAndy Yan 	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
2530604be855SAndy Yan 	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
2531604be855SAndy Yan 	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
2532604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
2533604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
2534604be855SAndy Yan 	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
2535604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
2536604be855SAndy Yan 	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
2537604be855SAndy Yan 	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
2538604be855SAndy Yan 	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
2539604be855SAndy Yan 	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
2540604be855SAndy Yan 	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
2541604be855SAndy Yan 	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
2542604be855SAndy Yan 	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
2543604be855SAndy Yan 	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
2544604be855SAndy Yan 	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
2545604be855SAndy Yan 	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
2546604be855SAndy Yan 	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
2547604be855SAndy Yan 	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
2548604be855SAndy Yan 	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
2549604be855SAndy Yan 	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
2550604be855SAndy Yan 	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
2551604be855SAndy Yan 	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
2552604be855SAndy Yan 	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
2553604be855SAndy Yan 	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
2554604be855SAndy Yan 	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
2555604be855SAndy Yan };
2556604be855SAndy Yan 
2557604be855SAndy Yan static int vop2_cluster_init(struct vop2_win *win)
2558604be855SAndy Yan {
2559604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
2560604be855SAndy Yan 	struct reg_field *cluster_regs;
2561604be855SAndy Yan 	int ret, i;
2562604be855SAndy Yan 
2563604be855SAndy Yan 	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
2564604be855SAndy Yan 			       GFP_KERNEL);
2565604be855SAndy Yan 	if (!cluster_regs)
2566604be855SAndy Yan 		return -ENOMEM;
2567604be855SAndy Yan 
2568604be855SAndy Yan 	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
2569604be855SAndy Yan 		if (cluster_regs[i].reg != 0xffffffff)
2570604be855SAndy Yan 			cluster_regs[i].reg += win->offset;
2571604be855SAndy Yan 
2572604be855SAndy Yan 	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
2573604be855SAndy Yan 					   cluster_regs,
2574604be855SAndy Yan 					   ARRAY_SIZE(vop2_cluster_regs));
2575604be855SAndy Yan 
2576604be855SAndy Yan 	kfree(cluster_regs);
2577604be855SAndy Yan 
2578604be855SAndy Yan 	return ret;
2579604be855SAndy Yan };
2580604be855SAndy Yan 
2581604be855SAndy Yan static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
2582604be855SAndy Yan 	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
2583604be855SAndy Yan 	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
2584604be855SAndy Yan 	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
2585604be855SAndy Yan 	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
2586604be855SAndy Yan 	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
2587604be855SAndy Yan 	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
2588604be855SAndy Yan 	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
2589604be855SAndy Yan 	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
2590604be855SAndy Yan 	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
2591604be855SAndy Yan 	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
2592604be855SAndy Yan 	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
2593604be855SAndy Yan 	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
2594604be855SAndy Yan 	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
2595604be855SAndy Yan 	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
2596604be855SAndy Yan 	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
2597604be855SAndy Yan 	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
2598604be855SAndy Yan 	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
2599604be855SAndy Yan 	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
2600604be855SAndy Yan 	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
2601604be855SAndy Yan 
2602604be855SAndy Yan 	/* Scale */
2603604be855SAndy Yan 	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
2604604be855SAndy Yan 	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
2605604be855SAndy Yan 	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
2606604be855SAndy Yan 	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
2607604be855SAndy Yan 	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
2608604be855SAndy Yan 	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
2609604be855SAndy Yan 	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
2610604be855SAndy Yan 	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
2611604be855SAndy Yan 	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
2612604be855SAndy Yan 	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
2613604be855SAndy Yan 	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
2614604be855SAndy Yan 	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
2615604be855SAndy Yan 	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
2616604be855SAndy Yan 	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
2617604be855SAndy Yan 	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
2618604be855SAndy Yan 	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
2619604be855SAndy Yan 	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
2620604be855SAndy Yan 	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
2621604be855SAndy Yan 	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
2622604be855SAndy Yan 	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
2623604be855SAndy Yan 	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
2624604be855SAndy Yan 	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
2625604be855SAndy Yan 	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
2626604be855SAndy Yan 	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
2627604be855SAndy Yan 	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
2628604be855SAndy Yan 	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
2629604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
2630604be855SAndy Yan 	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
2631604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
2632604be855SAndy Yan 	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
2633604be855SAndy Yan 	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
2634604be855SAndy Yan 	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
2635604be855SAndy Yan 	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
2636604be855SAndy Yan 	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
2637604be855SAndy Yan 	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
2638604be855SAndy Yan 	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
2639604be855SAndy Yan };
2640604be855SAndy Yan 
2641604be855SAndy Yan static int vop2_esmart_init(struct vop2_win *win)
2642604be855SAndy Yan {
2643604be855SAndy Yan 	struct vop2 *vop2 = win->vop2;
2644604be855SAndy Yan 	struct reg_field *esmart_regs;
2645604be855SAndy Yan 	int ret, i;
2646604be855SAndy Yan 
2647604be855SAndy Yan 	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
2648604be855SAndy Yan 			      GFP_KERNEL);
2649604be855SAndy Yan 	if (!esmart_regs)
2650604be855SAndy Yan 		return -ENOMEM;
2651604be855SAndy Yan 
2652604be855SAndy Yan 	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
2653604be855SAndy Yan 		if (esmart_regs[i].reg != 0xffffffff)
2654604be855SAndy Yan 			esmart_regs[i].reg += win->offset;
2655604be855SAndy Yan 
2656604be855SAndy Yan 	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
2657604be855SAndy Yan 					   esmart_regs,
2658604be855SAndy Yan 					   ARRAY_SIZE(vop2_esmart_regs));
2659604be855SAndy Yan 
2660604be855SAndy Yan 	kfree(esmart_regs);
2661604be855SAndy Yan 
2662604be855SAndy Yan 	return ret;
2663604be855SAndy Yan };
2664604be855SAndy Yan 
2665604be855SAndy Yan static int vop2_win_init(struct vop2 *vop2)
2666604be855SAndy Yan {
2667604be855SAndy Yan 	const struct vop2_data *vop2_data = vop2->data;
2668604be855SAndy Yan 	struct vop2_win *win;
2669604be855SAndy Yan 	int i, ret;
2670604be855SAndy Yan 
2671604be855SAndy Yan 	for (i = 0; i < vop2_data->win_size; i++) {
2672604be855SAndy Yan 		const struct vop2_win_data *win_data = &vop2_data->win[i];
2673604be855SAndy Yan 
2674604be855SAndy Yan 		win = &vop2->win[i];
2675604be855SAndy Yan 		win->data = win_data;
2676604be855SAndy Yan 		win->type = win_data->type;
2677604be855SAndy Yan 		win->offset = win_data->base;
2678604be855SAndy Yan 		win->win_id = i;
2679604be855SAndy Yan 		win->vop2 = vop2;
2680604be855SAndy Yan 		if (vop2_cluster_window(win))
2681604be855SAndy Yan 			ret = vop2_cluster_init(win);
2682604be855SAndy Yan 		else
2683604be855SAndy Yan 			ret = vop2_esmart_init(win);
2684604be855SAndy Yan 		if (ret)
2685604be855SAndy Yan 			return ret;
2686604be855SAndy Yan 	}
2687604be855SAndy Yan 
2688604be855SAndy Yan 	vop2->registered_num_wins = vop2_data->win_size;
2689604be855SAndy Yan 
2690604be855SAndy Yan 	return 0;
2691604be855SAndy Yan }
2692604be855SAndy Yan 
2693604be855SAndy Yan /*
2694604be855SAndy Yan  * The window registers are only updated when config done is written.
2695604be855SAndy Yan  * Until that they read back the old value. As we read-modify-write
2696604be855SAndy Yan  * these registers mark them as non-volatile. This makes sure we read
2697604be855SAndy Yan  * the new values from the regmap register cache.
2698604be855SAndy Yan  */
2699604be855SAndy Yan static const struct regmap_range vop2_nonvolatile_range[] = {
2700604be855SAndy Yan 	regmap_reg_range(0x1000, 0x23ff),
2701604be855SAndy Yan };
2702604be855SAndy Yan 
2703604be855SAndy Yan static const struct regmap_access_table vop2_volatile_table = {
2704604be855SAndy Yan 	.no_ranges = vop2_nonvolatile_range,
2705604be855SAndy Yan 	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
2706604be855SAndy Yan };
2707604be855SAndy Yan 
2708604be855SAndy Yan static const struct regmap_config vop2_regmap_config = {
2709604be855SAndy Yan 	.reg_bits	= 32,
2710604be855SAndy Yan 	.val_bits	= 32,
2711604be855SAndy Yan 	.reg_stride	= 4,
2712604be855SAndy Yan 	.max_register	= 0x3000,
2713604be855SAndy Yan 	.name		= "vop2",
2714604be855SAndy Yan 	.volatile_table	= &vop2_volatile_table,
27153d59c22bSMark Brown 	.cache_type	= REGCACHE_MAPLE,
2716604be855SAndy Yan };
2717604be855SAndy Yan 
2718604be855SAndy Yan static int vop2_bind(struct device *dev, struct device *master, void *data)
2719604be855SAndy Yan {
2720604be855SAndy Yan 	struct platform_device *pdev = to_platform_device(dev);
2721604be855SAndy Yan 	const struct vop2_data *vop2_data;
2722604be855SAndy Yan 	struct drm_device *drm = data;
2723604be855SAndy Yan 	struct vop2 *vop2;
2724604be855SAndy Yan 	struct resource *res;
2725604be855SAndy Yan 	size_t alloc_size;
2726604be855SAndy Yan 	int ret;
2727604be855SAndy Yan 
2728604be855SAndy Yan 	vop2_data = of_device_get_match_data(dev);
2729604be855SAndy Yan 	if (!vop2_data)
2730604be855SAndy Yan 		return -ENODEV;
2731604be855SAndy Yan 
2732604be855SAndy Yan 	/* Allocate vop2 struct and its vop2_win array */
27333b4db36cSJacob Keller 	alloc_size = struct_size(vop2, win, vop2_data->win_size);
2734604be855SAndy Yan 	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
2735604be855SAndy Yan 	if (!vop2)
2736604be855SAndy Yan 		return -ENOMEM;
2737604be855SAndy Yan 
2738604be855SAndy Yan 	vop2->dev = dev;
2739604be855SAndy Yan 	vop2->data = vop2_data;
2740604be855SAndy Yan 	vop2->drm = drm;
2741604be855SAndy Yan 
2742604be855SAndy Yan 	dev_set_drvdata(dev, vop2);
2743604be855SAndy Yan 
27445ee8c8f9SSascha Hauer 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop");
2745604be855SAndy Yan 	if (!res) {
2746604be855SAndy Yan 		drm_err(vop2->drm, "failed to get vop2 register byname\n");
2747604be855SAndy Yan 		return -EINVAL;
2748604be855SAndy Yan 	}
2749604be855SAndy Yan 
2750604be855SAndy Yan 	vop2->regs = devm_ioremap_resource(dev, res);
2751604be855SAndy Yan 	if (IS_ERR(vop2->regs))
2752604be855SAndy Yan 		return PTR_ERR(vop2->regs);
2753604be855SAndy Yan 	vop2->len = resource_size(res);
2754604be855SAndy Yan 
2755604be855SAndy Yan 	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
27564ab9157cSAlfredo Cruz 	if (IS_ERR(vop2->map))
27574ab9157cSAlfredo Cruz 		return PTR_ERR(vop2->map);
2758604be855SAndy Yan 
2759604be855SAndy Yan 	ret = vop2_win_init(vop2);
2760604be855SAndy Yan 	if (ret)
2761604be855SAndy Yan 		return ret;
2762604be855SAndy Yan 
27635ee8c8f9SSascha Hauer 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma-lut");
2764604be855SAndy Yan 	if (res) {
2765604be855SAndy Yan 		vop2->lut_regs = devm_ioremap_resource(dev, res);
2766604be855SAndy Yan 		if (IS_ERR(vop2->lut_regs))
2767604be855SAndy Yan 			return PTR_ERR(vop2->lut_regs);
2768604be855SAndy Yan 	}
2769604be855SAndy Yan 
2770604be855SAndy Yan 	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
2771604be855SAndy Yan 
2772604be855SAndy Yan 	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
2773604be855SAndy Yan 	if (IS_ERR(vop2->hclk)) {
2774604be855SAndy Yan 		drm_err(vop2->drm, "failed to get hclk source\n");
2775604be855SAndy Yan 		return PTR_ERR(vop2->hclk);
2776604be855SAndy Yan 	}
2777604be855SAndy Yan 
2778604be855SAndy Yan 	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
2779604be855SAndy Yan 	if (IS_ERR(vop2->aclk)) {
2780604be855SAndy Yan 		drm_err(vop2->drm, "failed to get aclk source\n");
2781604be855SAndy Yan 		return PTR_ERR(vop2->aclk);
2782604be855SAndy Yan 	}
2783604be855SAndy Yan 
2784604be855SAndy Yan 	vop2->irq = platform_get_irq(pdev, 0);
2785604be855SAndy Yan 	if (vop2->irq < 0) {
2786604be855SAndy Yan 		drm_err(vop2->drm, "cannot find irq for vop2\n");
2787604be855SAndy Yan 		return vop2->irq;
2788604be855SAndy Yan 	}
2789604be855SAndy Yan 
2790604be855SAndy Yan 	mutex_init(&vop2->vop2_lock);
2791604be855SAndy Yan 
2792604be855SAndy Yan 	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
2793604be855SAndy Yan 	if (ret)
2794604be855SAndy Yan 		return ret;
2795604be855SAndy Yan 
2796cddddc06SMichael Riesch 	ret = vop2_create_crtcs(vop2);
2797604be855SAndy Yan 	if (ret)
2798604be855SAndy Yan 		return ret;
2799604be855SAndy Yan 
2800c66c6d7cSMichael Riesch 	ret = vop2_find_rgb_encoder(vop2);
2801c66c6d7cSMichael Riesch 	if (ret >= 0) {
2802c66c6d7cSMichael Riesch 		vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
2803c66c6d7cSMichael Riesch 					      vop2->drm, ret);
2804c66c6d7cSMichael Riesch 		if (IS_ERR(vop2->rgb)) {
2805c66c6d7cSMichael Riesch 			if (PTR_ERR(vop2->rgb) == -EPROBE_DEFER) {
2806c66c6d7cSMichael Riesch 				ret = PTR_ERR(vop2->rgb);
2807c66c6d7cSMichael Riesch 				goto err_crtcs;
2808c66c6d7cSMichael Riesch 			}
2809c66c6d7cSMichael Riesch 			vop2->rgb = NULL;
2810c66c6d7cSMichael Riesch 		}
2811c66c6d7cSMichael Riesch 	}
2812c66c6d7cSMichael Riesch 
2813604be855SAndy Yan 	rockchip_drm_dma_init_device(vop2->drm, vop2->dev);
2814604be855SAndy Yan 
2815604be855SAndy Yan 	pm_runtime_enable(&pdev->dev);
2816604be855SAndy Yan 
2817604be855SAndy Yan 	return 0;
2818c66c6d7cSMichael Riesch 
2819c66c6d7cSMichael Riesch err_crtcs:
2820c66c6d7cSMichael Riesch 	vop2_destroy_crtcs(vop2);
2821c66c6d7cSMichael Riesch 
2822c66c6d7cSMichael Riesch 	return ret;
2823604be855SAndy Yan }
2824604be855SAndy Yan 
2825604be855SAndy Yan static void vop2_unbind(struct device *dev, struct device *master, void *data)
2826604be855SAndy Yan {
2827604be855SAndy Yan 	struct vop2 *vop2 = dev_get_drvdata(dev);
2828604be855SAndy Yan 
2829604be855SAndy Yan 	pm_runtime_disable(dev);
2830604be855SAndy Yan 
2831c66c6d7cSMichael Riesch 	if (vop2->rgb)
2832c66c6d7cSMichael Riesch 		rockchip_rgb_fini(vop2->rgb);
2833604be855SAndy Yan 
2834cddddc06SMichael Riesch 	vop2_destroy_crtcs(vop2);
2835604be855SAndy Yan }
2836604be855SAndy Yan 
2837604be855SAndy Yan const struct component_ops vop2_component_ops = {
2838604be855SAndy Yan 	.bind = vop2_bind,
2839604be855SAndy Yan 	.unbind = vop2_unbind,
2840604be855SAndy Yan };
2841604be855SAndy Yan EXPORT_SYMBOL_GPL(vop2_component_ops);
2842