xref: /linux/drivers/gpu/drm/verisilicon/vs_bridge.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
4  */
5 
6 #include <linux/of.h>
7 #include <linux/regmap.h>
8 
9 #include <uapi/linux/media-bus-format.h>
10 
11 #include <drm/drm_atomic.h>
12 #include <drm/drm_atomic_helper.h>
13 #include <drm/drm_bridge.h>
14 #include <drm/drm_bridge_connector.h>
15 #include <drm/drm_connector.h>
16 #include <drm/drm_encoder.h>
17 #include <drm/drm_of.h>
18 #include <drm/drm_print.h>
19 #include <drm/drm_simple_kms_helper.h>
20 
21 #include "vs_bridge.h"
22 #include "vs_bridge_regs.h"
23 #include "vs_crtc.h"
24 #include "vs_dc.h"
25 
26 static int vs_bridge_attach(struct drm_bridge *bridge,
27 			    struct drm_encoder *encoder,
28 			    enum drm_bridge_attach_flags flags)
29 {
30 	struct vs_bridge *vbridge = drm_bridge_to_vs_bridge(bridge);
31 
32 	return drm_bridge_attach(encoder, vbridge->next_bridge,
33 				 bridge, flags);
34 }
35 
36 struct vsdc_dp_format {
37 	u32 linux_fmt;
38 	bool is_yuv;
39 	u32 vsdc_fmt;
40 };
41 
42 static struct vsdc_dp_format vsdc_dp_supported_fmts[] = {
43 	/* default to RGB888 */
44 	{ MEDIA_BUS_FMT_FIXED, false, VSDC_DISP_DP_CONFIG_FMT_RGB888 },
45 	{ MEDIA_BUS_FMT_RGB888_1X24, false, VSDC_DISP_DP_CONFIG_FMT_RGB888 },
46 	{ MEDIA_BUS_FMT_RGB565_1X16, false, VSDC_DISP_DP_CONFIG_FMT_RGB565 },
47 	{ MEDIA_BUS_FMT_RGB666_1X18, false, VSDC_DISP_DP_CONFIG_FMT_RGB666 },
48 	{ MEDIA_BUS_FMT_RGB101010_1X30,
49 	  false, VSDC_DISP_DP_CONFIG_FMT_RGB101010 },
50 	{ MEDIA_BUS_FMT_UYVY8_1X16, true, VSDC_DISP_DP_CONFIG_YUV_FMT_UYVY8 },
51 	{ MEDIA_BUS_FMT_UYVY10_1X20, true, VSDC_DISP_DP_CONFIG_YUV_FMT_UYVY10 },
52 	{ MEDIA_BUS_FMT_YUV8_1X24, true, VSDC_DISP_DP_CONFIG_YUV_FMT_YUV8 },
53 	{ MEDIA_BUS_FMT_YUV10_1X30, true, VSDC_DISP_DP_CONFIG_YUV_FMT_YUV10 },
54 	{ MEDIA_BUS_FMT_UYYVYY8_0_5X24,
55 	  true, VSDC_DISP_DP_CONFIG_YUV_FMT_UYYVYY8 },
56 	{ MEDIA_BUS_FMT_UYYVYY10_0_5X30,
57 	  true, VSDC_DISP_DP_CONFIG_YUV_FMT_UYYVYY10 },
58 };
59 
60 static u32 *vs_bridge_atomic_get_output_bus_fmts_dpi(struct drm_bridge *bridge,
61 					struct drm_bridge_state *bridge_state,
62 					struct drm_crtc_state *crtc_state,
63 					struct drm_connector_state *conn_state,
64 					unsigned int *num_output_fmts)
65 {
66 	u32 *output_fmts;
67 
68 	*num_output_fmts = 2;
69 
70 	output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
71 			      GFP_KERNEL);
72 	if (!output_fmts)
73 		return NULL;
74 
75 	/* TODO: support more DPI output formats */
76 	output_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
77 	output_fmts[1] = MEDIA_BUS_FMT_FIXED;
78 
79 	return output_fmts;
80 }
81 
82 static u32 *vs_bridge_atomic_get_output_bus_fmts_dp(struct drm_bridge *bridge,
83 					struct drm_bridge_state *bridge_state,
84 					struct drm_crtc_state *crtc_state,
85 					struct drm_connector_state *conn_state,
86 					unsigned int *num_output_fmts)
87 {
88 	u32 *output_fmts;
89 	unsigned int i;
90 
91 	*num_output_fmts = ARRAY_SIZE(vsdc_dp_supported_fmts);
92 
93 	output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
94 			      GFP_KERNEL);
95 	if (!output_fmts)
96 		return NULL;
97 
98 	for (i = 0; i < *num_output_fmts; i++)
99 		output_fmts[i] = vsdc_dp_supported_fmts[i].linux_fmt;
100 
101 	return output_fmts;
102 }
103 
104 static bool vs_bridge_out_dp_fmt_supported(u32 out_fmt)
105 {
106 	unsigned int i;
107 
108 	for (i = 0; i < ARRAY_SIZE(vsdc_dp_supported_fmts); i++)
109 		if (vsdc_dp_supported_fmts[i].linux_fmt == out_fmt)
110 			return true;
111 
112 	return false;
113 }
114 
115 static u32 *vs_bridge_atomic_get_input_bus_fmts_dp(struct drm_bridge *bridge,
116 					struct drm_bridge_state *bridge_state,
117 					struct drm_crtc_state *crtc_state,
118 					struct drm_connector_state *conn_state,
119 					u32 output_fmt,
120 					unsigned int *num_input_fmts)
121 {
122 	if (!vs_bridge_out_dp_fmt_supported(output_fmt)) {
123 		*num_input_fmts = 0;
124 		return NULL;
125 	}
126 
127 	return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state,
128 							  crtc_state,
129 							  conn_state,
130 							  output_fmt,
131 							  num_input_fmts);
132 }
133 
134 static int vs_bridge_atomic_check_dp(struct drm_bridge *bridge,
135 				     struct drm_bridge_state *bridge_state,
136 				     struct drm_crtc_state *crtc_state,
137 				     struct drm_connector_state *conn_state)
138 {
139 	if (!vs_bridge_out_dp_fmt_supported(bridge_state->output_bus_cfg.format))
140 		return -EINVAL;
141 
142 	return 0;
143 }
144 
145 static void vs_bridge_enable_common(struct vs_crtc *crtc,
146 				    struct drm_bridge_state *br_state)
147 {
148 	struct vs_dc *dc = crtc->dc;
149 	unsigned int output = crtc->id;
150 
151 	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
152 			  VSDC_DISP_PANEL_CONFIG_DAT_POL);
153 	regmap_assign_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
154 			   VSDC_DISP_PANEL_CONFIG_DE_POL,
155 			   br_state->output_bus_cfg.flags &
156 			   DRM_BUS_FLAG_DE_LOW);
157 	regmap_assign_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
158 			   VSDC_DISP_PANEL_CONFIG_CLK_POL,
159 			   br_state->output_bus_cfg.flags &
160 			   DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
161 	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
162 			VSDC_DISP_PANEL_CONFIG_DE_EN |
163 			VSDC_DISP_PANEL_CONFIG_DAT_EN |
164 			VSDC_DISP_PANEL_CONFIG_CLK_EN);
165 	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
166 			VSDC_DISP_PANEL_CONFIG_RUNNING);
167 	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
168 			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
169 	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
170 			VSDC_DISP_PANEL_START_RUNNING(output));
171 
172 	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
173 			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
174 }
175 
176 static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
177 					struct drm_atomic_state *state)
178 {
179 	struct vs_bridge *vbridge = drm_bridge_to_vs_bridge(bridge);
180 	struct drm_bridge_state *br_state =
181 		drm_atomic_get_new_bridge_state(state, bridge);
182 	struct vs_crtc *crtc = vbridge->crtc;
183 	struct vs_dc *dc = crtc->dc;
184 	unsigned int output = crtc->id;
185 
186 	regmap_clear_bits(dc->regs, VSDC_DISP_DP_CONFIG(output),
187 			  VSDC_DISP_DP_CONFIG_DP_EN);
188 	regmap_write(dc->regs, VSDC_DISP_DPI_CONFIG(output),
189 		     VSDC_DISP_DPI_CONFIG_FMT_RGB888);
190 
191 	vs_bridge_enable_common(crtc, br_state);
192 }
193 
194 static void vs_bridge_atomic_enable_dp(struct drm_bridge *bridge,
195 					struct drm_atomic_state *state)
196 {
197 	struct vs_bridge *vbridge = drm_bridge_to_vs_bridge(bridge);
198 	struct drm_bridge_state *br_state =
199 		drm_atomic_get_new_bridge_state(state, bridge);
200 	struct vs_crtc *crtc = vbridge->crtc;
201 	struct vs_dc *dc = crtc->dc;
202 	unsigned int output = crtc->id;
203 	u32 dp_fmt;
204 	unsigned int i;
205 
206 	for (i = 0; i < ARRAY_SIZE(vsdc_dp_supported_fmts); i++) {
207 		if (vsdc_dp_supported_fmts[i].linux_fmt ==
208 		    br_state->output_bus_cfg.format)
209 			break;
210 	}
211 	if (WARN_ON_ONCE(i == ARRAY_SIZE(vsdc_dp_supported_fmts)))
212 		return;
213 	dp_fmt = vsdc_dp_supported_fmts[i].vsdc_fmt;
214 	dp_fmt |= VSDC_DISP_DP_CONFIG_DP_EN;
215 	regmap_write(dc->regs, VSDC_DISP_DP_CONFIG(output), dp_fmt);
216 	regmap_assign_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
217 			   VSDC_DISP_PANEL_CONFIG_YUV,
218 			   vsdc_dp_supported_fmts[i].is_yuv);
219 
220 	vs_bridge_enable_common(crtc, br_state);
221 }
222 
223 static void vs_bridge_atomic_disable(struct drm_bridge *bridge,
224 				     struct drm_atomic_state *state)
225 {
226 	struct vs_bridge *vbridge = drm_bridge_to_vs_bridge(bridge);
227 	struct vs_crtc *crtc = vbridge->crtc;
228 	struct vs_dc *dc = crtc->dc;
229 	unsigned int output = crtc->id;
230 
231 	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
232 			  VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
233 			  VSDC_DISP_PANEL_START_RUNNING(output));
234 	regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
235 			  VSDC_DISP_PANEL_CONFIG_RUNNING);
236 
237 	regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc->id),
238 			VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
239 }
240 
241 static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
242 	.attach = vs_bridge_attach,
243 	.atomic_enable = vs_bridge_atomic_enable_dpi,
244 	.atomic_disable = vs_bridge_atomic_disable,
245 	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
246 	.atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dpi,
247 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
248 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
249 	.atomic_reset = drm_atomic_helper_bridge_reset,
250 };
251 
252 static const struct drm_bridge_funcs vs_dp_bridge_funcs = {
253 	.attach = vs_bridge_attach,
254 	.atomic_enable = vs_bridge_atomic_enable_dp,
255 	.atomic_disable = vs_bridge_atomic_disable,
256 	.atomic_check = vs_bridge_atomic_check_dp,
257 	.atomic_get_input_bus_fmts = vs_bridge_atomic_get_input_bus_fmts_dp,
258 	.atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dp,
259 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
260 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
261 	.atomic_reset = drm_atomic_helper_bridge_reset,
262 };
263 
264 static int vs_bridge_detect_output_interface(struct device_node *of_node,
265 					     unsigned int output)
266 {
267 	int ret;
268 	struct device_node *remote;
269 
270 	remote = of_graph_get_remote_node(of_node, output,
271 					  VSDC_OUTPUT_INTERFACE_DPI);
272 	if (remote) {
273 		ret = VSDC_OUTPUT_INTERFACE_DPI;
274 	} else {
275 		remote = of_graph_get_remote_node(of_node, output,
276 						  VSDC_OUTPUT_INTERFACE_DP);
277 		if (remote)
278 			ret = VSDC_OUTPUT_INTERFACE_DP;
279 		else
280 			ret = -ENODEV;
281 	}
282 
283 	if (remote)
284 		of_node_put(remote);
285 
286 	return ret;
287 }
288 
289 struct vs_bridge *vs_bridge_init(struct drm_device *drm_dev,
290 				 struct vs_crtc *crtc)
291 {
292 	unsigned int output = crtc->id;
293 	struct vs_bridge *bridge;
294 	struct drm_bridge *next;
295 	enum vs_bridge_output_interface intf;
296 	const struct drm_bridge_funcs *bridge_funcs;
297 	int ret, enctype;
298 
299 	intf = vs_bridge_detect_output_interface(drm_dev->dev->of_node,
300 						 output);
301 	if (intf == -ENODEV) {
302 		drm_dbg(drm_dev, "Skipping output %u\n", output);
303 		return NULL;
304 	}
305 
306 	next = devm_drm_of_get_bridge(drm_dev->dev, drm_dev->dev->of_node,
307 				      output, intf);
308 	if (IS_ERR(next)) {
309 		ret = PTR_ERR(next);
310 		if (ret != -EPROBE_DEFER)
311 			drm_err(drm_dev,
312 				"Cannot get downstream bridge of output %u\n",
313 				output);
314 		return ERR_PTR(ret);
315 	}
316 
317 	if (intf == VSDC_OUTPUT_INTERFACE_DPI)
318 		bridge_funcs = &vs_dpi_bridge_funcs;
319 	else
320 		bridge_funcs = &vs_dp_bridge_funcs;
321 
322 	bridge = devm_drm_bridge_alloc(drm_dev->dev, struct vs_bridge, base,
323 				       bridge_funcs);
324 	if (IS_ERR(bridge))
325 		return ERR_PTR(PTR_ERR(bridge));
326 
327 	bridge->crtc = crtc;
328 	bridge->intf = intf;
329 	bridge->next_bridge = next;
330 
331 	if (intf == VSDC_OUTPUT_INTERFACE_DPI)
332 		enctype = DRM_MODE_ENCODER_DPI;
333 	else
334 		enctype = DRM_MODE_ENCODER_NONE;
335 
336 	bridge->enc = drmm_plain_encoder_alloc(drm_dev, NULL, enctype, NULL);
337 	if (IS_ERR(bridge->enc)) {
338 		drm_err(drm_dev,
339 			"Cannot initialize encoder for output %u\n", output);
340 		ret = PTR_ERR(bridge->enc);
341 		return ERR_PTR(ret);
342 	}
343 
344 	bridge->enc->possible_crtcs = drm_crtc_mask(&crtc->base);
345 
346 	ret = devm_drm_bridge_add(drm_dev->dev, &bridge->base);
347 	if (ret) {
348 		drm_err(drm_dev,
349 			"Cannot add bridge for output %u\n", output);
350 		return ERR_PTR(ret);
351 	}
352 
353 	ret = drm_bridge_attach(bridge->enc, &bridge->base, NULL,
354 				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
355 	if (ret) {
356 		drm_err(drm_dev,
357 			"Cannot attach bridge for output %u\n", output);
358 		return ERR_PTR(ret);
359 	}
360 
361 	bridge->conn = drm_bridge_connector_init(drm_dev, bridge->enc);
362 	if (IS_ERR(bridge->conn)) {
363 		drm_err(drm_dev,
364 			"Cannot create connector for output %u\n", output);
365 		ret = PTR_ERR(bridge->conn);
366 		return ERR_PTR(ret);
367 	}
368 	drm_connector_attach_encoder(bridge->conn, bridge->enc);
369 
370 	return bridge;
371 }
372