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