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