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