162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
462306a36Sopenharmony_ci * Author: Andy Yan <andy.yan@rock-chips.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/component.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/media-bus-format.h>
1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_graph.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/swab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <drm/drm.h>
2362306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2462306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h>
2562306a36Sopenharmony_ci#include <drm/drm_blend.h>
2662306a36Sopenharmony_ci#include <drm/drm_crtc.h>
2762306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
2862306a36Sopenharmony_ci#include <drm/drm_flip_work.h>
2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
3162306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <uapi/linux/videodev2.h>
3462306a36Sopenharmony_ci#include <dt-bindings/soc/rockchip,vop2.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "rockchip_drm_drv.h"
3762306a36Sopenharmony_ci#include "rockchip_drm_gem.h"
3862306a36Sopenharmony_ci#include "rockchip_drm_fb.h"
3962306a36Sopenharmony_ci#include "rockchip_drm_vop2.h"
4062306a36Sopenharmony_ci#include "rockchip_rgb.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * VOP2 architecture
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci +----------+   +-------------+                                                        +-----------+
4662306a36Sopenharmony_ci |  Cluster |   | Sel 1 from 6|                                                        | 1 from 3  |
4762306a36Sopenharmony_ci |  window0 |   |    Layer0   |                                                        |    RGB    |
4862306a36Sopenharmony_ci +----------+   +-------------+              +---------------+    +-------------+      +-----------+
4962306a36Sopenharmony_ci +----------+   +-------------+              |N from 6 layers|    |             |
5062306a36Sopenharmony_ci |  Cluster |   | Sel 1 from 6|              |   Overlay0    +--->| Video Port0 |      +-----------+
5162306a36Sopenharmony_ci |  window1 |   |    Layer1   |              |               |    |             |      | 1 from 3  |
5262306a36Sopenharmony_ci +----------+   +-------------+              +---------------+    +-------------+      |   LVDS    |
5362306a36Sopenharmony_ci +----------+   +-------------+                                                        +-----------+
5462306a36Sopenharmony_ci |  Esmart  |   | Sel 1 from 6|
5562306a36Sopenharmony_ci |  window0 |   |   Layer2    |              +---------------+    +-------------+      +-----------+
5662306a36Sopenharmony_ci +----------+   +-------------+              |N from 6 Layers|    |             | +--> | 1 from 3  |
5762306a36Sopenharmony_ci +----------+   +-------------+   -------->  |   Overlay1    +--->| Video Port1 |      |   MIPI    |
5862306a36Sopenharmony_ci |  Esmart  |   | Sel 1 from 6|   -------->  |               |    |             |      +-----------+
5962306a36Sopenharmony_ci |  Window1 |   |   Layer3    |              +---------------+    +-------------+
6062306a36Sopenharmony_ci +----------+   +-------------+                                                        +-----------+
6162306a36Sopenharmony_ci +----------+   +-------------+                                                        | 1 from 3  |
6262306a36Sopenharmony_ci |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |   HDMI    |
6362306a36Sopenharmony_ci |  Window0 |   |    Layer4   |              |N from 6 Layers|    |             |      +-----------+
6462306a36Sopenharmony_ci +----------+   +-------------+              |   Overlay2    +--->| Video Port2 |
6562306a36Sopenharmony_ci +----------+   +-------------+              |               |    |             |      +-----------+
6662306a36Sopenharmony_ci |  Smart   |   | Sel 1 from 6|              +---------------+    +-------------+      |  1 from 3 |
6762306a36Sopenharmony_ci |  Window1 |   |    Layer5   |                                                        |    eDP    |
6862306a36Sopenharmony_ci +----------+   +-------------+                                                        +-----------+
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cienum vop2_data_format {
7362306a36Sopenharmony_ci	VOP2_FMT_ARGB8888 = 0,
7462306a36Sopenharmony_ci	VOP2_FMT_RGB888,
7562306a36Sopenharmony_ci	VOP2_FMT_RGB565,
7662306a36Sopenharmony_ci	VOP2_FMT_XRGB101010,
7762306a36Sopenharmony_ci	VOP2_FMT_YUV420SP,
7862306a36Sopenharmony_ci	VOP2_FMT_YUV422SP,
7962306a36Sopenharmony_ci	VOP2_FMT_YUV444SP,
8062306a36Sopenharmony_ci	VOP2_FMT_YUYV422 = 8,
8162306a36Sopenharmony_ci	VOP2_FMT_YUYV420,
8262306a36Sopenharmony_ci	VOP2_FMT_VYUY422,
8362306a36Sopenharmony_ci	VOP2_FMT_VYUY420,
8462306a36Sopenharmony_ci	VOP2_FMT_YUV420SP_TILE_8x4 = 0x10,
8562306a36Sopenharmony_ci	VOP2_FMT_YUV420SP_TILE_16x2,
8662306a36Sopenharmony_ci	VOP2_FMT_YUV422SP_TILE_8x4,
8762306a36Sopenharmony_ci	VOP2_FMT_YUV422SP_TILE_16x2,
8862306a36Sopenharmony_ci	VOP2_FMT_YUV420SP_10,
8962306a36Sopenharmony_ci	VOP2_FMT_YUV422SP_10,
9062306a36Sopenharmony_ci	VOP2_FMT_YUV444SP_10,
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cienum vop2_afbc_format {
9462306a36Sopenharmony_ci	VOP2_AFBC_FMT_RGB565,
9562306a36Sopenharmony_ci	VOP2_AFBC_FMT_ARGB2101010 = 2,
9662306a36Sopenharmony_ci	VOP2_AFBC_FMT_YUV420_10BIT,
9762306a36Sopenharmony_ci	VOP2_AFBC_FMT_RGB888,
9862306a36Sopenharmony_ci	VOP2_AFBC_FMT_ARGB8888,
9962306a36Sopenharmony_ci	VOP2_AFBC_FMT_YUV420 = 9,
10062306a36Sopenharmony_ci	VOP2_AFBC_FMT_YUV422 = 0xb,
10162306a36Sopenharmony_ci	VOP2_AFBC_FMT_YUV422_10BIT = 0xe,
10262306a36Sopenharmony_ci	VOP2_AFBC_FMT_INVALID = -1,
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciunion vop2_alpha_ctrl {
10662306a36Sopenharmony_ci	u32 val;
10762306a36Sopenharmony_ci	struct {
10862306a36Sopenharmony_ci		/* [0:1] */
10962306a36Sopenharmony_ci		u32 color_mode:1;
11062306a36Sopenharmony_ci		u32 alpha_mode:1;
11162306a36Sopenharmony_ci		/* [2:3] */
11262306a36Sopenharmony_ci		u32 blend_mode:2;
11362306a36Sopenharmony_ci		u32 alpha_cal_mode:1;
11462306a36Sopenharmony_ci		/* [5:7] */
11562306a36Sopenharmony_ci		u32 factor_mode:3;
11662306a36Sopenharmony_ci		/* [8:9] */
11762306a36Sopenharmony_ci		u32 alpha_en:1;
11862306a36Sopenharmony_ci		u32 src_dst_swap:1;
11962306a36Sopenharmony_ci		u32 reserved:6;
12062306a36Sopenharmony_ci		/* [16:23] */
12162306a36Sopenharmony_ci		u32 glb_alpha:8;
12262306a36Sopenharmony_ci	} bits;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct vop2_alpha {
12662306a36Sopenharmony_ci	union vop2_alpha_ctrl src_color_ctrl;
12762306a36Sopenharmony_ci	union vop2_alpha_ctrl dst_color_ctrl;
12862306a36Sopenharmony_ci	union vop2_alpha_ctrl src_alpha_ctrl;
12962306a36Sopenharmony_ci	union vop2_alpha_ctrl dst_alpha_ctrl;
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct vop2_alpha_config {
13362306a36Sopenharmony_ci	bool src_premulti_en;
13462306a36Sopenharmony_ci	bool dst_premulti_en;
13562306a36Sopenharmony_ci	bool src_pixel_alpha_en;
13662306a36Sopenharmony_ci	bool dst_pixel_alpha_en;
13762306a36Sopenharmony_ci	u16 src_glb_alpha_value;
13862306a36Sopenharmony_ci	u16 dst_glb_alpha_value;
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistruct vop2_win {
14262306a36Sopenharmony_ci	struct vop2 *vop2;
14362306a36Sopenharmony_ci	struct drm_plane base;
14462306a36Sopenharmony_ci	const struct vop2_win_data *data;
14562306a36Sopenharmony_ci	struct regmap_field *reg[VOP2_WIN_MAX_REG];
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/**
14862306a36Sopenharmony_ci	 * @win_id: graphic window id, a cluster may be split into two
14962306a36Sopenharmony_ci	 * graphics windows.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	u8 win_id;
15262306a36Sopenharmony_ci	u8 delay;
15362306a36Sopenharmony_ci	u32 offset;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	enum drm_plane_type type;
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistruct vop2_video_port {
15962306a36Sopenharmony_ci	struct drm_crtc crtc;
16062306a36Sopenharmony_ci	struct vop2 *vop2;
16162306a36Sopenharmony_ci	struct clk *dclk;
16262306a36Sopenharmony_ci	unsigned int id;
16362306a36Sopenharmony_ci	const struct vop2_video_port_regs *regs;
16462306a36Sopenharmony_ci	const struct vop2_video_port_data *data;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	struct completion dsp_hold_completion;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/**
16962306a36Sopenharmony_ci	 * @win_mask: Bitmask of windows attached to the video port;
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	u32 win_mask;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	struct vop2_win *primary_plane;
17462306a36Sopenharmony_ci	struct drm_pending_vblank_event *event;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	unsigned int nlayers;
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistruct vop2 {
18062306a36Sopenharmony_ci	struct device *dev;
18162306a36Sopenharmony_ci	struct drm_device *drm;
18262306a36Sopenharmony_ci	struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	const struct vop2_data *data;
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Number of windows that are registered as plane, may be less than the
18762306a36Sopenharmony_ci	 * total number of hardware windows.
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	u32 registered_num_wins;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	void __iomem *regs;
19262306a36Sopenharmony_ci	struct regmap *map;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	struct regmap *grf;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* physical map length of vop2 register */
19762306a36Sopenharmony_ci	u32 len;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	void __iomem *lut_regs;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* protects crtc enable/disable */
20262306a36Sopenharmony_ci	struct mutex vop2_lock;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	int irq;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * Some global resources are shared between all video ports(crtcs), so
20862306a36Sopenharmony_ci	 * we need a ref counter here.
20962306a36Sopenharmony_ci	 */
21062306a36Sopenharmony_ci	unsigned int enable_count;
21162306a36Sopenharmony_ci	struct clk *hclk;
21262306a36Sopenharmony_ci	struct clk *aclk;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* optional internal rgb encoder */
21562306a36Sopenharmony_ci	struct rockchip_rgb *rgb;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* must be put at the end of the struct */
21862306a36Sopenharmony_ci	struct vop2_win win[];
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return container_of(crtc, struct vop2_video_port, crtc);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic struct vop2_win *to_vop2_win(struct drm_plane *p)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return container_of(p, struct vop2_win, base);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void vop2_lock(struct vop2 *vop2)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	mutex_lock(&vop2->vop2_lock);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void vop2_unlock(struct vop2 *vop2)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	mutex_unlock(&vop2->vop2_lock);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	regmap_write(vop2->map, offset, v);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	regmap_write(vp->vop2->map, vp->data->offset + offset, v);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic u32 vop2_readl(struct vop2 *vop2, u32 offset)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	u32 val;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	regmap_read(vop2->map, offset, &val);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return val;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	regmap_field_write(win->reg[reg], v);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic bool vop2_cluster_window(const struct vop2_win *win)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	return win->data->feature & WIN_FEATURE_CLUSTER;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void vop2_cfg_done(struct vop2_video_port *vp)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
27562306a36Sopenharmony_ci			BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void vop2_win_disable(struct vop2_win *win)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_ENABLE, 0);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (vop2_cluster_window(win))
28362306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic enum vop2_data_format vop2_convert_format(u32 format)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	switch (format) {
28962306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
29062306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
29162306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
29262306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
29362306a36Sopenharmony_ci		return VOP2_FMT_ARGB8888;
29462306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
29562306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
29662306a36Sopenharmony_ci		return VOP2_FMT_RGB888;
29762306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
29862306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
29962306a36Sopenharmony_ci		return VOP2_FMT_RGB565;
30062306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
30162306a36Sopenharmony_ci		return VOP2_FMT_YUV420SP;
30262306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
30362306a36Sopenharmony_ci		return VOP2_FMT_YUV422SP;
30462306a36Sopenharmony_ci	case DRM_FORMAT_NV24:
30562306a36Sopenharmony_ci		return VOP2_FMT_YUV444SP;
30662306a36Sopenharmony_ci	case DRM_FORMAT_YUYV:
30762306a36Sopenharmony_ci	case DRM_FORMAT_YVYU:
30862306a36Sopenharmony_ci		return VOP2_FMT_VYUY422;
30962306a36Sopenharmony_ci	case DRM_FORMAT_VYUY:
31062306a36Sopenharmony_ci	case DRM_FORMAT_UYVY:
31162306a36Sopenharmony_ci		return VOP2_FMT_YUYV422;
31262306a36Sopenharmony_ci	default:
31362306a36Sopenharmony_ci		DRM_ERROR("unsupported format[%08x]\n", format);
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic enum vop2_afbc_format vop2_convert_afbc_format(u32 format)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	switch (format) {
32162306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
32262306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
32362306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
32462306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
32562306a36Sopenharmony_ci		return VOP2_AFBC_FMT_ARGB8888;
32662306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
32762306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
32862306a36Sopenharmony_ci		return VOP2_AFBC_FMT_RGB888;
32962306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
33062306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
33162306a36Sopenharmony_ci		return VOP2_AFBC_FMT_RGB565;
33262306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
33362306a36Sopenharmony_ci		return VOP2_AFBC_FMT_YUV420;
33462306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
33562306a36Sopenharmony_ci		return VOP2_AFBC_FMT_YUV422;
33662306a36Sopenharmony_ci	default:
33762306a36Sopenharmony_ci		return VOP2_AFBC_FMT_INVALID;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return VOP2_AFBC_FMT_INVALID;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic bool vop2_win_rb_swap(u32 format)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	switch (format) {
34662306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
34762306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
34862306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
34962306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
35062306a36Sopenharmony_ci		return true;
35162306a36Sopenharmony_ci	default:
35262306a36Sopenharmony_ci		return false;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic bool vop2_afbc_rb_swap(u32 format)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	switch (format) {
35962306a36Sopenharmony_ci	case DRM_FORMAT_NV24:
36062306a36Sopenharmony_ci		return true;
36162306a36Sopenharmony_ci	default:
36262306a36Sopenharmony_ci		return false;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic bool vop2_afbc_uv_swap(u32 format)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	switch (format) {
36962306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
37062306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
37162306a36Sopenharmony_ci		return true;
37262306a36Sopenharmony_ci	default:
37362306a36Sopenharmony_ci		return false;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic bool vop2_win_uv_swap(u32 format)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	switch (format) {
38062306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
38162306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
38262306a36Sopenharmony_ci	case DRM_FORMAT_NV24:
38362306a36Sopenharmony_ci		return true;
38462306a36Sopenharmony_ci	default:
38562306a36Sopenharmony_ci		return false;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic bool vop2_win_dither_up(u32 format)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	switch (format) {
39262306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
39362306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
39462306a36Sopenharmony_ci		return true;
39562306a36Sopenharmony_ci	default:
39662306a36Sopenharmony_ci		return false;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	/*
40362306a36Sopenharmony_ci	 * FIXME:
40462306a36Sopenharmony_ci	 *
40562306a36Sopenharmony_ci	 * There is no media type for YUV444 output,
40662306a36Sopenharmony_ci	 * so when out_mode is AAAA or P888, assume output is YUV444 on
40762306a36Sopenharmony_ci	 * yuv format.
40862306a36Sopenharmony_ci	 *
40962306a36Sopenharmony_ci	 * From H/W testing, YUV444 mode need a rb swap.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
41262306a36Sopenharmony_ci	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
41362306a36Sopenharmony_ci	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
41462306a36Sopenharmony_ci	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
41562306a36Sopenharmony_ci	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
41662306a36Sopenharmony_ci	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
41762306a36Sopenharmony_ci	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
41862306a36Sopenharmony_ci	      output_mode == ROCKCHIP_OUT_MODE_P888)))
41962306a36Sopenharmony_ci		return true;
42062306a36Sopenharmony_ci	else
42162306a36Sopenharmony_ci		return false;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic bool is_yuv_output(u32 bus_format)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	switch (bus_format) {
42762306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUV8_1X24:
42862306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUV10_1X30:
42962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
43062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
43162306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUYV8_2X8:
43262306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YVYU8_2X8:
43362306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_2X8:
43462306a36Sopenharmony_ci	case MEDIA_BUS_FMT_VYUY8_2X8:
43562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUYV8_1X16:
43662306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YVYU8_1X16:
43762306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_1X16:
43862306a36Sopenharmony_ci	case MEDIA_BUS_FMT_VYUY8_1X16:
43962306a36Sopenharmony_ci		return true;
44062306a36Sopenharmony_ci	default:
44162306a36Sopenharmony_ci		return false;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (modifier == DRM_FORMAT_MOD_LINEAR)
45062306a36Sopenharmony_ci		return false;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	for (i = 0 ; i < plane->modifier_count; i++)
45362306a36Sopenharmony_ci		if (plane->modifiers[i] == modifier)
45462306a36Sopenharmony_ci			return true;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return false;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format,
46062306a36Sopenharmony_ci					u64 modifier)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct vop2_win *win = to_vop2_win(plane);
46362306a36Sopenharmony_ci	struct vop2 *vop2 = win->vop2;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (modifier == DRM_FORMAT_MOD_INVALID)
46662306a36Sopenharmony_ci		return false;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (modifier == DRM_FORMAT_MOD_LINEAR)
46962306a36Sopenharmony_ci		return true;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (!rockchip_afbc(plane, modifier)) {
47262306a36Sopenharmony_ci		drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n",
47362306a36Sopenharmony_ci			modifier);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		return false;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return vop2_convert_afbc_format(format) >= 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate,
48262306a36Sopenharmony_ci				      bool afbc_half_block_en)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct drm_rect *src = &pstate->src;
48562306a36Sopenharmony_ci	struct drm_framebuffer *fb = pstate->fb;
48662306a36Sopenharmony_ci	u32 bpp = fb->format->cpp[0] * 8;
48762306a36Sopenharmony_ci	u32 vir_width = (fb->pitches[0] << 3) / bpp;
48862306a36Sopenharmony_ci	u32 width = drm_rect_width(src) >> 16;
48962306a36Sopenharmony_ci	u32 height = drm_rect_height(src) >> 16;
49062306a36Sopenharmony_ci	u32 act_xoffset = src->x1 >> 16;
49162306a36Sopenharmony_ci	u32 act_yoffset = src->y1 >> 16;
49262306a36Sopenharmony_ci	u32 align16_crop = 0;
49362306a36Sopenharmony_ci	u32 align64_crop = 0;
49462306a36Sopenharmony_ci	u32 height_tmp;
49562306a36Sopenharmony_ci	u8 tx, ty;
49662306a36Sopenharmony_ci	u8 bottom_crop_line_num = 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* 16 pixel align */
49962306a36Sopenharmony_ci	if (height & 0xf)
50062306a36Sopenharmony_ci		align16_crop = 16 - (height & 0xf);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	height_tmp = height + align16_crop;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* 64 pixel align */
50562306a36Sopenharmony_ci	if (height_tmp & 0x3f)
50662306a36Sopenharmony_ci		align64_crop = 64 - (height_tmp & 0x3f);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	bottom_crop_line_num = align16_crop + align64_crop;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (pstate->rotation &
51162306a36Sopenharmony_ci		(DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y |
51262306a36Sopenharmony_ci		 DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
51362306a36Sopenharmony_ci	case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y:
51462306a36Sopenharmony_ci		tx = 16 - ((act_xoffset + width) & 0xf);
51562306a36Sopenharmony_ci		ty = bottom_crop_line_num - act_yoffset;
51662306a36Sopenharmony_ci		break;
51762306a36Sopenharmony_ci	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90:
51862306a36Sopenharmony_ci		tx = bottom_crop_line_num - act_yoffset;
51962306a36Sopenharmony_ci		ty = vir_width - width - act_xoffset;
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270:
52262306a36Sopenharmony_ci		tx = act_yoffset;
52362306a36Sopenharmony_ci		ty = act_xoffset;
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	case DRM_MODE_REFLECT_X:
52662306a36Sopenharmony_ci		tx = 16 - ((act_xoffset + width) & 0xf);
52762306a36Sopenharmony_ci		ty = act_yoffset;
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case DRM_MODE_REFLECT_Y:
53062306a36Sopenharmony_ci		tx = act_xoffset;
53162306a36Sopenharmony_ci		ty = bottom_crop_line_num - act_yoffset;
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case DRM_MODE_ROTATE_90:
53462306a36Sopenharmony_ci		tx = bottom_crop_line_num - act_yoffset;
53562306a36Sopenharmony_ci		ty = act_xoffset;
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	case DRM_MODE_ROTATE_270:
53862306a36Sopenharmony_ci		tx = act_yoffset;
53962306a36Sopenharmony_ci		ty = vir_width - width - act_xoffset;
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci	case 0:
54262306a36Sopenharmony_ci		tx = act_xoffset;
54362306a36Sopenharmony_ci		ty = act_yoffset;
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (afbc_half_block_en)
54862306a36Sopenharmony_ci		ty &= 0x7f;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci#define TRANSFORM_XOFFSET GENMASK(7, 0)
55162306a36Sopenharmony_ci#define TRANSFORM_YOFFSET GENMASK(23, 16)
55262306a36Sopenharmony_ci	return FIELD_PREP(TRANSFORM_XOFFSET, tx) |
55362306a36Sopenharmony_ci		FIELD_PREP(TRANSFORM_YOFFSET, ty);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * A Cluster window has 2048 x 16 line buffer, which can
55862306a36Sopenharmony_ci * works at 2048 x 16(Full) or 4096 x 8 (Half) mode.
55962306a36Sopenharmony_ci * for Cluster_lb_mode register:
56062306a36Sopenharmony_ci * 0: half mode, for plane input width range 2048 ~ 4096
56162306a36Sopenharmony_ci * 1: half mode, for cluster work at 2 * 2048 plane mode
56262306a36Sopenharmony_ci * 2: half mode, for rotate_90/270 mode
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic int vop2_get_cluster_lb_mode(struct vop2_win *win,
56662306a36Sopenharmony_ci				    struct drm_plane_state *pstate)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
56962306a36Sopenharmony_ci	    (pstate->rotation & DRM_MODE_ROTATE_90))
57062306a36Sopenharmony_ci		return 2;
57162306a36Sopenharmony_ci	else
57262306a36Sopenharmony_ci		return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic u16 vop2_scale_factor(u32 src, u32 dst)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	u32 fac;
57862306a36Sopenharmony_ci	int shift;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (src == dst)
58162306a36Sopenharmony_ci		return 0;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (dst < 2)
58462306a36Sopenharmony_ci		return U16_MAX;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (src < 2)
58762306a36Sopenharmony_ci		return 0;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (src > dst)
59062306a36Sopenharmony_ci		shift = 12;
59162306a36Sopenharmony_ci	else
59262306a36Sopenharmony_ci		shift = 16;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	src--;
59562306a36Sopenharmony_ci	dst--;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	fac = DIV_ROUND_UP(src << shift, dst) - 1;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (fac > U16_MAX)
60062306a36Sopenharmony_ci		return U16_MAX;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return fac;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
60662306a36Sopenharmony_ci			     u32 src_w, u32 src_h, u32 dst_w,
60762306a36Sopenharmony_ci			     u32 dst_h, u32 pixel_format)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	const struct drm_format_info *info;
61062306a36Sopenharmony_ci	u16 hor_scl_mode, ver_scl_mode;
61162306a36Sopenharmony_ci	u16 hscl_filter_mode, vscl_filter_mode;
61262306a36Sopenharmony_ci	u8 gt2 = 0;
61362306a36Sopenharmony_ci	u8 gt4 = 0;
61462306a36Sopenharmony_ci	u32 val;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	info = drm_format_info(pixel_format);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (src_h >= (4 * dst_h)) {
61962306a36Sopenharmony_ci		gt4 = 1;
62062306a36Sopenharmony_ci		src_h >>= 2;
62162306a36Sopenharmony_ci	} else if (src_h >= (2 * dst_h)) {
62262306a36Sopenharmony_ci		gt2 = 1;
62362306a36Sopenharmony_ci		src_h >>= 1;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
62762306a36Sopenharmony_ci	ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (hor_scl_mode == SCALE_UP)
63062306a36Sopenharmony_ci		hscl_filter_mode = VOP2_SCALE_UP_BIC;
63162306a36Sopenharmony_ci	else
63262306a36Sopenharmony_ci		hscl_filter_mode = VOP2_SCALE_DOWN_BIL;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (ver_scl_mode == SCALE_UP)
63562306a36Sopenharmony_ci		vscl_filter_mode = VOP2_SCALE_UP_BIL;
63662306a36Sopenharmony_ci	else
63762306a36Sopenharmony_ci		vscl_filter_mode = VOP2_SCALE_DOWN_BIL;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/*
64062306a36Sopenharmony_ci	 * RK3568 VOP Esmart/Smart dsp_w should be even pixel
64162306a36Sopenharmony_ci	 * at scale down mode
64262306a36Sopenharmony_ci	 */
64362306a36Sopenharmony_ci	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
64462306a36Sopenharmony_ci		if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
64562306a36Sopenharmony_ci			drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
64662306a36Sopenharmony_ci				win->data->name, dst_w);
64762306a36Sopenharmony_ci			dst_w++;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	val = vop2_scale_factor(src_w, dst_w);
65262306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
65362306a36Sopenharmony_ci	val = vop2_scale_factor(src_h, dst_h);
65462306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4);
65762306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode);
66062306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (vop2_cluster_window(win))
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode);
66662306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (info->is_yuv) {
66962306a36Sopenharmony_ci		src_w /= info->hsub;
67062306a36Sopenharmony_ci		src_h /= info->vsub;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		gt4 = 0;
67362306a36Sopenharmony_ci		gt2 = 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		if (src_h >= (4 * dst_h)) {
67662306a36Sopenharmony_ci			gt4 = 1;
67762306a36Sopenharmony_ci			src_h >>= 2;
67862306a36Sopenharmony_ci		} else if (src_h >= (2 * dst_h)) {
67962306a36Sopenharmony_ci			gt2 = 1;
68062306a36Sopenharmony_ci			src_h >>= 1;
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
68462306a36Sopenharmony_ci		ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		val = vop2_scale_factor(src_w, dst_w);
68762306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		val = vop2_scale_factor(src_h, dst_h);
69062306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
69362306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2);
69462306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode);
69562306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode);
69662306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode);
69762306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode);
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic int vop2_convert_csc_mode(int csc_mode)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	switch (csc_mode) {
70462306a36Sopenharmony_ci	case V4L2_COLORSPACE_SMPTE170M:
70562306a36Sopenharmony_ci	case V4L2_COLORSPACE_470_SYSTEM_M:
70662306a36Sopenharmony_ci	case V4L2_COLORSPACE_470_SYSTEM_BG:
70762306a36Sopenharmony_ci		return CSC_BT601L;
70862306a36Sopenharmony_ci	case V4L2_COLORSPACE_REC709:
70962306a36Sopenharmony_ci	case V4L2_COLORSPACE_SMPTE240M:
71062306a36Sopenharmony_ci	case V4L2_COLORSPACE_DEFAULT:
71162306a36Sopenharmony_ci		return CSC_BT709L;
71262306a36Sopenharmony_ci	case V4L2_COLORSPACE_JPEG:
71362306a36Sopenharmony_ci		return CSC_BT601F;
71462306a36Sopenharmony_ci	case V4L2_COLORSPACE_BT2020:
71562306a36Sopenharmony_ci		return CSC_BT2020;
71662306a36Sopenharmony_ci	default:
71762306a36Sopenharmony_ci		return CSC_BT709L;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/*
72262306a36Sopenharmony_ci * colorspace path:
72362306a36Sopenharmony_ci *      Input        Win csc                     Output
72462306a36Sopenharmony_ci * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
72562306a36Sopenharmony_ci *    RGB        --> R2Y                  __/
72662306a36Sopenharmony_ci *
72762306a36Sopenharmony_ci * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
72862306a36Sopenharmony_ci *    RGB        --> 709To2020->R2Y       __/
72962306a36Sopenharmony_ci *
73062306a36Sopenharmony_ci * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
73162306a36Sopenharmony_ci *    RGB        --> R2Y                  __/
73262306a36Sopenharmony_ci *
73362306a36Sopenharmony_ci * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
73462306a36Sopenharmony_ci *    RGB        --> 709To2020->R2Y       __/
73562306a36Sopenharmony_ci *
73662306a36Sopenharmony_ci * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
73762306a36Sopenharmony_ci *    RGB        --> R2Y                  __/
73862306a36Sopenharmony_ci *
73962306a36Sopenharmony_ci * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
74062306a36Sopenharmony_ci *    RGB        --> R2Y(601)             __/
74162306a36Sopenharmony_ci *
74262306a36Sopenharmony_ci * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
74362306a36Sopenharmony_ci *    RGB        --> bypass               __/
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
74862306a36Sopenharmony_ci *
74962306a36Sopenharmony_ci * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
75062306a36Sopenharmony_ci *
75162306a36Sopenharmony_ci * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
75262306a36Sopenharmony_ci */
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void vop2_setup_csc_mode(struct vop2_video_port *vp,
75562306a36Sopenharmony_ci				struct vop2_win *win,
75662306a36Sopenharmony_ci				struct drm_plane_state *pstate)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state);
75962306a36Sopenharmony_ci	int is_input_yuv = pstate->fb->format->is_yuv;
76062306a36Sopenharmony_ci	int is_output_yuv = is_yuv_output(vcstate->bus_format);
76162306a36Sopenharmony_ci	int input_csc = V4L2_COLORSPACE_DEFAULT;
76262306a36Sopenharmony_ci	int output_csc = vcstate->color_space;
76362306a36Sopenharmony_ci	bool r2y_en, y2r_en;
76462306a36Sopenharmony_ci	int csc_mode;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (is_input_yuv && !is_output_yuv) {
76762306a36Sopenharmony_ci		y2r_en = true;
76862306a36Sopenharmony_ci		r2y_en = false;
76962306a36Sopenharmony_ci		csc_mode = vop2_convert_csc_mode(input_csc);
77062306a36Sopenharmony_ci	} else if (!is_input_yuv && is_output_yuv) {
77162306a36Sopenharmony_ci		y2r_en = false;
77262306a36Sopenharmony_ci		r2y_en = true;
77362306a36Sopenharmony_ci		csc_mode = vop2_convert_csc_mode(output_csc);
77462306a36Sopenharmony_ci	} else {
77562306a36Sopenharmony_ci		y2r_en = false;
77662306a36Sopenharmony_ci		r2y_en = false;
77762306a36Sopenharmony_ci		csc_mode = false;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
78162306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en);
78262306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq);
79062306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq);
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int vop2_core_clks_prepare_enable(struct vop2 *vop2)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	int ret;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	ret = clk_prepare_enable(vop2->hclk);
80562306a36Sopenharmony_ci	if (ret < 0) {
80662306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to enable hclk - %d\n", ret);
80762306a36Sopenharmony_ci		return ret;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	ret = clk_prepare_enable(vop2->aclk);
81162306a36Sopenharmony_ci	if (ret < 0) {
81262306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
81362306a36Sopenharmony_ci		goto err;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return 0;
81762306a36Sopenharmony_cierr:
81862306a36Sopenharmony_ci	clk_disable_unprepare(vop2->hclk);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return ret;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic void vop2_enable(struct vop2 *vop2)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(vop2->dev);
82862306a36Sopenharmony_ci	if (ret < 0) {
82962306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret);
83062306a36Sopenharmony_ci		return;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	ret = vop2_core_clks_prepare_enable(vop2);
83462306a36Sopenharmony_ci	if (ret) {
83562306a36Sopenharmony_ci		pm_runtime_put_sync(vop2->dev);
83662306a36Sopenharmony_ci		return;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev);
84062306a36Sopenharmony_ci	if (ret) {
84162306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret);
84262306a36Sopenharmony_ci		return;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	regcache_sync(vop2->map);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (vop2->data->soc_id == 3566)
84862306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/*
85362306a36Sopenharmony_ci	 * Disable auto gating, this is a workaround to
85462306a36Sopenharmony_ci	 * avoid display image shift when a window enabled.
85562306a36Sopenharmony_ci	 */
85662306a36Sopenharmony_ci	regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
85762306a36Sopenharmony_ci			  RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS0_INT_CLR,
86062306a36Sopenharmony_ci		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
86162306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS0_INT_EN,
86262306a36Sopenharmony_ci		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
86362306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS1_INT_CLR,
86462306a36Sopenharmony_ci		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
86562306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS1_INT_EN,
86662306a36Sopenharmony_ci		    VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR);
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic void vop2_disable(struct vop2 *vop2)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	pm_runtime_put_sync(vop2->dev);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	regcache_mark_dirty(vop2->map);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	clk_disable_unprepare(vop2->aclk);
87862306a36Sopenharmony_ci	clk_disable_unprepare(vop2->hclk);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
88262306a36Sopenharmony_ci				     struct drm_atomic_state *state)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
88562306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
88662306a36Sopenharmony_ci	struct drm_crtc_state *old_crtc_state;
88762306a36Sopenharmony_ci	int ret;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	vop2_lock(vop2);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
89262306a36Sopenharmony_ci	drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/*
89762306a36Sopenharmony_ci	 * Vop standby will take effect at end of current frame,
89862306a36Sopenharmony_ci	 * if dsp hold valid irq happen, it means standby complete.
89962306a36Sopenharmony_ci	 *
90062306a36Sopenharmony_ci	 * we must wait standby complete when we want to disable aclk,
90162306a36Sopenharmony_ci	 * if not, memory bus maybe dead.
90262306a36Sopenharmony_ci	 */
90362306a36Sopenharmony_ci	reinit_completion(&vp->dsp_hold_completion);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
91062306a36Sopenharmony_ci					  msecs_to_jiffies(50));
91162306a36Sopenharmony_ci	if (!ret)
91262306a36Sopenharmony_ci		drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	clk_disable_unprepare(vp->dclk);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	vop2->enable_count--;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (!vop2->enable_count)
92162306a36Sopenharmony_ci		vop2_disable(vop2);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	vop2_unlock(vop2);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (crtc->state->event && !crtc->state->active) {
92662306a36Sopenharmony_ci		spin_lock_irq(&crtc->dev->event_lock);
92762306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
92862306a36Sopenharmony_ci		spin_unlock_irq(&crtc->dev->event_lock);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		crtc->state->event = NULL;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int vop2_plane_atomic_check(struct drm_plane *plane,
93562306a36Sopenharmony_ci				   struct drm_atomic_state *astate)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane);
93862306a36Sopenharmony_ci	struct drm_framebuffer *fb = pstate->fb;
93962306a36Sopenharmony_ci	struct drm_crtc *crtc = pstate->crtc;
94062306a36Sopenharmony_ci	struct drm_crtc_state *cstate;
94162306a36Sopenharmony_ci	struct vop2_video_port *vp;
94262306a36Sopenharmony_ci	struct vop2 *vop2;
94362306a36Sopenharmony_ci	const struct vop2_data *vop2_data;
94462306a36Sopenharmony_ci	struct drm_rect *dest = &pstate->dst;
94562306a36Sopenharmony_ci	struct drm_rect *src = &pstate->src;
94662306a36Sopenharmony_ci	int min_scale = FRAC_16_16(1, 8);
94762306a36Sopenharmony_ci	int max_scale = FRAC_16_16(8, 1);
94862306a36Sopenharmony_ci	int format;
94962306a36Sopenharmony_ci	int ret;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (!crtc)
95262306a36Sopenharmony_ci		return 0;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	vp = to_vop2_video_port(crtc);
95562306a36Sopenharmony_ci	vop2 = vp->vop2;
95662306a36Sopenharmony_ci	vop2_data = vop2->data;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
95962306a36Sopenharmony_ci	if (WARN_ON(!cstate))
96062306a36Sopenharmony_ci		return -EINVAL;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(pstate, cstate,
96362306a36Sopenharmony_ci						  min_scale, max_scale,
96462306a36Sopenharmony_ci						  true, true);
96562306a36Sopenharmony_ci	if (ret)
96662306a36Sopenharmony_ci		return ret;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (!pstate->visible)
96962306a36Sopenharmony_ci		return 0;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	format = vop2_convert_format(fb->format->format);
97262306a36Sopenharmony_ci	if (format < 0)
97362306a36Sopenharmony_ci		return format;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
97662306a36Sopenharmony_ci	    drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) {
97762306a36Sopenharmony_ci		drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
97862306a36Sopenharmony_ci			drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
97962306a36Sopenharmony_ci			drm_rect_width(dest), drm_rect_height(dest));
98062306a36Sopenharmony_ci		pstate->visible = false;
98162306a36Sopenharmony_ci		return 0;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
98562306a36Sopenharmony_ci	    drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
98662306a36Sopenharmony_ci		drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
98762306a36Sopenharmony_ci			drm_rect_width(src) >> 16,
98862306a36Sopenharmony_ci			drm_rect_height(src) >> 16,
98962306a36Sopenharmony_ci			vop2_data->max_input.width,
99062306a36Sopenharmony_ci			vop2_data->max_input.height);
99162306a36Sopenharmony_ci		return -EINVAL;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/*
99562306a36Sopenharmony_ci	 * Src.x1 can be odd when do clip, but yuv plane start point
99662306a36Sopenharmony_ci	 * need align with 2 pixel.
99762306a36Sopenharmony_ci	 */
99862306a36Sopenharmony_ci	if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
99962306a36Sopenharmony_ci		drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
100062306a36Sopenharmony_ci		return -EINVAL;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic void vop2_plane_atomic_disable(struct drm_plane *plane,
100762306a36Sopenharmony_ci				      struct drm_atomic_state *state)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct drm_plane_state *old_pstate = NULL;
101062306a36Sopenharmony_ci	struct vop2_win *win = to_vop2_win(plane);
101162306a36Sopenharmony_ci	struct vop2 *vop2 = win->vop2;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	drm_dbg(vop2->drm, "%s disable\n", win->data->name);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (state)
101662306a36Sopenharmony_ci		old_pstate = drm_atomic_get_old_plane_state(state, plane);
101762306a36Sopenharmony_ci	if (old_pstate && !old_pstate->crtc)
101862306a36Sopenharmony_ci		return;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	vop2_win_disable(win);
102162306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0);
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci/*
102562306a36Sopenharmony_ci * The color key is 10 bit, so all format should
102662306a36Sopenharmony_ci * convert to 10 bit here.
102762306a36Sopenharmony_ci */
102862306a36Sopenharmony_cistatic void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct drm_plane_state *pstate = plane->state;
103162306a36Sopenharmony_ci	struct drm_framebuffer *fb = pstate->fb;
103262306a36Sopenharmony_ci	struct vop2_win *win = to_vop2_win(plane);
103362306a36Sopenharmony_ci	u32 color_key_en = 0;
103462306a36Sopenharmony_ci	u32 r = 0;
103562306a36Sopenharmony_ci	u32 g = 0;
103662306a36Sopenharmony_ci	u32 b = 0;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) {
103962306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0);
104062306a36Sopenharmony_ci		return;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	switch (fb->format->format) {
104462306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
104562306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
104662306a36Sopenharmony_ci		r = (color_key & 0xf800) >> 11;
104762306a36Sopenharmony_ci		g = (color_key & 0x7e0) >> 5;
104862306a36Sopenharmony_ci		b = (color_key & 0x1f);
104962306a36Sopenharmony_ci		r <<= 5;
105062306a36Sopenharmony_ci		g <<= 4;
105162306a36Sopenharmony_ci		b <<= 5;
105262306a36Sopenharmony_ci		color_key_en = 1;
105362306a36Sopenharmony_ci		break;
105462306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
105562306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
105662306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
105762306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
105862306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
105962306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
106062306a36Sopenharmony_ci		r = (color_key & 0xff0000) >> 16;
106162306a36Sopenharmony_ci		g = (color_key & 0xff00) >> 8;
106262306a36Sopenharmony_ci		b = (color_key & 0xff);
106362306a36Sopenharmony_ci		r <<= 2;
106462306a36Sopenharmony_ci		g <<= 2;
106562306a36Sopenharmony_ci		b <<= 2;
106662306a36Sopenharmony_ci		color_key_en = 1;
106762306a36Sopenharmony_ci		break;
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en);
107162306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic void vop2_plane_atomic_update(struct drm_plane *plane,
107562306a36Sopenharmony_ci				     struct drm_atomic_state *state)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct drm_plane_state *pstate = plane->state;
107862306a36Sopenharmony_ci	struct drm_crtc *crtc = pstate->crtc;
107962306a36Sopenharmony_ci	struct vop2_win *win = to_vop2_win(plane);
108062306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
108162306a36Sopenharmony_ci	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
108262306a36Sopenharmony_ci	struct vop2 *vop2 = win->vop2;
108362306a36Sopenharmony_ci	struct drm_framebuffer *fb = pstate->fb;
108462306a36Sopenharmony_ci	u32 bpp = fb->format->cpp[0] * 8;
108562306a36Sopenharmony_ci	u32 actual_w, actual_h, dsp_w, dsp_h;
108662306a36Sopenharmony_ci	u32 act_info, dsp_info;
108762306a36Sopenharmony_ci	u32 format;
108862306a36Sopenharmony_ci	u32 afbc_format;
108962306a36Sopenharmony_ci	u32 rb_swap;
109062306a36Sopenharmony_ci	u32 uv_swap;
109162306a36Sopenharmony_ci	struct drm_rect *src = &pstate->src;
109262306a36Sopenharmony_ci	struct drm_rect *dest = &pstate->dst;
109362306a36Sopenharmony_ci	u32 afbc_tile_num;
109462306a36Sopenharmony_ci	u32 transform_offset;
109562306a36Sopenharmony_ci	bool dither_up;
109662306a36Sopenharmony_ci	bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false;
109762306a36Sopenharmony_ci	bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false;
109862306a36Sopenharmony_ci	bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270;
109962306a36Sopenharmony_ci	bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90;
110062306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
110162306a36Sopenharmony_ci	unsigned long offset;
110262306a36Sopenharmony_ci	bool afbc_en;
110362306a36Sopenharmony_ci	dma_addr_t yrgb_mst;
110462306a36Sopenharmony_ci	dma_addr_t uv_mst;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/*
110762306a36Sopenharmony_ci	 * can't update plane when vop2 is disabled.
110862306a36Sopenharmony_ci	 */
110962306a36Sopenharmony_ci	if (WARN_ON(!crtc))
111062306a36Sopenharmony_ci		return;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (!pstate->visible) {
111362306a36Sopenharmony_ci		vop2_plane_atomic_disable(plane, state);
111462306a36Sopenharmony_ci		return;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	afbc_en = rockchip_afbc(plane, fb->modifier);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	offset = (src->x1 >> 16) * fb->format->cpp[0];
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	/*
112262306a36Sopenharmony_ci	 * AFBC HDR_PTR must set to the zero offset of the framebuffer.
112362306a36Sopenharmony_ci	 */
112462306a36Sopenharmony_ci	if (afbc_en)
112562306a36Sopenharmony_ci		offset = 0;
112662306a36Sopenharmony_ci	else if (pstate->rotation & DRM_MODE_REFLECT_Y)
112762306a36Sopenharmony_ci		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
112862306a36Sopenharmony_ci	else
112962306a36Sopenharmony_ci		offset += (src->y1 >> 16) * fb->pitches[0];
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	rk_obj = to_rockchip_obj(fb->obj[0]);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
113462306a36Sopenharmony_ci	if (fb->format->is_yuv) {
113562306a36Sopenharmony_ci		int hsub = fb->format->hsub;
113662306a36Sopenharmony_ci		int vsub = fb->format->vsub;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
113962306a36Sopenharmony_ci		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en)
114262306a36Sopenharmony_ci			offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		rk_obj = to_rockchip_obj(fb->obj[0]);
114562306a36Sopenharmony_ci		uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	actual_w = drm_rect_width(src) >> 16;
114962306a36Sopenharmony_ci	actual_h = drm_rect_height(src) >> 16;
115062306a36Sopenharmony_ci	dsp_w = drm_rect_width(dest);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
115362306a36Sopenharmony_ci		drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
115462306a36Sopenharmony_ci			vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
115562306a36Sopenharmony_ci		dsp_w = adjusted_mode->hdisplay - dest->x1;
115662306a36Sopenharmony_ci		if (dsp_w < 4)
115762306a36Sopenharmony_ci			dsp_w = 4;
115862306a36Sopenharmony_ci		actual_w = dsp_w * actual_w / drm_rect_width(dest);
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	dsp_h = drm_rect_height(dest);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
116462306a36Sopenharmony_ci		drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
116562306a36Sopenharmony_ci			vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
116662306a36Sopenharmony_ci		dsp_h = adjusted_mode->vdisplay - dest->y1;
116762306a36Sopenharmony_ci		if (dsp_h < 4)
116862306a36Sopenharmony_ci			dsp_h = 4;
116962306a36Sopenharmony_ci		actual_h = dsp_h * actual_h / drm_rect_height(dest);
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	/*
117362306a36Sopenharmony_ci	 * This is workaround solution for IC design:
117462306a36Sopenharmony_ci	 * esmart can't support scale down when actual_w % 16 == 1.
117562306a36Sopenharmony_ci	 */
117662306a36Sopenharmony_ci	if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
117762306a36Sopenharmony_ci		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
117862306a36Sopenharmony_ci			drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
117962306a36Sopenharmony_ci				vp->id, win->data->name, actual_w);
118062306a36Sopenharmony_ci			actual_w -= 1;
118162306a36Sopenharmony_ci		}
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (afbc_en && actual_w % 4) {
118562306a36Sopenharmony_ci		drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
118662306a36Sopenharmony_ci			vp->id, win->data->name, actual_w);
118762306a36Sopenharmony_ci		actual_w = ALIGN_DOWN(actual_w, 4);
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
119162306a36Sopenharmony_ci	dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	format = vop2_convert_format(fb->format->format);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
119662306a36Sopenharmony_ci		vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
119762306a36Sopenharmony_ci		dest->x1, dest->y1,
119862306a36Sopenharmony_ci		&fb->format->format,
119962306a36Sopenharmony_ci		afbc_en ? "AFBC" : "", &yrgb_mst);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (afbc_en) {
120262306a36Sopenharmony_ci		u32 stride;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		/* the afbc superblock is 16 x 16 */
120562306a36Sopenharmony_ci		afbc_format = vop2_convert_afbc_format(fb->format->format);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		/* Enable color transform for YTR */
120862306a36Sopenharmony_ci		if (fb->modifier & AFBC_FORMAT_MOD_YTR)
120962306a36Sopenharmony_ci			afbc_format |= (1 << 4);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		afbc_tile_num = ALIGN(actual_w, 16) >> 4;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		/*
121462306a36Sopenharmony_ci		 * AFBC pic_vir_width is count by pixel, this is different
121562306a36Sopenharmony_ci		 * with WIN_VIR_STRIDE.
121662306a36Sopenharmony_ci		 */
121762306a36Sopenharmony_ci		stride = (fb->pitches[0] << 3) / bpp;
121862306a36Sopenharmony_ci		if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
121962306a36Sopenharmony_ci			drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
122062306a36Sopenharmony_ci				vp->id, win->data->name, stride);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		rb_swap = vop2_afbc_rb_swap(fb->format->format);
122362306a36Sopenharmony_ci		uv_swap = vop2_afbc_uv_swap(fb->format->format);
122462306a36Sopenharmony_ci		/*
122562306a36Sopenharmony_ci		 * This is a workaround for crazy IC design, Cluster
122662306a36Sopenharmony_ci		 * and Esmart/Smart use different format configuration map:
122762306a36Sopenharmony_ci		 * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart.
122862306a36Sopenharmony_ci		 *
122962306a36Sopenharmony_ci		 * This is one thing we can make the convert simple:
123062306a36Sopenharmony_ci		 * AFBCD decode all the YUV data to YUV444. So we just
123162306a36Sopenharmony_ci		 * set all the yuv 10 bit to YUV444_10.
123262306a36Sopenharmony_ci		 */
123362306a36Sopenharmony_ci		if (fb->format->is_yuv && bpp == 10)
123462306a36Sopenharmony_ci			format = VOP2_CLUSTER_YUV444_10;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		if (vop2_cluster_window(win))
123762306a36Sopenharmony_ci			vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
123862306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
123962306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap);
124062306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
124162306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
124262306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
124362306a36Sopenharmony_ci		if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
124462306a36Sopenharmony_ci			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
124562306a36Sopenharmony_ci			transform_offset = vop2_afbc_transform_offset(pstate, false);
124662306a36Sopenharmony_ci		} else {
124762306a36Sopenharmony_ci			vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1);
124862306a36Sopenharmony_ci			transform_offset = vop2_afbc_transform_offset(pstate, true);
124962306a36Sopenharmony_ci		}
125062306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst);
125162306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info);
125262306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset);
125362306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1));
125462306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16)));
125562306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride);
125662306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num);
125762306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror);
125862306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270);
125962306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90);
126062306a36Sopenharmony_ci	} else {
126162306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4));
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	if (rotate_90 || rotate_270) {
126762306a36Sopenharmony_ci		act_info = swahw32(act_info);
126862306a36Sopenharmony_ci		actual_w = drm_rect_height(src) >> 16;
126962306a36Sopenharmony_ci		actual_h = drm_rect_width(src) >> 16;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_FORMAT, format);
127362306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	rb_swap = vop2_win_rb_swap(fb->format->format);
127662306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap);
127762306a36Sopenharmony_ci	if (!vop2_cluster_window(win)) {
127862306a36Sopenharmony_ci		uv_swap = vop2_win_uv_swap(fb->format->format);
127962306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap);
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (fb->format->is_yuv) {
128362306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4));
128462306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
128862306a36Sopenharmony_ci	if (!vop2_cluster_window(win))
128962306a36Sopenharmony_ci		vop2_plane_setup_color_key(plane, 0);
129062306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
129162306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info);
129262306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff));
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	vop2_setup_csc_mode(vp, win, pstate);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	dither_up = vop2_win_dither_up(fb->format->format);
129762306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	vop2_win_write(win, VOP2_WIN_ENABLE, 1);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (vop2_cluster_window(win)) {
130262306a36Sopenharmony_ci		int lb_mode = vop2_get_cluster_lb_mode(win, pstate);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode);
130562306a36Sopenharmony_ci		vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1);
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vop2_plane_helper_funcs = {
131062306a36Sopenharmony_ci	.atomic_check = vop2_plane_atomic_check,
131162306a36Sopenharmony_ci	.atomic_update = vop2_plane_atomic_update,
131262306a36Sopenharmony_ci	.atomic_disable = vop2_plane_atomic_disable,
131362306a36Sopenharmony_ci};
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic const struct drm_plane_funcs vop2_plane_funcs = {
131662306a36Sopenharmony_ci	.update_plane	= drm_atomic_helper_update_plane,
131762306a36Sopenharmony_ci	.disable_plane	= drm_atomic_helper_disable_plane,
131862306a36Sopenharmony_ci	.destroy = drm_plane_cleanup,
131962306a36Sopenharmony_ci	.reset = drm_atomic_helper_plane_reset,
132062306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
132162306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
132262306a36Sopenharmony_ci	.format_mod_supported = rockchip_vop2_mod_supported,
132362306a36Sopenharmony_ci};
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_cistatic int vop2_crtc_enable_vblank(struct drm_crtc *crtc)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	return 0;
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic void vop2_crtc_disable_vblank(struct drm_crtc *crtc)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD);
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cistatic bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
134262306a36Sopenharmony_ci				 const struct drm_display_mode *mode,
134362306a36Sopenharmony_ci				 struct drm_display_mode *adj_mode)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
134662306a36Sopenharmony_ci					CRTC_STEREO_DOUBLE);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	return true;
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	switch (vcstate->bus_format) {
135662306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB565_1X16:
135762306a36Sopenharmony_ci		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
135862306a36Sopenharmony_ci		break;
135962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X18:
136062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
136162306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
136262306a36Sopenharmony_ci		*dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN;
136362306a36Sopenharmony_ci		*dsp_ctrl |= RGB888_TO_RGB666;
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_YUV8_1X24:
136662306a36Sopenharmony_ci	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
136762306a36Sopenharmony_ci		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
136862306a36Sopenharmony_ci		break;
136962306a36Sopenharmony_ci	default:
137062306a36Sopenharmony_ci		break;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA)
137462306a36Sopenharmony_ci		*dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	*dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL,
137762306a36Sopenharmony_ci				DITHER_DOWN_ALLEGRO);
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic void vop2_post_config(struct drm_crtc *crtc)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
138362306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
138462306a36Sopenharmony_ci	u16 vtotal = mode->crtc_vtotal;
138562306a36Sopenharmony_ci	u16 hdisplay = mode->crtc_hdisplay;
138662306a36Sopenharmony_ci	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
138762306a36Sopenharmony_ci	u16 vdisplay = mode->crtc_vdisplay;
138862306a36Sopenharmony_ci	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
138962306a36Sopenharmony_ci	u32 left_margin = 100, right_margin = 100;
139062306a36Sopenharmony_ci	u32 top_margin = 100, bottom_margin = 100;
139162306a36Sopenharmony_ci	u16 hsize = hdisplay * (left_margin + right_margin) / 200;
139262306a36Sopenharmony_ci	u16 vsize = vdisplay * (top_margin + bottom_margin) / 200;
139362306a36Sopenharmony_ci	u16 hact_end, vact_end;
139462306a36Sopenharmony_ci	u32 val;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	vsize = rounddown(vsize, 2);
139762306a36Sopenharmony_ci	hsize = rounddown(hsize, 2);
139862306a36Sopenharmony_ci	hact_st += hdisplay * (100 - left_margin) / 200;
139962306a36Sopenharmony_ci	hact_end = hact_st + hsize;
140062306a36Sopenharmony_ci	val = hact_st << 16;
140162306a36Sopenharmony_ci	val |= hact_end;
140262306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val);
140362306a36Sopenharmony_ci	vact_st += vdisplay * (100 - top_margin) / 200;
140462306a36Sopenharmony_ci	vact_end = vact_st + vsize;
140562306a36Sopenharmony_ci	val = vact_st << 16;
140662306a36Sopenharmony_ci	val |= vact_end;
140762306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val);
140862306a36Sopenharmony_ci	val = scl_cal_scale2(vdisplay, vsize) << 16;
140962306a36Sopenharmony_ci	val |= scl_cal_scale2(hdisplay, hsize);
141062306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	val = 0;
141362306a36Sopenharmony_ci	if (hdisplay != hsize)
141462306a36Sopenharmony_ci		val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN;
141562306a36Sopenharmony_ci	if (vdisplay != vsize)
141662306a36Sopenharmony_ci		val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
141762306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
142062306a36Sopenharmony_ci		u16 vact_st_f1 = vtotal + vact_st + 1;
142162306a36Sopenharmony_ci		u16 vact_end_f1 = vact_st_f1 + vsize;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		val = vact_st_f1 << 16 | vact_end_f1;
142462306a36Sopenharmony_ci		vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_BG, 0);
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
143162306a36Sopenharmony_ci				u32 polflags)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
143462306a36Sopenharmony_ci	u32 die, dip;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	die = vop2_readl(vop2, RK3568_DSP_IF_EN);
143762306a36Sopenharmony_ci	dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	switch (id) {
144062306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_RGB0:
144162306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
144262306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_RGB |
144362306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
144462306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
144562306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
144662306a36Sopenharmony_ci		if (polflags & POLFLAG_DCLK_INV)
144762306a36Sopenharmony_ci			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
144862306a36Sopenharmony_ci		else
144962306a36Sopenharmony_ci			regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16));
145062306a36Sopenharmony_ci		break;
145162306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_HDMI0:
145262306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX;
145362306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
145462306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
145562306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL;
145662306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags);
145762306a36Sopenharmony_ci		break;
145862306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_EDP0:
145962306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
146062306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_EDP |
146162306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id);
146262306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL;
146362306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags);
146462306a36Sopenharmony_ci		break;
146562306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_MIPI0:
146662306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX;
146762306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 |
146862306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id);
146962306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
147062306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
147162306a36Sopenharmony_ci		break;
147262306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_MIPI1:
147362306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX;
147462306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 |
147562306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
147662306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL;
147762306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags);
147862306a36Sopenharmony_ci		break;
147962306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_LVDS0:
148062306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX;
148162306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 |
148262306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id);
148362306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
148462306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
148562306a36Sopenharmony_ci		break;
148662306a36Sopenharmony_ci	case ROCKCHIP_VOP2_EP_LVDS1:
148762306a36Sopenharmony_ci		die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX;
148862306a36Sopenharmony_ci		die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 |
148962306a36Sopenharmony_ci			   FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id);
149062306a36Sopenharmony_ci		dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
149162306a36Sopenharmony_ci		dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
149262306a36Sopenharmony_ci		break;
149362306a36Sopenharmony_ci	default:
149462306a36Sopenharmony_ci		drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
149562306a36Sopenharmony_ci		return;
149662306a36Sopenharmony_ci	}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_DSP_IF_EN, die);
150162306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic int us_to_vertical_line(struct drm_display_mode *mode, int us)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	return us * mode->clock / mode->htotal / 1000;
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_cistatic void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
151062306a36Sopenharmony_ci				    struct drm_atomic_state *state)
151162306a36Sopenharmony_ci{
151262306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
151362306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
151462306a36Sopenharmony_ci	const struct vop2_data *vop2_data = vop2->data;
151562306a36Sopenharmony_ci	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
151662306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
151762306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
151862306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
151962306a36Sopenharmony_ci	unsigned long clock = mode->crtc_clock * 1000;
152062306a36Sopenharmony_ci	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
152162306a36Sopenharmony_ci	u16 hdisplay = mode->crtc_hdisplay;
152262306a36Sopenharmony_ci	u16 htotal = mode->crtc_htotal;
152362306a36Sopenharmony_ci	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
152462306a36Sopenharmony_ci	u16 hact_end = hact_st + hdisplay;
152562306a36Sopenharmony_ci	u16 vdisplay = mode->crtc_vdisplay;
152662306a36Sopenharmony_ci	u16 vtotal = mode->crtc_vtotal;
152762306a36Sopenharmony_ci	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
152862306a36Sopenharmony_ci	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
152962306a36Sopenharmony_ci	u16 vact_end = vact_st + vdisplay;
153062306a36Sopenharmony_ci	u8 out_mode;
153162306a36Sopenharmony_ci	u32 dsp_ctrl = 0;
153262306a36Sopenharmony_ci	int act_end;
153362306a36Sopenharmony_ci	u32 val, polflags;
153462306a36Sopenharmony_ci	int ret;
153562306a36Sopenharmony_ci	struct drm_encoder *encoder;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
153862306a36Sopenharmony_ci		hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
153962306a36Sopenharmony_ci		drm_mode_vrefresh(mode), vcstate->output_type, vp->id);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	vop2_lock(vop2);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	ret = clk_prepare_enable(vp->dclk);
154462306a36Sopenharmony_ci	if (ret < 0) {
154562306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
154662306a36Sopenharmony_ci			vp->id, ret);
154762306a36Sopenharmony_ci		vop2_unlock(vop2);
154862306a36Sopenharmony_ci		return;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (!vop2->enable_count)
155262306a36Sopenharmony_ci		vop2_enable(vop2);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	vop2->enable_count++;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	polflags = 0;
155962306a36Sopenharmony_ci	if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
156062306a36Sopenharmony_ci		polflags |= POLFLAG_DCLK_INV;
156162306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
156262306a36Sopenharmony_ci		polflags |= BIT(HSYNC_POSITIVE);
156362306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
156462306a36Sopenharmony_ci		polflags |= BIT(VSYNC_POSITIVE);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
156762306a36Sopenharmony_ci		struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
157362306a36Sopenharmony_ci	    !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT))
157462306a36Sopenharmony_ci		out_mode = ROCKCHIP_OUT_MODE_P888;
157562306a36Sopenharmony_ci	else
157662306a36Sopenharmony_ci		out_mode = vcstate->output_mode;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
158162306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (is_yuv_output(vcstate->bus_format))
158462306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	vop2_dither_setup(crtc, &dsp_ctrl);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len);
158962306a36Sopenharmony_ci	val = hact_st << 16;
159062306a36Sopenharmony_ci	val |= hact_end;
159162306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	val = vact_st << 16;
159462306a36Sopenharmony_ci	val |= vact_end;
159562306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
159862306a36Sopenharmony_ci		u16 vact_st_f1 = vtotal + vact_st + 1;
159962306a36Sopenharmony_ci		u16 vact_end_f1 = vact_st_f1 + vdisplay;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci		val = vact_st_f1 << 16 | vact_end_f1;
160262306a36Sopenharmony_ci		vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci		val = vtotal << 16 | (vtotal + vsync_len);
160562306a36Sopenharmony_ci		vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val);
160662306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE;
160762306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL;
160862306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN;
160962306a36Sopenharmony_ci		vtotal += vtotal + 1;
161062306a36Sopenharmony_ci		act_end = vact_end_f1;
161162306a36Sopenharmony_ci	} else {
161262306a36Sopenharmony_ci		act_end = vact_end;
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id),
161662306a36Sopenharmony_ci		    (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
162162306a36Sopenharmony_ci		dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
162262306a36Sopenharmony_ci		clock *= 2;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	clk_set_rate(vp->dclk, clock);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	vop2_post_config(crtc);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	vop2_cfg_done(vp);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	vop2_unlock(vop2);
163862306a36Sopenharmony_ci}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_cistatic int vop2_crtc_atomic_check(struct drm_crtc *crtc,
164162306a36Sopenharmony_ci				  struct drm_atomic_state *state)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
164462306a36Sopenharmony_ci	struct drm_plane *plane;
164562306a36Sopenharmony_ci	int nplanes = 0;
164662306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
164962306a36Sopenharmony_ci		nplanes++;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (nplanes > vp->nlayers)
165262306a36Sopenharmony_ci		return -EINVAL;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	return 0;
165562306a36Sopenharmony_ci}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_cistatic bool is_opaque(u16 alpha)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	return (alpha >> 8) == 0xff;
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
166362306a36Sopenharmony_ci			     struct vop2_alpha *alpha)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1;
166662306a36Sopenharmony_ci	int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1;
166762306a36Sopenharmony_ci	int src_color_mode = alpha_config->src_premulti_en ?
166862306a36Sopenharmony_ci				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
166962306a36Sopenharmony_ci	int dst_color_mode = alpha_config->dst_premulti_en ?
167062306a36Sopenharmony_ci				ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	alpha->src_color_ctrl.val = 0;
167362306a36Sopenharmony_ci	alpha->dst_color_ctrl.val = 0;
167462306a36Sopenharmony_ci	alpha->src_alpha_ctrl.val = 0;
167562306a36Sopenharmony_ci	alpha->dst_alpha_ctrl.val = 0;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	if (!alpha_config->src_pixel_alpha_en)
167862306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
167962306a36Sopenharmony_ci	else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en)
168062306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX;
168162306a36Sopenharmony_ci	else
168262306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	alpha->src_color_ctrl.bits.alpha_en = 1;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) {
168762306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
168862306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
168962306a36Sopenharmony_ci	} else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) {
169062306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.color_mode = src_color_mode;
169162306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE;
169262306a36Sopenharmony_ci	} else {
169362306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL;
169462306a36Sopenharmony_ci		alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci	alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8;
169762306a36Sopenharmony_ci	alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
169862306a36Sopenharmony_ci	alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
170162306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
170262306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL;
170362306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8;
170462306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.color_mode = dst_color_mode;
170562306a36Sopenharmony_ci	alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
170862306a36Sopenharmony_ci	alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode;
170962306a36Sopenharmony_ci	alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION;
171062306a36Sopenharmony_ci	alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT;
171362306a36Sopenharmony_ci	if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en)
171462306a36Sopenharmony_ci		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX;
171562306a36Sopenharmony_ci	else
171662306a36Sopenharmony_ci		alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL;
171762306a36Sopenharmony_ci	alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION;
171862306a36Sopenharmony_ci	alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cistatic int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	struct vop2_video_port *vp;
172462306a36Sopenharmony_ci	int used_layer = 0;
172562306a36Sopenharmony_ci	int i;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	for (i = 0; i < port_id; i++) {
172862306a36Sopenharmony_ci		vp = &vop2->vps[i];
172962306a36Sopenharmony_ci		used_layer += hweight32(vp->win_mask);
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return used_layer;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	u32 offset = (main_win->data->phys_id * 0x10);
173862306a36Sopenharmony_ci	struct vop2_alpha_config alpha_config;
173962306a36Sopenharmony_ci	struct vop2_alpha alpha;
174062306a36Sopenharmony_ci	struct drm_plane_state *bottom_win_pstate;
174162306a36Sopenharmony_ci	bool src_pixel_alpha_en = false;
174262306a36Sopenharmony_ci	u16 src_glb_alpha_val, dst_glb_alpha_val;
174362306a36Sopenharmony_ci	bool premulti_en = false;
174462306a36Sopenharmony_ci	bool swap = false;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	/* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */
174762306a36Sopenharmony_ci	bottom_win_pstate = main_win->base.state;
174862306a36Sopenharmony_ci	src_glb_alpha_val = 0;
174962306a36Sopenharmony_ci	dst_glb_alpha_val = main_win->base.state->alpha;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	if (!bottom_win_pstate->fb)
175262306a36Sopenharmony_ci		return;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	alpha_config.src_premulti_en = premulti_en;
175562306a36Sopenharmony_ci	alpha_config.dst_premulti_en = false;
175662306a36Sopenharmony_ci	alpha_config.src_pixel_alpha_en = src_pixel_alpha_en;
175762306a36Sopenharmony_ci	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
175862306a36Sopenharmony_ci	alpha_config.src_glb_alpha_value = src_glb_alpha_val;
175962306a36Sopenharmony_ci	alpha_config.dst_glb_alpha_value = dst_glb_alpha_val;
176062306a36Sopenharmony_ci	vop2_parse_alpha(&alpha_config, &alpha);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	alpha.src_color_ctrl.bits.src_dst_swap = swap;
176362306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset,
176462306a36Sopenharmony_ci		    alpha.src_color_ctrl.val);
176562306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset,
176662306a36Sopenharmony_ci		    alpha.dst_color_ctrl.val);
176762306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset,
176862306a36Sopenharmony_ci		    alpha.src_alpha_ctrl.val);
176962306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset,
177062306a36Sopenharmony_ci		    alpha.dst_alpha_ctrl.val);
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic void vop2_setup_alpha(struct vop2_video_port *vp)
177462306a36Sopenharmony_ci{
177562306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
177662306a36Sopenharmony_ci	struct drm_framebuffer *fb;
177762306a36Sopenharmony_ci	struct vop2_alpha_config alpha_config;
177862306a36Sopenharmony_ci	struct vop2_alpha alpha;
177962306a36Sopenharmony_ci	struct drm_plane *plane;
178062306a36Sopenharmony_ci	int pixel_alpha_en;
178162306a36Sopenharmony_ci	int premulti_en, gpremulti_en = 0;
178262306a36Sopenharmony_ci	int mixer_id;
178362306a36Sopenharmony_ci	u32 offset;
178462306a36Sopenharmony_ci	bool bottom_layer_alpha_en = false;
178562306a36Sopenharmony_ci	u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
178862306a36Sopenharmony_ci	alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
179162306a36Sopenharmony_ci		struct vop2_win *win = to_vop2_win(plane);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci		if (plane->state->normalized_zpos == 0 &&
179462306a36Sopenharmony_ci		    !is_opaque(plane->state->alpha) &&
179562306a36Sopenharmony_ci		    !vop2_cluster_window(win)) {
179662306a36Sopenharmony_ci			/*
179762306a36Sopenharmony_ci			 * If bottom layer have global alpha effect [except cluster layer,
179862306a36Sopenharmony_ci			 * because cluster have deal with bottom layer global alpha value
179962306a36Sopenharmony_ci			 * at cluster mix], bottom layer mix need deal with global alpha.
180062306a36Sopenharmony_ci			 */
180162306a36Sopenharmony_ci			bottom_layer_alpha_en = true;
180262306a36Sopenharmony_ci			dst_global_alpha = plane->state->alpha;
180362306a36Sopenharmony_ci		}
180462306a36Sopenharmony_ci	}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
180762306a36Sopenharmony_ci		struct vop2_win *win = to_vop2_win(plane);
180862306a36Sopenharmony_ci		int zpos = plane->state->normalized_zpos;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci		if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
181162306a36Sopenharmony_ci			premulti_en = 1;
181262306a36Sopenharmony_ci		else
181362306a36Sopenharmony_ci			premulti_en = 0;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		plane = &win->base;
181662306a36Sopenharmony_ci		fb = plane->state->fb;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		pixel_alpha_en = fb->format->has_alpha;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci		alpha_config.src_premulti_en = premulti_en;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci		if (bottom_layer_alpha_en && zpos == 1) {
182362306a36Sopenharmony_ci			gpremulti_en = premulti_en;
182462306a36Sopenharmony_ci			/* Cd = Cs + (1 - As) * Cd * Agd */
182562306a36Sopenharmony_ci			alpha_config.dst_premulti_en = false;
182662306a36Sopenharmony_ci			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
182762306a36Sopenharmony_ci			alpha_config.src_glb_alpha_value = plane->state->alpha;
182862306a36Sopenharmony_ci			alpha_config.dst_glb_alpha_value = dst_global_alpha;
182962306a36Sopenharmony_ci		} else if (vop2_cluster_window(win)) {
183062306a36Sopenharmony_ci			/* Mix output data only have pixel alpha */
183162306a36Sopenharmony_ci			alpha_config.dst_premulti_en = true;
183262306a36Sopenharmony_ci			alpha_config.src_pixel_alpha_en = true;
183362306a36Sopenharmony_ci			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
183462306a36Sopenharmony_ci			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
183562306a36Sopenharmony_ci		} else {
183662306a36Sopenharmony_ci			/* Cd = Cs + (1 - As) * Cd */
183762306a36Sopenharmony_ci			alpha_config.dst_premulti_en = true;
183862306a36Sopenharmony_ci			alpha_config.src_pixel_alpha_en = pixel_alpha_en;
183962306a36Sopenharmony_ci			alpha_config.src_glb_alpha_value = plane->state->alpha;
184062306a36Sopenharmony_ci			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
184162306a36Sopenharmony_ci		}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci		vop2_parse_alpha(&alpha_config, &alpha);
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		offset = (mixer_id + zpos - 1) * 0x10;
184662306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset,
184762306a36Sopenharmony_ci			    alpha.src_color_ctrl.val);
184862306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset,
184962306a36Sopenharmony_ci			    alpha.dst_color_ctrl.val);
185062306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset,
185162306a36Sopenharmony_ci			    alpha.src_alpha_ctrl.val);
185262306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset,
185362306a36Sopenharmony_ci			    alpha.dst_alpha_ctrl.val);
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (vp->id == 0) {
185762306a36Sopenharmony_ci		if (bottom_layer_alpha_en) {
185862306a36Sopenharmony_ci			/* Transfer pixel alpha to hdr mix */
185962306a36Sopenharmony_ci			alpha_config.src_premulti_en = gpremulti_en;
186062306a36Sopenharmony_ci			alpha_config.dst_premulti_en = true;
186162306a36Sopenharmony_ci			alpha_config.src_pixel_alpha_en = true;
186262306a36Sopenharmony_ci			alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
186362306a36Sopenharmony_ci			alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE;
186462306a36Sopenharmony_ci			vop2_parse_alpha(&alpha_config, &alpha);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL,
186762306a36Sopenharmony_ci				    alpha.src_color_ctrl.val);
186862306a36Sopenharmony_ci			vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL,
186962306a36Sopenharmony_ci				    alpha.dst_color_ctrl.val);
187062306a36Sopenharmony_ci			vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL,
187162306a36Sopenharmony_ci				    alpha.src_alpha_ctrl.val);
187262306a36Sopenharmony_ci			vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL,
187362306a36Sopenharmony_ci				    alpha.dst_alpha_ctrl.val);
187462306a36Sopenharmony_ci		} else {
187562306a36Sopenharmony_ci			vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0);
187662306a36Sopenharmony_ci		}
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_cistatic void vop2_setup_layer_mixer(struct vop2_video_port *vp)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
188362306a36Sopenharmony_ci	struct drm_plane *plane;
188462306a36Sopenharmony_ci	u32 layer_sel = 0;
188562306a36Sopenharmony_ci	u32 port_sel;
188662306a36Sopenharmony_ci	unsigned int nlayer, ofs;
188762306a36Sopenharmony_ci	struct drm_display_mode *adjusted_mode;
188862306a36Sopenharmony_ci	u16 hsync_len;
188962306a36Sopenharmony_ci	u16 hdisplay;
189062306a36Sopenharmony_ci	u32 bg_dly;
189162306a36Sopenharmony_ci	u32 pre_scan_dly;
189262306a36Sopenharmony_ci	int i;
189362306a36Sopenharmony_ci	struct vop2_video_port *vp0 = &vop2->vps[0];
189462306a36Sopenharmony_ci	struct vop2_video_port *vp1 = &vop2->vps[1];
189562306a36Sopenharmony_ci	struct vop2_video_port *vp2 = &vop2->vps[2];
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	adjusted_mode = &vp->crtc.state->adjusted_mode;
189862306a36Sopenharmony_ci	hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
189962306a36Sopenharmony_ci	hdisplay = adjusted_mode->crtc_hdisplay;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	bg_dly = vp->data->pre_scan_max_dly[3];
190262306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id),
190362306a36Sopenharmony_ci		    FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly));
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len;
190662306a36Sopenharmony_ci	vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_OVL_CTRL, 0);
190962306a36Sopenharmony_ci	port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL);
191062306a36Sopenharmony_ci	port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (vp0->nlayers)
191362306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX,
191462306a36Sopenharmony_ci				     vp0->nlayers - 1);
191562306a36Sopenharmony_ci	else
191662306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8);
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	if (vp1->nlayers)
191962306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX,
192062306a36Sopenharmony_ci				     (vp0->nlayers + vp1->nlayers - 1));
192162306a36Sopenharmony_ci	else
192262306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	if (vp2->nlayers)
192562306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX,
192662306a36Sopenharmony_ci			(vp2->nlayers + vp1->nlayers + vp0->nlayers - 1));
192762306a36Sopenharmony_ci	else
192862306a36Sopenharmony_ci		port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	ofs = 0;
193362306a36Sopenharmony_ci	for (i = 0; i < vp->id; i++)
193462306a36Sopenharmony_ci		ofs += vop2->vps[i].nlayers;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	nlayer = 0;
193762306a36Sopenharmony_ci	drm_atomic_crtc_for_each_plane(plane, &vp->crtc) {
193862306a36Sopenharmony_ci		struct vop2_win *win = to_vop2_win(plane);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci		switch (win->data->phys_id) {
194162306a36Sopenharmony_ci		case ROCKCHIP_VOP2_CLUSTER0:
194262306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0;
194362306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id);
194462306a36Sopenharmony_ci			break;
194562306a36Sopenharmony_ci		case ROCKCHIP_VOP2_CLUSTER1:
194662306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1;
194762306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id);
194862306a36Sopenharmony_ci			break;
194962306a36Sopenharmony_ci		case ROCKCHIP_VOP2_ESMART0:
195062306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0;
195162306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id);
195262306a36Sopenharmony_ci			break;
195362306a36Sopenharmony_ci		case ROCKCHIP_VOP2_ESMART1:
195462306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1;
195562306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id);
195662306a36Sopenharmony_ci			break;
195762306a36Sopenharmony_ci		case ROCKCHIP_VOP2_SMART0:
195862306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__SMART0;
195962306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id);
196062306a36Sopenharmony_ci			break;
196162306a36Sopenharmony_ci		case ROCKCHIP_VOP2_SMART1:
196262306a36Sopenharmony_ci			port_sel &= ~RK3568_OVL_PORT_SEL__SMART1;
196362306a36Sopenharmony_ci			port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id);
196462306a36Sopenharmony_ci			break;
196562306a36Sopenharmony_ci		}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs,
196862306a36Sopenharmony_ci							  0x7);
196962306a36Sopenharmony_ci		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs,
197062306a36Sopenharmony_ci							 win->data->layer_sel_id);
197162306a36Sopenharmony_ci		nlayer++;
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	/* configure unused layers to 0x5 (reserved) */
197562306a36Sopenharmony_ci	for (; nlayer < vp->nlayers; nlayer++) {
197662306a36Sopenharmony_ci		layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7);
197762306a36Sopenharmony_ci		layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5);
197862306a36Sopenharmony_ci	}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
198162306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
198262306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD);
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_cistatic void vop2_setup_dly_for_windows(struct vop2 *vop2)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	struct vop2_win *win;
198862306a36Sopenharmony_ci	int i = 0;
198962306a36Sopenharmony_ci	u32 cdly = 0, sdly = 0;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	for (i = 0; i < vop2->data->win_size; i++) {
199262306a36Sopenharmony_ci		u32 dly;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci		win = &vop2->win[i];
199562306a36Sopenharmony_ci		dly = win->delay;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci		switch (win->data->phys_id) {
199862306a36Sopenharmony_ci		case ROCKCHIP_VOP2_CLUSTER0:
199962306a36Sopenharmony_ci			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly);
200062306a36Sopenharmony_ci			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly);
200162306a36Sopenharmony_ci			break;
200262306a36Sopenharmony_ci		case ROCKCHIP_VOP2_CLUSTER1:
200362306a36Sopenharmony_ci			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly);
200462306a36Sopenharmony_ci			cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly);
200562306a36Sopenharmony_ci			break;
200662306a36Sopenharmony_ci		case ROCKCHIP_VOP2_ESMART0:
200762306a36Sopenharmony_ci			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly);
200862306a36Sopenharmony_ci			break;
200962306a36Sopenharmony_ci		case ROCKCHIP_VOP2_ESMART1:
201062306a36Sopenharmony_ci			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly);
201162306a36Sopenharmony_ci			break;
201262306a36Sopenharmony_ci		case ROCKCHIP_VOP2_SMART0:
201362306a36Sopenharmony_ci			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly);
201462306a36Sopenharmony_ci			break;
201562306a36Sopenharmony_ci		case ROCKCHIP_VOP2_SMART1:
201662306a36Sopenharmony_ci			sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly);
201762306a36Sopenharmony_ci			break;
201862306a36Sopenharmony_ci		}
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly);
202262306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly);
202362306a36Sopenharmony_ci}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cistatic void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
202662306a36Sopenharmony_ci				   struct drm_atomic_state *state)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
202962306a36Sopenharmony_ci	struct vop2 *vop2 = vp->vop2;
203062306a36Sopenharmony_ci	struct drm_plane *plane;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	vp->win_mask = 0;
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	drm_atomic_crtc_for_each_plane(plane, crtc) {
203562306a36Sopenharmony_ci		struct vop2_win *win = to_vop2_win(plane);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci		win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT];
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci		vp->win_mask |= BIT(win->data->phys_id);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci		if (vop2_cluster_window(win))
204262306a36Sopenharmony_ci			vop2_setup_cluster_alpha(vop2, win);
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	if (!vp->win_mask)
204662306a36Sopenharmony_ci		return;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	vop2_setup_layer_mixer(vp);
204962306a36Sopenharmony_ci	vop2_setup_alpha(vp);
205062306a36Sopenharmony_ci	vop2_setup_dly_for_windows(vop2);
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_cistatic void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
205462306a36Sopenharmony_ci				   struct drm_atomic_state *state)
205562306a36Sopenharmony_ci{
205662306a36Sopenharmony_ci	struct vop2_video_port *vp = to_vop2_video_port(crtc);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	vop2_post_config(crtc);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	vop2_cfg_done(vp);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	if (crtc->state->event) {
206562306a36Sopenharmony_ci		WARN_ON(drm_crtc_vblank_get(crtc));
206662306a36Sopenharmony_ci		vp->event = crtc->state->event;
206762306a36Sopenharmony_ci		crtc->state->event = NULL;
206862306a36Sopenharmony_ci	}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
207162306a36Sopenharmony_ci}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
207462306a36Sopenharmony_ci	.mode_fixup = vop2_crtc_mode_fixup,
207562306a36Sopenharmony_ci	.atomic_check = vop2_crtc_atomic_check,
207662306a36Sopenharmony_ci	.atomic_begin = vop2_crtc_atomic_begin,
207762306a36Sopenharmony_ci	.atomic_flush = vop2_crtc_atomic_flush,
207862306a36Sopenharmony_ci	.atomic_enable = vop2_crtc_atomic_enable,
207962306a36Sopenharmony_ci	.atomic_disable = vop2_crtc_atomic_disable,
208062306a36Sopenharmony_ci};
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_cistatic struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc)
208362306a36Sopenharmony_ci{
208462306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	if (WARN_ON(!crtc->state))
208762306a36Sopenharmony_ci		return NULL;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	vcstate = kmemdup(to_rockchip_crtc_state(crtc->state),
209062306a36Sopenharmony_ci			  sizeof(*vcstate), GFP_KERNEL);
209162306a36Sopenharmony_ci	if (!vcstate)
209262306a36Sopenharmony_ci		return NULL;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	return &vcstate->base;
209762306a36Sopenharmony_ci}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_cistatic void vop2_crtc_destroy_state(struct drm_crtc *crtc,
210062306a36Sopenharmony_ci				    struct drm_crtc_state *state)
210162306a36Sopenharmony_ci{
210262306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
210562306a36Sopenharmony_ci	kfree(vcstate);
210662306a36Sopenharmony_ci}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_cistatic void vop2_crtc_reset(struct drm_crtc *crtc)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	struct rockchip_crtc_state *vcstate =
211162306a36Sopenharmony_ci		kzalloc(sizeof(*vcstate), GFP_KERNEL);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if (crtc->state)
211462306a36Sopenharmony_ci		vop2_crtc_destroy_state(crtc, crtc->state);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (vcstate)
211762306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, &vcstate->base);
211862306a36Sopenharmony_ci	else
211962306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, NULL);
212062306a36Sopenharmony_ci}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_cistatic const struct drm_crtc_funcs vop2_crtc_funcs = {
212362306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
212462306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
212562306a36Sopenharmony_ci	.destroy = drm_crtc_cleanup,
212662306a36Sopenharmony_ci	.reset = vop2_crtc_reset,
212762306a36Sopenharmony_ci	.atomic_duplicate_state = vop2_crtc_duplicate_state,
212862306a36Sopenharmony_ci	.atomic_destroy_state = vop2_crtc_destroy_state,
212962306a36Sopenharmony_ci	.enable_vblank = vop2_crtc_enable_vblank,
213062306a36Sopenharmony_ci	.disable_vblank = vop2_crtc_disable_vblank,
213162306a36Sopenharmony_ci};
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_cistatic irqreturn_t vop2_isr(int irq, void *data)
213462306a36Sopenharmony_ci{
213562306a36Sopenharmony_ci	struct vop2 *vop2 = data;
213662306a36Sopenharmony_ci	const struct vop2_data *vop2_data = vop2->data;
213762306a36Sopenharmony_ci	u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM];
213862306a36Sopenharmony_ci	int ret = IRQ_NONE;
213962306a36Sopenharmony_ci	int i;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	/*
214262306a36Sopenharmony_ci	 * The irq is shared with the iommu. If the runtime-pm state of the
214362306a36Sopenharmony_ci	 * vop2-device is disabled the irq has to be targeted at the iommu.
214462306a36Sopenharmony_ci	 */
214562306a36Sopenharmony_ci	if (!pm_runtime_get_if_in_use(vop2->dev))
214662306a36Sopenharmony_ci		return IRQ_NONE;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	for (i = 0; i < vop2_data->nr_vps; i++) {
214962306a36Sopenharmony_ci		struct vop2_video_port *vp = &vop2->vps[i];
215062306a36Sopenharmony_ci		struct drm_crtc *crtc = &vp->crtc;
215162306a36Sopenharmony_ci		u32 irqs;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci		irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id));
215462306a36Sopenharmony_ci		vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci		if (irqs & VP_INT_DSP_HOLD_VALID) {
215762306a36Sopenharmony_ci			complete(&vp->dsp_hold_completion);
215862306a36Sopenharmony_ci			ret = IRQ_HANDLED;
215962306a36Sopenharmony_ci		}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci		if (irqs & VP_INT_FS_FIELD) {
216262306a36Sopenharmony_ci			drm_crtc_handle_vblank(crtc);
216362306a36Sopenharmony_ci			spin_lock(&crtc->dev->event_lock);
216462306a36Sopenharmony_ci			if (vp->event) {
216562306a36Sopenharmony_ci				u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci				if (!(val & BIT(vp->id))) {
216862306a36Sopenharmony_ci					drm_crtc_send_vblank_event(crtc, vp->event);
216962306a36Sopenharmony_ci					vp->event = NULL;
217062306a36Sopenharmony_ci					drm_crtc_vblank_put(crtc);
217162306a36Sopenharmony_ci				}
217262306a36Sopenharmony_ci			}
217362306a36Sopenharmony_ci			spin_unlock(&crtc->dev->event_lock);
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci			ret = IRQ_HANDLED;
217662306a36Sopenharmony_ci		}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci		if (irqs & VP_INT_POST_BUF_EMPTY) {
217962306a36Sopenharmony_ci			drm_err_ratelimited(vop2->drm,
218062306a36Sopenharmony_ci					    "POST_BUF_EMPTY irq err at vp%d\n",
218162306a36Sopenharmony_ci					    vp->id);
218262306a36Sopenharmony_ci			ret = IRQ_HANDLED;
218362306a36Sopenharmony_ci		}
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS);
218762306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]);
218862306a36Sopenharmony_ci	axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS);
218962306a36Sopenharmony_ci	vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]);
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) {
219262306a36Sopenharmony_ci		if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
219362306a36Sopenharmony_ci			drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
219462306a36Sopenharmony_ci			ret = IRQ_HANDLED;
219562306a36Sopenharmony_ci		}
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	pm_runtime_put(vop2->dev);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	return ret;
220162306a36Sopenharmony_ci}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_cistatic int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win,
220462306a36Sopenharmony_ci			   unsigned long possible_crtcs)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	const struct vop2_win_data *win_data = win->data;
220762306a36Sopenharmony_ci	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
220862306a36Sopenharmony_ci				  BIT(DRM_MODE_BLEND_PREMULTI) |
220962306a36Sopenharmony_ci				  BIT(DRM_MODE_BLEND_COVERAGE);
221062306a36Sopenharmony_ci	int ret;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs,
221362306a36Sopenharmony_ci				       &vop2_plane_funcs, win_data->formats,
221462306a36Sopenharmony_ci				       win_data->nformats,
221562306a36Sopenharmony_ci				       win_data->format_modifiers,
221662306a36Sopenharmony_ci				       win->type, win_data->name);
221762306a36Sopenharmony_ci	if (ret) {
221862306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to initialize plane %d\n", ret);
221962306a36Sopenharmony_ci		return ret;
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	if (win->data->supported_rotations)
222562306a36Sopenharmony_ci		drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0,
222662306a36Sopenharmony_ci						   DRM_MODE_ROTATE_0 |
222762306a36Sopenharmony_ci						   win->data->supported_rotations);
222862306a36Sopenharmony_ci	drm_plane_create_alpha_property(&win->base);
222962306a36Sopenharmony_ci	drm_plane_create_blend_mode_property(&win->base, blend_caps);
223062306a36Sopenharmony_ci	drm_plane_create_zpos_property(&win->base, win->win_id, 0,
223162306a36Sopenharmony_ci				       vop2->registered_num_wins - 1);
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	return 0;
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_cistatic struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	int i;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	for (i = 0; i < vop2->data->nr_vps; i++) {
224162306a36Sopenharmony_ci		struct vop2_video_port *vp = &vop2->vps[i];
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci		if (!vp->crtc.port)
224462306a36Sopenharmony_ci			continue;
224562306a36Sopenharmony_ci		if (vp->primary_plane)
224662306a36Sopenharmony_ci			continue;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci		return vp;
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	return NULL;
225262306a36Sopenharmony_ci}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci#define NR_LAYERS 6
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_cistatic int vop2_create_crtcs(struct vop2 *vop2)
225762306a36Sopenharmony_ci{
225862306a36Sopenharmony_ci	const struct vop2_data *vop2_data = vop2->data;
225962306a36Sopenharmony_ci	struct drm_device *drm = vop2->drm;
226062306a36Sopenharmony_ci	struct device *dev = vop2->dev;
226162306a36Sopenharmony_ci	struct drm_plane *plane;
226262306a36Sopenharmony_ci	struct device_node *port;
226362306a36Sopenharmony_ci	struct vop2_video_port *vp;
226462306a36Sopenharmony_ci	int i, nvp, nvps = 0;
226562306a36Sopenharmony_ci	int ret;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	for (i = 0; i < vop2_data->nr_vps; i++) {
226862306a36Sopenharmony_ci		const struct vop2_video_port_data *vp_data;
226962306a36Sopenharmony_ci		struct device_node *np;
227062306a36Sopenharmony_ci		char dclk_name[9];
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci		vp_data = &vop2_data->vp[i];
227362306a36Sopenharmony_ci		vp = &vop2->vps[i];
227462306a36Sopenharmony_ci		vp->vop2 = vop2;
227562306a36Sopenharmony_ci		vp->id = vp_data->id;
227662306a36Sopenharmony_ci		vp->regs = vp_data->regs;
227762306a36Sopenharmony_ci		vp->data = vp_data;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci		snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
228062306a36Sopenharmony_ci		vp->dclk = devm_clk_get(vop2->dev, dclk_name);
228162306a36Sopenharmony_ci		if (IS_ERR(vp->dclk)) {
228262306a36Sopenharmony_ci			drm_err(vop2->drm, "failed to get %s\n", dclk_name);
228362306a36Sopenharmony_ci			return PTR_ERR(vp->dclk);
228462306a36Sopenharmony_ci		}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci		np = of_graph_get_remote_node(dev->of_node, i, -1);
228762306a36Sopenharmony_ci		if (!np) {
228862306a36Sopenharmony_ci			drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
228962306a36Sopenharmony_ci			continue;
229062306a36Sopenharmony_ci		}
229162306a36Sopenharmony_ci		of_node_put(np);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci		port = of_graph_get_port_by_id(dev->of_node, i);
229462306a36Sopenharmony_ci		if (!port) {
229562306a36Sopenharmony_ci			drm_err(vop2->drm, "no port node found for video_port%d\n", i);
229662306a36Sopenharmony_ci			return -ENOENT;
229762306a36Sopenharmony_ci		}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci		vp->crtc.port = port;
230062306a36Sopenharmony_ci		nvps++;
230162306a36Sopenharmony_ci	}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	nvp = 0;
230462306a36Sopenharmony_ci	for (i = 0; i < vop2->registered_num_wins; i++) {
230562306a36Sopenharmony_ci		struct vop2_win *win = &vop2->win[i];
230662306a36Sopenharmony_ci		u32 possible_crtcs = 0;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci		if (vop2->data->soc_id == 3566) {
230962306a36Sopenharmony_ci			/*
231062306a36Sopenharmony_ci			 * On RK3566 these windows don't have an independent
231162306a36Sopenharmony_ci			 * framebuffer. They share the framebuffer with smart0,
231262306a36Sopenharmony_ci			 * esmart0 and cluster0 respectively.
231362306a36Sopenharmony_ci			 */
231462306a36Sopenharmony_ci			switch (win->data->phys_id) {
231562306a36Sopenharmony_ci			case ROCKCHIP_VOP2_SMART1:
231662306a36Sopenharmony_ci			case ROCKCHIP_VOP2_ESMART1:
231762306a36Sopenharmony_ci			case ROCKCHIP_VOP2_CLUSTER1:
231862306a36Sopenharmony_ci				continue;
231962306a36Sopenharmony_ci			}
232062306a36Sopenharmony_ci		}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci		if (win->type == DRM_PLANE_TYPE_PRIMARY) {
232362306a36Sopenharmony_ci			vp = find_vp_without_primary(vop2);
232462306a36Sopenharmony_ci			if (vp) {
232562306a36Sopenharmony_ci				possible_crtcs = BIT(nvp);
232662306a36Sopenharmony_ci				vp->primary_plane = win;
232762306a36Sopenharmony_ci				nvp++;
232862306a36Sopenharmony_ci			} else {
232962306a36Sopenharmony_ci				/* change the unused primary window to overlay window */
233062306a36Sopenharmony_ci				win->type = DRM_PLANE_TYPE_OVERLAY;
233162306a36Sopenharmony_ci			}
233262306a36Sopenharmony_ci		}
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		if (win->type == DRM_PLANE_TYPE_OVERLAY)
233562306a36Sopenharmony_ci			possible_crtcs = (1 << nvps) - 1;
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci		ret = vop2_plane_init(vop2, win, possible_crtcs);
233862306a36Sopenharmony_ci		if (ret) {
233962306a36Sopenharmony_ci			drm_err(vop2->drm, "failed to init plane %s: %d\n",
234062306a36Sopenharmony_ci				win->data->name, ret);
234162306a36Sopenharmony_ci			return ret;
234262306a36Sopenharmony_ci		}
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	for (i = 0; i < vop2_data->nr_vps; i++) {
234662306a36Sopenharmony_ci		vp = &vop2->vps[i];
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci		if (!vp->crtc.port)
234962306a36Sopenharmony_ci			continue;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci		plane = &vp->primary_plane->base;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci		ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL,
235462306a36Sopenharmony_ci						&vop2_crtc_funcs,
235562306a36Sopenharmony_ci						"video_port%d", vp->id);
235662306a36Sopenharmony_ci		if (ret) {
235762306a36Sopenharmony_ci			drm_err(vop2->drm, "crtc init for video_port%d failed\n", i);
235862306a36Sopenharmony_ci			return ret;
235962306a36Sopenharmony_ci		}
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs);
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		init_completion(&vp->dsp_hold_completion);
236462306a36Sopenharmony_ci	}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	/*
236762306a36Sopenharmony_ci	 * On the VOP2 it's very hard to change the number of layers on a VP
236862306a36Sopenharmony_ci	 * during runtime, so we distribute the layers equally over the used
236962306a36Sopenharmony_ci	 * VPs
237062306a36Sopenharmony_ci	 */
237162306a36Sopenharmony_ci	for (i = 0; i < vop2->data->nr_vps; i++) {
237262306a36Sopenharmony_ci		struct vop2_video_port *vp = &vop2->vps[i];
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci		if (vp->crtc.port)
237562306a36Sopenharmony_ci			vp->nlayers = NR_LAYERS / nvps;
237662306a36Sopenharmony_ci	}
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	return 0;
237962306a36Sopenharmony_ci}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_cistatic void vop2_destroy_crtcs(struct vop2 *vop2)
238262306a36Sopenharmony_ci{
238362306a36Sopenharmony_ci	struct drm_device *drm = vop2->drm;
238462306a36Sopenharmony_ci	struct list_head *crtc_list = &drm->mode_config.crtc_list;
238562306a36Sopenharmony_ci	struct list_head *plane_list = &drm->mode_config.plane_list;
238662306a36Sopenharmony_ci	struct drm_crtc *crtc, *tmpc;
238762306a36Sopenharmony_ci	struct drm_plane *plane, *tmpp;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	list_for_each_entry_safe(plane, tmpp, plane_list, head)
239062306a36Sopenharmony_ci		drm_plane_cleanup(plane);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	/*
239362306a36Sopenharmony_ci	 * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane()
239462306a36Sopenharmony_ci	 * references the CRTC.
239562306a36Sopenharmony_ci	 */
239662306a36Sopenharmony_ci	list_for_each_entry_safe(crtc, tmpc, crtc_list, head) {
239762306a36Sopenharmony_ci		of_node_put(crtc->port);
239862306a36Sopenharmony_ci		drm_crtc_cleanup(crtc);
239962306a36Sopenharmony_ci	}
240062306a36Sopenharmony_ci}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_cistatic int vop2_find_rgb_encoder(struct vop2 *vop2)
240362306a36Sopenharmony_ci{
240462306a36Sopenharmony_ci	struct device_node *node = vop2->dev->of_node;
240562306a36Sopenharmony_ci	struct device_node *endpoint;
240662306a36Sopenharmony_ci	int i;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	for (i = 0; i < vop2->data->nr_vps; i++) {
240962306a36Sopenharmony_ci		endpoint = of_graph_get_endpoint_by_regs(node, i,
241062306a36Sopenharmony_ci							 ROCKCHIP_VOP2_EP_RGB0);
241162306a36Sopenharmony_ci		if (!endpoint)
241262306a36Sopenharmony_ci			continue;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci		of_node_put(endpoint);
241562306a36Sopenharmony_ci		return i;
241662306a36Sopenharmony_ci	}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	return -ENOENT;
241962306a36Sopenharmony_ci}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_cistatic struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = {
242262306a36Sopenharmony_ci	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0),
242362306a36Sopenharmony_ci	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5),
242462306a36Sopenharmony_ci	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14),
242562306a36Sopenharmony_ci	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18),
242662306a36Sopenharmony_ci	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31),
242762306a36Sopenharmony_ci	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31),
242862306a36Sopenharmony_ci	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31),
242962306a36Sopenharmony_ci	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31),
243062306a36Sopenharmony_ci	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31),
243162306a36Sopenharmony_ci	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19),
243262306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15),
243362306a36Sopenharmony_ci	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31),
243462306a36Sopenharmony_ci	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8),
243562306a36Sopenharmony_ci	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9),
243662306a36Sopenharmony_ci	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11),
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	/* Scale */
243962306a36Sopenharmony_ci	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15),
244062306a36Sopenharmony_ci	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31),
244162306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15),
244262306a36Sopenharmony_ci	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13),
244362306a36Sopenharmony_ci	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3),
244462306a36Sopenharmony_ci	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28),
244562306a36Sopenharmony_ci	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29),
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	/* cluster regs */
244862306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1),
244962306a36Sopenharmony_ci	[VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0),
245062306a36Sopenharmony_ci	[VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7),
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	/* afbc regs */
245362306a36Sopenharmony_ci	[VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6),
245462306a36Sopenharmony_ci	[VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9),
245562306a36Sopenharmony_ci	[VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10),
245662306a36Sopenharmony_ci	[VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4),
245762306a36Sopenharmony_ci	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7),
245862306a36Sopenharmony_ci	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8),
245962306a36Sopenharmony_ci	[VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31),
246062306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31),
246162306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15),
246262306a36Sopenharmony_ci	[VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31),
246362306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31),
246462306a36Sopenharmony_ci	[VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31),
246562306a36Sopenharmony_ci	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31),
246662306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0),
246762306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1),
246862306a36Sopenharmony_ci	[VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2),
246962306a36Sopenharmony_ci	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3),
247062306a36Sopenharmony_ci	[VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff },
247162306a36Sopenharmony_ci	[VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff },
247262306a36Sopenharmony_ci	[VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff },
247362306a36Sopenharmony_ci	[VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff },
247462306a36Sopenharmony_ci	[VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff },
247562306a36Sopenharmony_ci	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
247662306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
247762306a36Sopenharmony_ci	[VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff },
247862306a36Sopenharmony_ci	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff },
247962306a36Sopenharmony_ci	[VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff },
248062306a36Sopenharmony_ci	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff },
248162306a36Sopenharmony_ci	[VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff },
248262306a36Sopenharmony_ci	[VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff },
248362306a36Sopenharmony_ci};
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_cistatic int vop2_cluster_init(struct vop2_win *win)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct vop2 *vop2 = win->vop2;
248862306a36Sopenharmony_ci	struct reg_field *cluster_regs;
248962306a36Sopenharmony_ci	int ret, i;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs),
249262306a36Sopenharmony_ci			       GFP_KERNEL);
249362306a36Sopenharmony_ci	if (!cluster_regs)
249462306a36Sopenharmony_ci		return -ENOMEM;
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++)
249762306a36Sopenharmony_ci		if (cluster_regs[i].reg != 0xffffffff)
249862306a36Sopenharmony_ci			cluster_regs[i].reg += win->offset;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
250162306a36Sopenharmony_ci					   cluster_regs,
250262306a36Sopenharmony_ci					   ARRAY_SIZE(vop2_cluster_regs));
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	kfree(cluster_regs);
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	return ret;
250762306a36Sopenharmony_ci};
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_cistatic struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = {
251062306a36Sopenharmony_ci	[VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0),
251162306a36Sopenharmony_ci	[VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5),
251262306a36Sopenharmony_ci	[VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12),
251362306a36Sopenharmony_ci	[VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14),
251462306a36Sopenharmony_ci	[VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16),
251562306a36Sopenharmony_ci	[VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31),
251662306a36Sopenharmony_ci	[VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31),
251762306a36Sopenharmony_ci	[VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28),
251862306a36Sopenharmony_ci	[VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31),
251962306a36Sopenharmony_ci	[VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31),
252062306a36Sopenharmony_ci	[VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17),
252162306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15),
252262306a36Sopenharmony_ci	[VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31),
252362306a36Sopenharmony_ci	[VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0),
252462306a36Sopenharmony_ci	[VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1),
252562306a36Sopenharmony_ci	[VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3),
252662306a36Sopenharmony_ci	[VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31),
252762306a36Sopenharmony_ci	[VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29),
252862306a36Sopenharmony_ci	[VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31),
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	/* Scale */
253162306a36Sopenharmony_ci	[VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15),
253262306a36Sopenharmony_ci	[VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31),
253362306a36Sopenharmony_ci	[VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15),
253462306a36Sopenharmony_ci	[VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31),
253562306a36Sopenharmony_ci	[VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1),
253662306a36Sopenharmony_ci	[VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3),
253762306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5),
253862306a36Sopenharmony_ci	[VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7),
253962306a36Sopenharmony_ci	[VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9),
254062306a36Sopenharmony_ci	[VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11),
254162306a36Sopenharmony_ci	[VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13),
254262306a36Sopenharmony_ci	[VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15),
254362306a36Sopenharmony_ci	[VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17),
254462306a36Sopenharmony_ci	[VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8),
254562306a36Sopenharmony_ci	[VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9),
254662306a36Sopenharmony_ci	[VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10),
254762306a36Sopenharmony_ci	[VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11),
254862306a36Sopenharmony_ci	[VOP2_WIN_XMIRROR] = { .reg = 0xffffffff },
254962306a36Sopenharmony_ci	[VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff },
255062306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff },
255162306a36Sopenharmony_ci	[VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff },
255262306a36Sopenharmony_ci	[VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff },
255362306a36Sopenharmony_ci	[VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff },
255462306a36Sopenharmony_ci	[VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff },
255562306a36Sopenharmony_ci	[VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff },
255662306a36Sopenharmony_ci	[VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff },
255762306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff },
255862306a36Sopenharmony_ci	[VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff },
255962306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff },
256062306a36Sopenharmony_ci	[VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff },
256162306a36Sopenharmony_ci	[VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff },
256262306a36Sopenharmony_ci	[VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff },
256362306a36Sopenharmony_ci	[VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff },
256462306a36Sopenharmony_ci	[VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff },
256562306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff },
256662306a36Sopenharmony_ci	[VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff },
256762306a36Sopenharmony_ci};
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_cistatic int vop2_esmart_init(struct vop2_win *win)
257062306a36Sopenharmony_ci{
257162306a36Sopenharmony_ci	struct vop2 *vop2 = win->vop2;
257262306a36Sopenharmony_ci	struct reg_field *esmart_regs;
257362306a36Sopenharmony_ci	int ret, i;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs),
257662306a36Sopenharmony_ci			      GFP_KERNEL);
257762306a36Sopenharmony_ci	if (!esmart_regs)
257862306a36Sopenharmony_ci		return -ENOMEM;
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++)
258162306a36Sopenharmony_ci		if (esmart_regs[i].reg != 0xffffffff)
258262306a36Sopenharmony_ci			esmart_regs[i].reg += win->offset;
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg,
258562306a36Sopenharmony_ci					   esmart_regs,
258662306a36Sopenharmony_ci					   ARRAY_SIZE(vop2_esmart_regs));
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	kfree(esmart_regs);
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	return ret;
259162306a36Sopenharmony_ci};
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_cistatic int vop2_win_init(struct vop2 *vop2)
259462306a36Sopenharmony_ci{
259562306a36Sopenharmony_ci	const struct vop2_data *vop2_data = vop2->data;
259662306a36Sopenharmony_ci	struct vop2_win *win;
259762306a36Sopenharmony_ci	int i, ret;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	for (i = 0; i < vop2_data->win_size; i++) {
260062306a36Sopenharmony_ci		const struct vop2_win_data *win_data = &vop2_data->win[i];
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci		win = &vop2->win[i];
260362306a36Sopenharmony_ci		win->data = win_data;
260462306a36Sopenharmony_ci		win->type = win_data->type;
260562306a36Sopenharmony_ci		win->offset = win_data->base;
260662306a36Sopenharmony_ci		win->win_id = i;
260762306a36Sopenharmony_ci		win->vop2 = vop2;
260862306a36Sopenharmony_ci		if (vop2_cluster_window(win))
260962306a36Sopenharmony_ci			ret = vop2_cluster_init(win);
261062306a36Sopenharmony_ci		else
261162306a36Sopenharmony_ci			ret = vop2_esmart_init(win);
261262306a36Sopenharmony_ci		if (ret)
261362306a36Sopenharmony_ci			return ret;
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	vop2->registered_num_wins = vop2_data->win_size;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	return 0;
261962306a36Sopenharmony_ci}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci/*
262262306a36Sopenharmony_ci * The window registers are only updated when config done is written.
262362306a36Sopenharmony_ci * Until that they read back the old value. As we read-modify-write
262462306a36Sopenharmony_ci * these registers mark them as non-volatile. This makes sure we read
262562306a36Sopenharmony_ci * the new values from the regmap register cache.
262662306a36Sopenharmony_ci */
262762306a36Sopenharmony_cistatic const struct regmap_range vop2_nonvolatile_range[] = {
262862306a36Sopenharmony_ci	regmap_reg_range(0x1000, 0x23ff),
262962306a36Sopenharmony_ci};
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_cistatic const struct regmap_access_table vop2_volatile_table = {
263262306a36Sopenharmony_ci	.no_ranges = vop2_nonvolatile_range,
263362306a36Sopenharmony_ci	.n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range),
263462306a36Sopenharmony_ci};
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_cistatic const struct regmap_config vop2_regmap_config = {
263762306a36Sopenharmony_ci	.reg_bits	= 32,
263862306a36Sopenharmony_ci	.val_bits	= 32,
263962306a36Sopenharmony_ci	.reg_stride	= 4,
264062306a36Sopenharmony_ci	.max_register	= 0x3000,
264162306a36Sopenharmony_ci	.name		= "vop2",
264262306a36Sopenharmony_ci	.volatile_table	= &vop2_volatile_table,
264362306a36Sopenharmony_ci	.cache_type	= REGCACHE_RBTREE,
264462306a36Sopenharmony_ci};
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_cistatic int vop2_bind(struct device *dev, struct device *master, void *data)
264762306a36Sopenharmony_ci{
264862306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
264962306a36Sopenharmony_ci	const struct vop2_data *vop2_data;
265062306a36Sopenharmony_ci	struct drm_device *drm = data;
265162306a36Sopenharmony_ci	struct vop2 *vop2;
265262306a36Sopenharmony_ci	struct resource *res;
265362306a36Sopenharmony_ci	size_t alloc_size;
265462306a36Sopenharmony_ci	int ret;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	vop2_data = of_device_get_match_data(dev);
265762306a36Sopenharmony_ci	if (!vop2_data)
265862306a36Sopenharmony_ci		return -ENODEV;
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci	/* Allocate vop2 struct and its vop2_win array */
266162306a36Sopenharmony_ci	alloc_size = struct_size(vop2, win, vop2_data->win_size);
266262306a36Sopenharmony_ci	vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
266362306a36Sopenharmony_ci	if (!vop2)
266462306a36Sopenharmony_ci		return -ENOMEM;
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	vop2->dev = dev;
266762306a36Sopenharmony_ci	vop2->data = vop2_data;
266862306a36Sopenharmony_ci	vop2->drm = drm;
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci	dev_set_drvdata(dev, vop2);
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop");
267362306a36Sopenharmony_ci	if (!res) {
267462306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to get vop2 register byname\n");
267562306a36Sopenharmony_ci		return -EINVAL;
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	vop2->regs = devm_ioremap_resource(dev, res);
267962306a36Sopenharmony_ci	if (IS_ERR(vop2->regs))
268062306a36Sopenharmony_ci		return PTR_ERR(vop2->regs);
268162306a36Sopenharmony_ci	vop2->len = resource_size(res);
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config);
268462306a36Sopenharmony_ci	if (IS_ERR(vop2->map))
268562306a36Sopenharmony_ci		return PTR_ERR(vop2->map);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	ret = vop2_win_init(vop2);
268862306a36Sopenharmony_ci	if (ret)
268962306a36Sopenharmony_ci		return ret;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma-lut");
269262306a36Sopenharmony_ci	if (res) {
269362306a36Sopenharmony_ci		vop2->lut_regs = devm_ioremap_resource(dev, res);
269462306a36Sopenharmony_ci		if (IS_ERR(vop2->lut_regs))
269562306a36Sopenharmony_ci			return PTR_ERR(vop2->lut_regs);
269662306a36Sopenharmony_ci	}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	vop2->hclk = devm_clk_get(vop2->dev, "hclk");
270162306a36Sopenharmony_ci	if (IS_ERR(vop2->hclk)) {
270262306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to get hclk source\n");
270362306a36Sopenharmony_ci		return PTR_ERR(vop2->hclk);
270462306a36Sopenharmony_ci	}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	vop2->aclk = devm_clk_get(vop2->dev, "aclk");
270762306a36Sopenharmony_ci	if (IS_ERR(vop2->aclk)) {
270862306a36Sopenharmony_ci		drm_err(vop2->drm, "failed to get aclk source\n");
270962306a36Sopenharmony_ci		return PTR_ERR(vop2->aclk);
271062306a36Sopenharmony_ci	}
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	vop2->irq = platform_get_irq(pdev, 0);
271362306a36Sopenharmony_ci	if (vop2->irq < 0) {
271462306a36Sopenharmony_ci		drm_err(vop2->drm, "cannot find irq for vop2\n");
271562306a36Sopenharmony_ci		return vop2->irq;
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	mutex_init(&vop2->vop2_lock);
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
272162306a36Sopenharmony_ci	if (ret)
272262306a36Sopenharmony_ci		return ret;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	ret = vop2_create_crtcs(vop2);
272562306a36Sopenharmony_ci	if (ret)
272662306a36Sopenharmony_ci		return ret;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	ret = vop2_find_rgb_encoder(vop2);
272962306a36Sopenharmony_ci	if (ret >= 0) {
273062306a36Sopenharmony_ci		vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
273162306a36Sopenharmony_ci					      vop2->drm, ret);
273262306a36Sopenharmony_ci		if (IS_ERR(vop2->rgb)) {
273362306a36Sopenharmony_ci			if (PTR_ERR(vop2->rgb) == -EPROBE_DEFER) {
273462306a36Sopenharmony_ci				ret = PTR_ERR(vop2->rgb);
273562306a36Sopenharmony_ci				goto err_crtcs;
273662306a36Sopenharmony_ci			}
273762306a36Sopenharmony_ci			vop2->rgb = NULL;
273862306a36Sopenharmony_ci		}
273962306a36Sopenharmony_ci	}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	rockchip_drm_dma_init_device(vop2->drm, vop2->dev);
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	return 0;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_cierr_crtcs:
274862306a36Sopenharmony_ci	vop2_destroy_crtcs(vop2);
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	return ret;
275162306a36Sopenharmony_ci}
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_cistatic void vop2_unbind(struct device *dev, struct device *master, void *data)
275462306a36Sopenharmony_ci{
275562306a36Sopenharmony_ci	struct vop2 *vop2 = dev_get_drvdata(dev);
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci	pm_runtime_disable(dev);
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	if (vop2->rgb)
276062306a36Sopenharmony_ci		rockchip_rgb_fini(vop2->rgb);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	vop2_destroy_crtcs(vop2);
276362306a36Sopenharmony_ci}
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ciconst struct component_ops vop2_component_ops = {
276662306a36Sopenharmony_ci	.bind = vop2_bind,
276762306a36Sopenharmony_ci	.unbind = vop2_unbind,
276862306a36Sopenharmony_ci};
276962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vop2_component_ops);
2770