1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) Rockchip Electronics Co.Ltd
4  * Author: Andy Yan <andy.yan@rock-chips.com>
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/component.h>
9 #include <linux/mod_devicetable.h>
10 #include <linux/platform_device.h>
11 #include <linux/of.h>
12 #include <drm/drm_fourcc.h>
13 #include <drm/drm_plane.h>
14 #include <drm/drm_print.h>
15 
16 #include "rockchip_drm_vop2.h"
17 
18 static const uint32_t formats_win_full_10bit[] = {
19 	DRM_FORMAT_XRGB8888,
20 	DRM_FORMAT_ARGB8888,
21 	DRM_FORMAT_XBGR8888,
22 	DRM_FORMAT_ABGR8888,
23 	DRM_FORMAT_RGB888,
24 	DRM_FORMAT_BGR888,
25 	DRM_FORMAT_RGB565,
26 	DRM_FORMAT_BGR565,
27 	DRM_FORMAT_NV12,
28 	DRM_FORMAT_NV16,
29 	DRM_FORMAT_NV24,
30 };
31 
32 static const uint32_t formats_win_full_10bit_yuyv[] = {
33 	DRM_FORMAT_XRGB8888,
34 	DRM_FORMAT_ARGB8888,
35 	DRM_FORMAT_XBGR8888,
36 	DRM_FORMAT_ABGR8888,
37 	DRM_FORMAT_RGB888,
38 	DRM_FORMAT_BGR888,
39 	DRM_FORMAT_RGB565,
40 	DRM_FORMAT_BGR565,
41 	DRM_FORMAT_NV12,
42 	DRM_FORMAT_NV16,
43 	DRM_FORMAT_NV24,
44 	DRM_FORMAT_YVYU,
45 	DRM_FORMAT_VYUY,
46 };
47 
48 static const uint32_t formats_win_lite[] = {
49 	DRM_FORMAT_XRGB8888,
50 	DRM_FORMAT_ARGB8888,
51 	DRM_FORMAT_XBGR8888,
52 	DRM_FORMAT_ABGR8888,
53 	DRM_FORMAT_RGB888,
54 	DRM_FORMAT_BGR888,
55 	DRM_FORMAT_RGB565,
56 	DRM_FORMAT_BGR565,
57 };
58 
59 static const uint64_t format_modifiers[] = {
60 	DRM_FORMAT_MOD_LINEAR,
61 	DRM_FORMAT_MOD_INVALID,
62 };
63 
64 static const uint64_t format_modifiers_afbc[] = {
65 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
66 
67 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
68 				AFBC_FORMAT_MOD_SPARSE),
69 
70 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
71 				AFBC_FORMAT_MOD_YTR),
72 
73 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
74 				AFBC_FORMAT_MOD_CBR),
75 
76 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
77 				AFBC_FORMAT_MOD_YTR |
78 				AFBC_FORMAT_MOD_SPARSE),
79 
80 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
81 				AFBC_FORMAT_MOD_CBR |
82 				AFBC_FORMAT_MOD_SPARSE),
83 
84 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
85 				AFBC_FORMAT_MOD_YTR |
86 				AFBC_FORMAT_MOD_CBR),
87 
88 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
89 				AFBC_FORMAT_MOD_YTR |
90 				AFBC_FORMAT_MOD_CBR |
91 				AFBC_FORMAT_MOD_SPARSE),
92 
93 	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
94 	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
95 				AFBC_FORMAT_MOD_YTR |
96 				AFBC_FORMAT_MOD_SPARSE |
97 				AFBC_FORMAT_MOD_SPLIT),
98 	DRM_FORMAT_MOD_INVALID,
99 };
100 
101 static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
102 	{
103 		.id = 0,
104 		.feature = VOP_FEATURE_OUTPUT_10BIT,
105 		.gamma_lut_len = 1024,
106 		.cubic_lut_len = 9 * 9 * 9,
107 		.max_output = { 4096, 2304 },
108 		.pre_scan_max_dly = { 69, 53, 53, 42 },
109 		.offset = 0xc00,
110 	}, {
111 		.id = 1,
112 		.gamma_lut_len = 1024,
113 		.max_output = { 2048, 1536 },
114 		.pre_scan_max_dly = { 40, 40, 40, 40 },
115 		.offset = 0xd00,
116 	}, {
117 		.id = 2,
118 		.gamma_lut_len = 1024,
119 		.max_output = { 1920, 1080 },
120 		.pre_scan_max_dly = { 40, 40, 40, 40 },
121 		.offset = 0xe00,
122 	},
123 };
124 
125 /*
126  * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
127  * Every cluster can work as 4K win or split into two win.
128  * All win in cluster support AFBCD.
129  *
130  * Every esmart win and smart win support 4 Multi-region.
131  *
132  * Scale filter mode:
133  *
134  * * Cluster:  bicubic for horizontal scale up, others use bilinear
135  * * ESmart:
136  *    * nearest-neighbor/bilinear/bicubic for scale up
137  *    * nearest-neighbor/bilinear/average for scale down
138  *
139  *
140  * @TODO describe the wind like cpu-map dt nodes;
141  */
142 static const struct vop2_win_data rk3568_vop_win_data[] = {
143 	{
144 		.name = "Smart0-win0",
145 		.phys_id = ROCKCHIP_VOP2_SMART0,
146 		.base = 0x1c00,
147 		.formats = formats_win_lite,
148 		.nformats = ARRAY_SIZE(formats_win_lite),
149 		.format_modifiers = format_modifiers,
150 		.layer_sel_id = 3,
151 		.supported_rotations = DRM_MODE_REFLECT_Y,
152 		.type = DRM_PLANE_TYPE_PRIMARY,
153 		.max_upscale_factor = 8,
154 		.max_downscale_factor = 8,
155 		.dly = { 20, 47, 41 },
156 	}, {
157 		.name = "Smart1-win0",
158 		.phys_id = ROCKCHIP_VOP2_SMART1,
159 		.formats = formats_win_lite,
160 		.nformats = ARRAY_SIZE(formats_win_lite),
161 		.format_modifiers = format_modifiers,
162 		.base = 0x1e00,
163 		.layer_sel_id = 7,
164 		.supported_rotations = DRM_MODE_REFLECT_Y,
165 		.type = DRM_PLANE_TYPE_PRIMARY,
166 		.max_upscale_factor = 8,
167 		.max_downscale_factor = 8,
168 		.dly = { 20, 47, 41 },
169 	}, {
170 		.name = "Esmart1-win0",
171 		.phys_id = ROCKCHIP_VOP2_ESMART1,
172 		.formats = formats_win_full_10bit_yuyv,
173 		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
174 		.format_modifiers = format_modifiers,
175 		.base = 0x1a00,
176 		.layer_sel_id = 6,
177 		.supported_rotations = DRM_MODE_REFLECT_Y,
178 		.type = DRM_PLANE_TYPE_PRIMARY,
179 		.max_upscale_factor = 8,
180 		.max_downscale_factor = 8,
181 		.dly = { 20, 47, 41 },
182 	}, {
183 		.name = "Esmart0-win0",
184 		.phys_id = ROCKCHIP_VOP2_ESMART0,
185 		.formats = formats_win_full_10bit_yuyv,
186 		.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
187 		.format_modifiers = format_modifiers,
188 		.base = 0x1800,
189 		.layer_sel_id = 2,
190 		.supported_rotations = DRM_MODE_REFLECT_Y,
191 		.type = DRM_PLANE_TYPE_PRIMARY,
192 		.max_upscale_factor = 8,
193 		.max_downscale_factor = 8,
194 		.dly = { 20, 47, 41 },
195 	}, {
196 		.name = "Cluster0-win0",
197 		.phys_id = ROCKCHIP_VOP2_CLUSTER0,
198 		.base = 0x1000,
199 		.formats = formats_win_full_10bit,
200 		.nformats = ARRAY_SIZE(formats_win_full_10bit),
201 		.format_modifiers = format_modifiers_afbc,
202 		.layer_sel_id = 0,
203 		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
204 					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
205 		.max_upscale_factor = 4,
206 		.max_downscale_factor = 4,
207 		.dly = { 0, 27, 21 },
208 		.type = DRM_PLANE_TYPE_OVERLAY,
209 		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
210 	}, {
211 		.name = "Cluster1-win0",
212 		.phys_id = ROCKCHIP_VOP2_CLUSTER1,
213 		.base = 0x1200,
214 		.formats = formats_win_full_10bit,
215 		.nformats = ARRAY_SIZE(formats_win_full_10bit),
216 		.format_modifiers = format_modifiers_afbc,
217 		.layer_sel_id = 1,
218 		.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
219 					DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
220 		.type = DRM_PLANE_TYPE_OVERLAY,
221 		.max_upscale_factor = 4,
222 		.max_downscale_factor = 4,
223 		.dly = { 0, 27, 21 },
224 		.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
225 	},
226 };
227 
228 static const struct vop2_data rk3566_vop = {
229 	.nr_vps = 3,
230 	.max_input = { 4096, 2304 },
231 	.max_output = { 4096, 2304 },
232 	.vp = rk3568_vop_video_ports,
233 	.win = rk3568_vop_win_data,
234 	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
235 	.soc_id = 3566,
236 };
237 
238 static const struct vop2_data rk3568_vop = {
239 	.nr_vps = 3,
240 	.max_input = { 4096, 2304 },
241 	.max_output = { 4096, 2304 },
242 	.vp = rk3568_vop_video_ports,
243 	.win = rk3568_vop_win_data,
244 	.win_size = ARRAY_SIZE(rk3568_vop_win_data),
245 	.soc_id = 3568,
246 };
247 
248 static const struct of_device_id vop2_dt_match[] = {
249 	{
250 		.compatible = "rockchip,rk3566-vop",
251 		.data = &rk3566_vop,
252 	}, {
253 		.compatible = "rockchip,rk3568-vop",
254 		.data = &rk3568_vop,
255 	}, {
256 	},
257 };
258 MODULE_DEVICE_TABLE(of, vop2_dt_match);
259 
260 static int vop2_probe(struct platform_device *pdev)
261 {
262 	struct device *dev = &pdev->dev;
263 
264 	return component_add(dev, &vop2_component_ops);
265 }
266 
267 static int vop2_remove(struct platform_device *pdev)
268 {
269 	component_del(&pdev->dev, &vop2_component_ops);
270 
271 	return 0;
272 }
273 
274 struct platform_driver vop2_platform_driver = {
275 	.probe = vop2_probe,
276 	.remove = vop2_remove,
277 	.driver = {
278 		.name = "rockchip-vop2",
279 		.of_match_table = of_match_ptr(vop2_dt_match),
280 	},
281 };
282