17246e092SAradhya Bhatia // SPDX-License-Identifier: GPL-2.0-or-later 27246e092SAradhya Bhatia /* 37246e092SAradhya Bhatia * Copyright (C) 2025 - Texas Instruments Incorporated 47246e092SAradhya Bhatia * 57246e092SAradhya Bhatia * Aradhya Bhatia <a-bhatia1@ti.com> 67246e092SAradhya Bhatia */ 77246e092SAradhya Bhatia 87246e092SAradhya Bhatia #include <linux/clk.h> 97246e092SAradhya Bhatia #include <linux/of.h> 107246e092SAradhya Bhatia #include <linux/of_graph.h> 117246e092SAradhya Bhatia #include <linux/mfd/syscon.h> 127246e092SAradhya Bhatia #include <linux/media-bus-format.h> 137246e092SAradhya Bhatia #include <linux/regmap.h> 147246e092SAradhya Bhatia 157246e092SAradhya Bhatia #include <drm/drm_atomic_helper.h> 167246e092SAradhya Bhatia #include <drm/drm_bridge.h> 177246e092SAradhya Bhatia #include <drm/drm_of.h> 187246e092SAradhya Bhatia 197246e092SAradhya Bhatia #include "tidss_dispc.h" 207246e092SAradhya Bhatia #include "tidss_dispc_regs.h" 217246e092SAradhya Bhatia #include "tidss_oldi.h" 227246e092SAradhya Bhatia 237246e092SAradhya Bhatia struct tidss_oldi { 247246e092SAradhya Bhatia struct tidss_device *tidss; 257246e092SAradhya Bhatia struct device *dev; 267246e092SAradhya Bhatia 277246e092SAradhya Bhatia struct drm_bridge bridge; 287246e092SAradhya Bhatia struct drm_bridge *next_bridge; 297246e092SAradhya Bhatia 307246e092SAradhya Bhatia enum tidss_oldi_link_type link_type; 317246e092SAradhya Bhatia const struct oldi_bus_format *bus_format; 327246e092SAradhya Bhatia u32 oldi_instance; 337246e092SAradhya Bhatia int companion_instance; /* -1 when OLDI TX operates in Single-Link */ 347246e092SAradhya Bhatia u32 parent_vp; 357246e092SAradhya Bhatia 367246e092SAradhya Bhatia struct clk *serial; 377246e092SAradhya Bhatia struct regmap *io_ctrl; 387246e092SAradhya Bhatia }; 397246e092SAradhya Bhatia 407246e092SAradhya Bhatia struct oldi_bus_format { 417246e092SAradhya Bhatia u32 bus_fmt; 427246e092SAradhya Bhatia u32 data_width; 437246e092SAradhya Bhatia enum oldi_mode_reg_val oldi_mode_reg_val; 447246e092SAradhya Bhatia u32 input_bus_fmt; 457246e092SAradhya Bhatia }; 467246e092SAradhya Bhatia 477246e092SAradhya Bhatia static const struct oldi_bus_format oldi_bus_formats[] = { 487246e092SAradhya Bhatia { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, SPWG_18, MEDIA_BUS_FMT_RGB666_1X18 }, 497246e092SAradhya Bhatia { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, SPWG_24, MEDIA_BUS_FMT_RGB888_1X24 }, 507246e092SAradhya Bhatia { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, JEIDA_24, MEDIA_BUS_FMT_RGB888_1X24 }, 517246e092SAradhya Bhatia }; 527246e092SAradhya Bhatia 537246e092SAradhya Bhatia #define OLDI_IDLE_CLK_HZ 25000000 /*25 MHz */ 547246e092SAradhya Bhatia 557246e092SAradhya Bhatia static inline struct tidss_oldi * 567246e092SAradhya Bhatia drm_bridge_to_tidss_oldi(struct drm_bridge *bridge) 577246e092SAradhya Bhatia { 587246e092SAradhya Bhatia return container_of(bridge, struct tidss_oldi, bridge); 597246e092SAradhya Bhatia } 607246e092SAradhya Bhatia 617246e092SAradhya Bhatia static int tidss_oldi_bridge_attach(struct drm_bridge *bridge, 627246e092SAradhya Bhatia struct drm_encoder *encoder, 637246e092SAradhya Bhatia enum drm_bridge_attach_flags flags) 647246e092SAradhya Bhatia { 657246e092SAradhya Bhatia struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge); 667246e092SAradhya Bhatia 677246e092SAradhya Bhatia if (!oldi->next_bridge) { 687246e092SAradhya Bhatia dev_err(oldi->dev, 697246e092SAradhya Bhatia "%s: OLDI%u Failure attach next bridge\n", 707246e092SAradhya Bhatia __func__, oldi->oldi_instance); 717246e092SAradhya Bhatia return -ENODEV; 727246e092SAradhya Bhatia } 737246e092SAradhya Bhatia 747246e092SAradhya Bhatia if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { 757246e092SAradhya Bhatia dev_err(oldi->dev, 767246e092SAradhya Bhatia "%s: OLDI%u DRM_BRIDGE_ATTACH_NO_CONNECTOR is mandatory.\n", 777246e092SAradhya Bhatia __func__, oldi->oldi_instance); 787246e092SAradhya Bhatia return -EINVAL; 797246e092SAradhya Bhatia } 807246e092SAradhya Bhatia 817246e092SAradhya Bhatia return drm_bridge_attach(encoder, oldi->next_bridge, bridge, flags); 827246e092SAradhya Bhatia } 837246e092SAradhya Bhatia 847246e092SAradhya Bhatia static int 857246e092SAradhya Bhatia tidss_oldi_set_serial_clk(struct tidss_oldi *oldi, unsigned long rate) 867246e092SAradhya Bhatia { 877246e092SAradhya Bhatia unsigned long new_rate; 887246e092SAradhya Bhatia int ret; 897246e092SAradhya Bhatia 907246e092SAradhya Bhatia ret = clk_set_rate(oldi->serial, rate); 917246e092SAradhya Bhatia if (ret) { 927246e092SAradhya Bhatia dev_err(oldi->dev, 937246e092SAradhya Bhatia "OLDI%u: failed to set serial clk rate to %lu Hz\n", 947246e092SAradhya Bhatia oldi->oldi_instance, rate); 957246e092SAradhya Bhatia return ret; 967246e092SAradhya Bhatia } 977246e092SAradhya Bhatia 987246e092SAradhya Bhatia new_rate = clk_get_rate(oldi->serial); 997246e092SAradhya Bhatia 1007246e092SAradhya Bhatia if (dispc_pclk_diff(rate, new_rate) > 5) 1017246e092SAradhya Bhatia dev_warn(oldi->dev, 1027246e092SAradhya Bhatia "OLDI%u Clock rate %lu differs over 5%% from requested %lu\n", 1037246e092SAradhya Bhatia oldi->oldi_instance, new_rate, rate); 1047246e092SAradhya Bhatia 1057246e092SAradhya Bhatia dev_dbg(oldi->dev, "OLDI%u: new rate %lu Hz (requested %lu Hz)\n", 1067246e092SAradhya Bhatia oldi->oldi_instance, clk_get_rate(oldi->serial), rate); 1077246e092SAradhya Bhatia 1087246e092SAradhya Bhatia return 0; 1097246e092SAradhya Bhatia } 1107246e092SAradhya Bhatia 1117246e092SAradhya Bhatia static void tidss_oldi_tx_power(struct tidss_oldi *oldi, bool enable) 1127246e092SAradhya Bhatia { 1137246e092SAradhya Bhatia u32 mask; 1147246e092SAradhya Bhatia 1157246e092SAradhya Bhatia /* 1167246e092SAradhya Bhatia * The power control bits are Active Low, and remain powered off by 1177246e092SAradhya Bhatia * default. That is, the bits are set to 1. To power on the OLDI TXes, 1187246e092SAradhya Bhatia * the bits must be cleared to 0. Since there are cases where not all 1197246e092SAradhya Bhatia * OLDI TXes are being used, the power logic selectively powers them 1207246e092SAradhya Bhatia * on. 1217246e092SAradhya Bhatia * Setting the variable 'val' to particular bit masks, makes sure that 1227246e092SAradhya Bhatia * the undesired OLDI TXes remain powered off. 1237246e092SAradhya Bhatia */ 1247246e092SAradhya Bhatia 1257246e092SAradhya Bhatia if (enable) { 1267246e092SAradhya Bhatia switch (oldi->link_type) { 1277246e092SAradhya Bhatia case OLDI_MODE_SINGLE_LINK: 1287246e092SAradhya Bhatia /* Power-on only the required OLDI TX's IO*/ 1297246e092SAradhya Bhatia mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) | OLDI_PWRDN_BG; 1307246e092SAradhya Bhatia break; 1317246e092SAradhya Bhatia case OLDI_MODE_CLONE_SINGLE_LINK: 1327246e092SAradhya Bhatia case OLDI_MODE_DUAL_LINK: 1337246e092SAradhya Bhatia /* Power-on both the OLDI TXes' IOs */ 1347246e092SAradhya Bhatia mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) | 1357246e092SAradhya Bhatia OLDI_PWRDOWN_TX(oldi->companion_instance) | 1367246e092SAradhya Bhatia OLDI_PWRDN_BG; 1377246e092SAradhya Bhatia break; 1387246e092SAradhya Bhatia default: 1397246e092SAradhya Bhatia /* 1407246e092SAradhya Bhatia * This code execution should never reach here as any 1417246e092SAradhya Bhatia * OLDI with an unsupported OLDI mode would never get 1427246e092SAradhya Bhatia * registered in the first place. 1437246e092SAradhya Bhatia * However, power-off the OLDI in concern just in case. 1447246e092SAradhya Bhatia */ 1457246e092SAradhya Bhatia mask = OLDI_PWRDOWN_TX(oldi->oldi_instance); 1467246e092SAradhya Bhatia enable = false; 1477246e092SAradhya Bhatia break; 1487246e092SAradhya Bhatia } 1497246e092SAradhya Bhatia } else { 1507246e092SAradhya Bhatia switch (oldi->link_type) { 1517246e092SAradhya Bhatia case OLDI_MODE_CLONE_SINGLE_LINK: 1527246e092SAradhya Bhatia case OLDI_MODE_DUAL_LINK: 1537246e092SAradhya Bhatia mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) | 1547246e092SAradhya Bhatia OLDI_PWRDOWN_TX(oldi->companion_instance) | 1557246e092SAradhya Bhatia OLDI_PWRDN_BG; 1567246e092SAradhya Bhatia break; 1577246e092SAradhya Bhatia case OLDI_MODE_SINGLE_LINK: 1587246e092SAradhya Bhatia default: 1597246e092SAradhya Bhatia mask = OLDI_PWRDOWN_TX(oldi->oldi_instance); 1607246e092SAradhya Bhatia break; 1617246e092SAradhya Bhatia } 1627246e092SAradhya Bhatia } 1637246e092SAradhya Bhatia 1647246e092SAradhya Bhatia regmap_update_bits(oldi->io_ctrl, OLDI_PD_CTRL, mask, enable ? 0 : mask); 1657246e092SAradhya Bhatia } 1667246e092SAradhya Bhatia 1677246e092SAradhya Bhatia static int tidss_oldi_config(struct tidss_oldi *oldi) 1687246e092SAradhya Bhatia { 1697246e092SAradhya Bhatia const struct oldi_bus_format *bus_fmt = NULL; 1707246e092SAradhya Bhatia u32 oldi_cfg = 0; 1717246e092SAradhya Bhatia int ret; 1727246e092SAradhya Bhatia 1737246e092SAradhya Bhatia bus_fmt = oldi->bus_format; 1747246e092SAradhya Bhatia 1757246e092SAradhya Bhatia /* 1767246e092SAradhya Bhatia * MASTERSLAVE and SRC bits of OLDI Config are always set to 0. 1777246e092SAradhya Bhatia */ 1787246e092SAradhya Bhatia 1797246e092SAradhya Bhatia if (bus_fmt->data_width == 24) 1807246e092SAradhya Bhatia oldi_cfg |= OLDI_MSB; 1817246e092SAradhya Bhatia else if (bus_fmt->data_width != 18) 1827246e092SAradhya Bhatia dev_warn(oldi->dev, 1837246e092SAradhya Bhatia "OLDI%u: DSS port width %d not supported\n", 1847246e092SAradhya Bhatia oldi->oldi_instance, bus_fmt->data_width); 1857246e092SAradhya Bhatia 1867246e092SAradhya Bhatia oldi_cfg |= OLDI_DEPOL; 1877246e092SAradhya Bhatia 1887246e092SAradhya Bhatia oldi_cfg = (oldi_cfg & (~OLDI_MAP)) | (bus_fmt->oldi_mode_reg_val << 1); 1897246e092SAradhya Bhatia 1907246e092SAradhya Bhatia oldi_cfg |= OLDI_SOFTRST; 1917246e092SAradhya Bhatia 1927246e092SAradhya Bhatia oldi_cfg |= OLDI_ENABLE; 1937246e092SAradhya Bhatia 1947246e092SAradhya Bhatia switch (oldi->link_type) { 1957246e092SAradhya Bhatia case OLDI_MODE_SINGLE_LINK: 1967246e092SAradhya Bhatia /* All configuration is done for this mode. */ 1977246e092SAradhya Bhatia break; 1987246e092SAradhya Bhatia 1997246e092SAradhya Bhatia case OLDI_MODE_CLONE_SINGLE_LINK: 2007246e092SAradhya Bhatia oldi_cfg |= OLDI_CLONE_MODE; 2017246e092SAradhya Bhatia break; 2027246e092SAradhya Bhatia 2037246e092SAradhya Bhatia case OLDI_MODE_DUAL_LINK: 2047246e092SAradhya Bhatia /* data-mapping field also indicates dual-link mode */ 2057246e092SAradhya Bhatia oldi_cfg |= BIT(3); 2067246e092SAradhya Bhatia oldi_cfg |= OLDI_DUALMODESYNC; 2077246e092SAradhya Bhatia break; 2087246e092SAradhya Bhatia 2097246e092SAradhya Bhatia default: 2107246e092SAradhya Bhatia dev_err(oldi->dev, "OLDI%u: Unsupported mode.\n", 2117246e092SAradhya Bhatia oldi->oldi_instance); 2127246e092SAradhya Bhatia return -EINVAL; 2137246e092SAradhya Bhatia } 2147246e092SAradhya Bhatia 2157246e092SAradhya Bhatia ret = tidss_configure_oldi(oldi->tidss, oldi->parent_vp, oldi_cfg); 2167246e092SAradhya Bhatia if (ret == -ETIMEDOUT) 2177246e092SAradhya Bhatia dev_warn(oldi->dev, "OLDI%u: timeout waiting for OLDI reset done.\n", 2187246e092SAradhya Bhatia oldi->oldi_instance); 2197246e092SAradhya Bhatia 2207246e092SAradhya Bhatia return ret; 2217246e092SAradhya Bhatia } 2227246e092SAradhya Bhatia 2237246e092SAradhya Bhatia static void tidss_oldi_atomic_pre_enable(struct drm_bridge *bridge, 2247246e092SAradhya Bhatia struct drm_atomic_state *state) 2257246e092SAradhya Bhatia { 2267246e092SAradhya Bhatia struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge); 2277246e092SAradhya Bhatia struct drm_connector *connector; 2287246e092SAradhya Bhatia struct drm_connector_state *conn_state; 2297246e092SAradhya Bhatia struct drm_crtc_state *crtc_state; 2307246e092SAradhya Bhatia struct drm_display_mode *mode; 2317246e092SAradhya Bhatia 2327246e092SAradhya Bhatia if (oldi->link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) 2337246e092SAradhya Bhatia return; 2347246e092SAradhya Bhatia 2357246e092SAradhya Bhatia connector = drm_atomic_get_new_connector_for_encoder(state, 2367246e092SAradhya Bhatia bridge->encoder); 2377246e092SAradhya Bhatia if (WARN_ON(!connector)) 2387246e092SAradhya Bhatia return; 2397246e092SAradhya Bhatia 2407246e092SAradhya Bhatia conn_state = drm_atomic_get_new_connector_state(state, connector); 2417246e092SAradhya Bhatia if (WARN_ON(!conn_state)) 2427246e092SAradhya Bhatia return; 2437246e092SAradhya Bhatia 2447246e092SAradhya Bhatia crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); 2457246e092SAradhya Bhatia if (WARN_ON(!crtc_state)) 2467246e092SAradhya Bhatia return; 2477246e092SAradhya Bhatia 2487246e092SAradhya Bhatia mode = &crtc_state->adjusted_mode; 2497246e092SAradhya Bhatia 2507246e092SAradhya Bhatia /* Configure the OLDI params*/ 2517246e092SAradhya Bhatia tidss_oldi_config(oldi); 2527246e092SAradhya Bhatia 2537246e092SAradhya Bhatia /* Set the OLDI serial clock (7 times the pixel clock) */ 2547246e092SAradhya Bhatia tidss_oldi_set_serial_clk(oldi, mode->clock * 7 * 1000); 2557246e092SAradhya Bhatia 2567246e092SAradhya Bhatia /* Enable OLDI IO power */ 2577246e092SAradhya Bhatia tidss_oldi_tx_power(oldi, true); 2587246e092SAradhya Bhatia } 2597246e092SAradhya Bhatia 2607246e092SAradhya Bhatia static void tidss_oldi_atomic_post_disable(struct drm_bridge *bridge, 2617246e092SAradhya Bhatia struct drm_atomic_state *state) 2627246e092SAradhya Bhatia { 2637246e092SAradhya Bhatia struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge); 2647246e092SAradhya Bhatia 2657246e092SAradhya Bhatia if (oldi->link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) 2667246e092SAradhya Bhatia return; 2677246e092SAradhya Bhatia 2687246e092SAradhya Bhatia /* Disable OLDI IO power */ 2697246e092SAradhya Bhatia tidss_oldi_tx_power(oldi, false); 2707246e092SAradhya Bhatia 2717246e092SAradhya Bhatia /* Set the OLDI serial clock to IDLE Frequency */ 2727246e092SAradhya Bhatia tidss_oldi_set_serial_clk(oldi, OLDI_IDLE_CLK_HZ); 2737246e092SAradhya Bhatia 2747246e092SAradhya Bhatia /* Clear OLDI Config */ 2757246e092SAradhya Bhatia tidss_disable_oldi(oldi->tidss, oldi->parent_vp); 2767246e092SAradhya Bhatia } 2777246e092SAradhya Bhatia 2787246e092SAradhya Bhatia #define MAX_INPUT_SEL_FORMATS 1 2797246e092SAradhya Bhatia 2807246e092SAradhya Bhatia static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge, 2817246e092SAradhya Bhatia struct drm_bridge_state *bridge_state, 2827246e092SAradhya Bhatia struct drm_crtc_state *crtc_state, 2837246e092SAradhya Bhatia struct drm_connector_state *conn_state, 2847246e092SAradhya Bhatia u32 output_fmt, 2857246e092SAradhya Bhatia unsigned int *num_input_fmts) 2867246e092SAradhya Bhatia { 2877246e092SAradhya Bhatia struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge); 2887246e092SAradhya Bhatia u32 *input_fmts; 2897246e092SAradhya Bhatia int i; 2907246e092SAradhya Bhatia 2917246e092SAradhya Bhatia *num_input_fmts = 0; 2927246e092SAradhya Bhatia 2937246e092SAradhya Bhatia for (i = 0; i < ARRAY_SIZE(oldi_bus_formats); i++) 2947246e092SAradhya Bhatia if (oldi_bus_formats[i].bus_fmt == output_fmt) 2957246e092SAradhya Bhatia break; 2967246e092SAradhya Bhatia 2977246e092SAradhya Bhatia if (i == ARRAY_SIZE(oldi_bus_formats)) 2987246e092SAradhya Bhatia return NULL; 2997246e092SAradhya Bhatia 3007246e092SAradhya Bhatia input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), 3017246e092SAradhya Bhatia GFP_KERNEL); 3027246e092SAradhya Bhatia if (!input_fmts) 3037246e092SAradhya Bhatia return NULL; 3047246e092SAradhya Bhatia 3057246e092SAradhya Bhatia *num_input_fmts = 1; 3067246e092SAradhya Bhatia input_fmts[0] = oldi_bus_formats[i].input_bus_fmt; 3077246e092SAradhya Bhatia oldi->bus_format = &oldi_bus_formats[i]; 3087246e092SAradhya Bhatia 3097246e092SAradhya Bhatia return input_fmts; 3107246e092SAradhya Bhatia } 3117246e092SAradhya Bhatia 3127246e092SAradhya Bhatia static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = { 3137246e092SAradhya Bhatia .attach = tidss_oldi_bridge_attach, 3147246e092SAradhya Bhatia .atomic_pre_enable = tidss_oldi_atomic_pre_enable, 3157246e092SAradhya Bhatia .atomic_post_disable = tidss_oldi_atomic_post_disable, 3167246e092SAradhya Bhatia .atomic_get_input_bus_fmts = tidss_oldi_atomic_get_input_bus_fmts, 3177246e092SAradhya Bhatia .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 3187246e092SAradhya Bhatia .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 3197246e092SAradhya Bhatia .atomic_reset = drm_atomic_helper_bridge_reset, 3207246e092SAradhya Bhatia }; 3217246e092SAradhya Bhatia 3227246e092SAradhya Bhatia static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance) 3237246e092SAradhya Bhatia { 3247246e092SAradhya Bhatia struct device_node *companion; 3257246e092SAradhya Bhatia struct device_node *port0, *port1; 3267246e092SAradhya Bhatia u32 companion_reg; 3277246e092SAradhya Bhatia bool secondary_oldi = false; 3287246e092SAradhya Bhatia int pixel_order; 3297246e092SAradhya Bhatia 3307246e092SAradhya Bhatia /* 3317246e092SAradhya Bhatia * Find if the OLDI is paired with another OLDI for combined OLDI 3327246e092SAradhya Bhatia * operation (dual-link or clone). 3337246e092SAradhya Bhatia */ 3347246e092SAradhya Bhatia companion = of_parse_phandle(oldi_tx, "ti,companion-oldi", 0); 3357246e092SAradhya Bhatia if (!companion) 3367246e092SAradhya Bhatia /* 3377246e092SAradhya Bhatia * The OLDI TX does not have a companion, nor is it a 3387246e092SAradhya Bhatia * secondary OLDI. It will operate independently. 3397246e092SAradhya Bhatia */ 3407246e092SAradhya Bhatia return OLDI_MODE_SINGLE_LINK; 3417246e092SAradhya Bhatia 3427246e092SAradhya Bhatia if (of_property_read_u32(companion, "reg", &companion_reg)) 3437246e092SAradhya Bhatia return OLDI_MODE_UNSUPPORTED; 3447246e092SAradhya Bhatia 3457246e092SAradhya Bhatia if (companion_reg > (TIDSS_MAX_OLDI_TXES - 1)) 3467246e092SAradhya Bhatia /* Invalid companion OLDI reg value. */ 3477246e092SAradhya Bhatia return OLDI_MODE_UNSUPPORTED; 3487246e092SAradhya Bhatia 3497246e092SAradhya Bhatia *companion_instance = (int)companion_reg; 3507246e092SAradhya Bhatia 3517246e092SAradhya Bhatia if (of_property_read_bool(oldi_tx, "ti,secondary-oldi")) 3527246e092SAradhya Bhatia secondary_oldi = true; 3537246e092SAradhya Bhatia 3547246e092SAradhya Bhatia /* 3557246e092SAradhya Bhatia * We need to work out if the sink is expecting us to function in 3567246e092SAradhya Bhatia * dual-link mode. We do this by looking at the DT port nodes, the 3577246e092SAradhya Bhatia * OLDI TX ports are connected to. If they are marked as expecting 3587246e092SAradhya Bhatia * even pixels and odd pixels, then we need to enable dual-link. 3597246e092SAradhya Bhatia */ 3607246e092SAradhya Bhatia port0 = of_graph_get_port_by_id(oldi_tx, 1); 3617246e092SAradhya Bhatia port1 = of_graph_get_port_by_id(companion, 1); 3627246e092SAradhya Bhatia pixel_order = drm_of_lvds_get_dual_link_pixel_order(port0, port1); 3637246e092SAradhya Bhatia of_node_put(port0); 3647246e092SAradhya Bhatia of_node_put(port1); 3657246e092SAradhya Bhatia of_node_put(companion); 3667246e092SAradhya Bhatia 3677246e092SAradhya Bhatia switch (pixel_order) { 3687246e092SAradhya Bhatia case -EINVAL: 3697246e092SAradhya Bhatia /* 3707246e092SAradhya Bhatia * The dual-link properties were not found in at least 3717246e092SAradhya Bhatia * one of the sink nodes. Since 2 OLDI ports are present 3727246e092SAradhya Bhatia * in the DT, it can be safely assumed that the required 3737246e092SAradhya Bhatia * configuration is Clone Mode. 3747246e092SAradhya Bhatia */ 3757246e092SAradhya Bhatia return (secondary_oldi ? OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK : 3767246e092SAradhya Bhatia OLDI_MODE_CLONE_SINGLE_LINK); 3777246e092SAradhya Bhatia 3787246e092SAradhya Bhatia case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: 3797246e092SAradhya Bhatia /* 3807246e092SAradhya Bhatia * Primary OLDI can only support "ODD" pixels. So, from its 3817246e092SAradhya Bhatia * perspective, the pixel order has to be ODD-EVEN. 3827246e092SAradhya Bhatia */ 3837246e092SAradhya Bhatia return (secondary_oldi ? OLDI_MODE_UNSUPPORTED : 3847246e092SAradhya Bhatia OLDI_MODE_DUAL_LINK); 3857246e092SAradhya Bhatia 3867246e092SAradhya Bhatia case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: 3877246e092SAradhya Bhatia /* 3887246e092SAradhya Bhatia * Secondary OLDI can only support "EVEN" pixels. So, from its 3897246e092SAradhya Bhatia * perspective, the pixel order has to be EVEN-ODD. 3907246e092SAradhya Bhatia */ 3917246e092SAradhya Bhatia return (secondary_oldi ? OLDI_MODE_SECONDARY_DUAL_LINK : 3927246e092SAradhya Bhatia OLDI_MODE_UNSUPPORTED); 3937246e092SAradhya Bhatia 3947246e092SAradhya Bhatia default: 3957246e092SAradhya Bhatia return OLDI_MODE_UNSUPPORTED; 3967246e092SAradhya Bhatia } 3977246e092SAradhya Bhatia } 3987246e092SAradhya Bhatia 3997246e092SAradhya Bhatia static int get_parent_dss_vp(struct device_node *oldi_tx, u32 *parent_vp) 4007246e092SAradhya Bhatia { 4017246e092SAradhya Bhatia struct device_node *ep, *dss_port; 4027246e092SAradhya Bhatia int ret; 4037246e092SAradhya Bhatia 4047246e092SAradhya Bhatia ep = of_graph_get_endpoint_by_regs(oldi_tx, OLDI_INPUT_PORT, -1); 4057246e092SAradhya Bhatia if (ep) { 4067246e092SAradhya Bhatia dss_port = of_graph_get_remote_port(ep); 4077246e092SAradhya Bhatia if (!dss_port) { 4087246e092SAradhya Bhatia ret = -ENODEV; 4097246e092SAradhya Bhatia goto err_return_ep_port; 4107246e092SAradhya Bhatia } 4117246e092SAradhya Bhatia 4127246e092SAradhya Bhatia ret = of_property_read_u32(dss_port, "reg", parent_vp); 4137246e092SAradhya Bhatia 4147246e092SAradhya Bhatia of_node_put(dss_port); 4157246e092SAradhya Bhatia err_return_ep_port: 4167246e092SAradhya Bhatia of_node_put(ep); 4177246e092SAradhya Bhatia return ret; 4187246e092SAradhya Bhatia } 4197246e092SAradhya Bhatia 4207246e092SAradhya Bhatia return -ENODEV; 4217246e092SAradhya Bhatia } 4227246e092SAradhya Bhatia 4237246e092SAradhya Bhatia static const struct drm_bridge_timings default_tidss_oldi_timings = { 4247246e092SAradhya Bhatia .input_bus_flags = DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE 4257246e092SAradhya Bhatia | DRM_BUS_FLAG_DE_HIGH, 4267246e092SAradhya Bhatia }; 4277246e092SAradhya Bhatia 4287246e092SAradhya Bhatia void tidss_oldi_deinit(struct tidss_device *tidss) 4297246e092SAradhya Bhatia { 4307246e092SAradhya Bhatia for (int i = 0; i < tidss->num_oldis; i++) { 4317246e092SAradhya Bhatia if (tidss->oldis[i]) { 4327246e092SAradhya Bhatia drm_bridge_remove(&tidss->oldis[i]->bridge); 4337246e092SAradhya Bhatia tidss->oldis[i] = NULL; 4347246e092SAradhya Bhatia } 4357246e092SAradhya Bhatia } 4367246e092SAradhya Bhatia } 4377246e092SAradhya Bhatia 4387246e092SAradhya Bhatia int tidss_oldi_init(struct tidss_device *tidss) 4397246e092SAradhya Bhatia { 4407246e092SAradhya Bhatia struct tidss_oldi *oldi; 4417246e092SAradhya Bhatia struct device_node *child; 4427246e092SAradhya Bhatia struct drm_bridge *bridge; 4437246e092SAradhya Bhatia u32 parent_vp, oldi_instance; 4447246e092SAradhya Bhatia int companion_instance = -1; 4457246e092SAradhya Bhatia enum tidss_oldi_link_type link_type = OLDI_MODE_UNSUPPORTED; 4467246e092SAradhya Bhatia struct device_node *oldi_parent; 4477246e092SAradhya Bhatia int ret = 0; 4487246e092SAradhya Bhatia 4497246e092SAradhya Bhatia tidss->num_oldis = 0; 4507246e092SAradhya Bhatia 4517246e092SAradhya Bhatia oldi_parent = of_get_child_by_name(tidss->dev->of_node, "oldi-transmitters"); 4527246e092SAradhya Bhatia if (!oldi_parent) 4537246e092SAradhya Bhatia /* Return gracefully */ 4547246e092SAradhya Bhatia return 0; 4557246e092SAradhya Bhatia 4567246e092SAradhya Bhatia for_each_available_child_of_node(oldi_parent, child) { 4577246e092SAradhya Bhatia ret = get_parent_dss_vp(child, &parent_vp); 4587246e092SAradhya Bhatia if (ret) { 4597246e092SAradhya Bhatia if (ret == -ENODEV) { 4607246e092SAradhya Bhatia /* 4617246e092SAradhya Bhatia * ENODEV means that this particular OLDI node 4627246e092SAradhya Bhatia * is not connected with the DSS, which is not 4637246e092SAradhya Bhatia * a harmful case. There could be another OLDI 4647246e092SAradhya Bhatia * which may still be connected. 4657246e092SAradhya Bhatia * Continue to search for that. 4667246e092SAradhya Bhatia */ 4677246e092SAradhya Bhatia ret = 0; 4687246e092SAradhya Bhatia continue; 4697246e092SAradhya Bhatia } 4707246e092SAradhya Bhatia goto err_put_node; 4717246e092SAradhya Bhatia } 4727246e092SAradhya Bhatia 4737246e092SAradhya Bhatia ret = of_property_read_u32(child, "reg", &oldi_instance); 4747246e092SAradhya Bhatia if (ret) 4757246e092SAradhya Bhatia goto err_put_node; 4767246e092SAradhya Bhatia 4777246e092SAradhya Bhatia /* 4787246e092SAradhya Bhatia * Now that it's confirmed that OLDI is connected with DSS, 4797246e092SAradhya Bhatia * let's continue getting the OLDI sinks ahead and other OLDI 4807246e092SAradhya Bhatia * properties. 4817246e092SAradhya Bhatia */ 4827246e092SAradhya Bhatia bridge = devm_drm_of_get_bridge(tidss->dev, child, 4837246e092SAradhya Bhatia OLDI_OUTPUT_PORT, 0); 4847246e092SAradhya Bhatia if (IS_ERR(bridge)) { 4857246e092SAradhya Bhatia /* 4867246e092SAradhya Bhatia * Either there was no OLDI sink in the devicetree, or 4877246e092SAradhya Bhatia * the OLDI sink has not been added yet. In any case, 4887246e092SAradhya Bhatia * return. 4897246e092SAradhya Bhatia * We don't want to have an OLDI node connected to DSS 4907246e092SAradhya Bhatia * but not to any sink. 4917246e092SAradhya Bhatia */ 4927246e092SAradhya Bhatia ret = dev_err_probe(tidss->dev, PTR_ERR(bridge), 4937246e092SAradhya Bhatia "no panel/bridge for OLDI%u.\n", 4947246e092SAradhya Bhatia oldi_instance); 4957246e092SAradhya Bhatia goto err_put_node; 4967246e092SAradhya Bhatia } 4977246e092SAradhya Bhatia 4987246e092SAradhya Bhatia link_type = get_oldi_mode(child, &companion_instance); 4997246e092SAradhya Bhatia if (link_type == OLDI_MODE_UNSUPPORTED) { 5007246e092SAradhya Bhatia ret = dev_err_probe(tidss->dev, -EINVAL, 5017246e092SAradhya Bhatia "OLDI%u: Unsupported OLDI connection.\n", 5027246e092SAradhya Bhatia oldi_instance); 5037246e092SAradhya Bhatia goto err_put_node; 5047246e092SAradhya Bhatia } else if ((link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) || 5057246e092SAradhya Bhatia (link_type == OLDI_MODE_CLONE_SINGLE_LINK)) { 5067246e092SAradhya Bhatia /* 5077246e092SAradhya Bhatia * The OLDI driver cannot support OLDI clone mode 5087246e092SAradhya Bhatia * properly at present. 5097246e092SAradhya Bhatia * The clone mode requires 2 working encoder-bridge 5107246e092SAradhya Bhatia * pipelines, generating from the same crtc. The DRM 5117246e092SAradhya Bhatia * framework does not support this at present. If 5127246e092SAradhya Bhatia * there were to be, say, 2 OLDI sink bridges each 5137246e092SAradhya Bhatia * connected to an OLDI TXes, they couldn't both be 5147246e092SAradhya Bhatia * supported simultaneously. 5157246e092SAradhya Bhatia * This driver still has some code pertaining to OLDI 5167246e092SAradhya Bhatia * clone mode configuration in DSS hardware for future, 5177246e092SAradhya Bhatia * when there is a better infrastructure in the DRM 5187246e092SAradhya Bhatia * framework to support 2 encoder-bridge pipelines 5197246e092SAradhya Bhatia * simultaneously. 5207246e092SAradhya Bhatia * Till that time, this driver shall error out if it 5217246e092SAradhya Bhatia * detects a clone mode configuration. 5227246e092SAradhya Bhatia */ 5237246e092SAradhya Bhatia ret = dev_err_probe(tidss->dev, -EOPNOTSUPP, 5247246e092SAradhya Bhatia "The OLDI driver does not support Clone Mode at present.\n"); 5257246e092SAradhya Bhatia goto err_put_node; 5267246e092SAradhya Bhatia } else if (link_type == OLDI_MODE_SECONDARY_DUAL_LINK) { 5277246e092SAradhya Bhatia /* 5287246e092SAradhya Bhatia * This is the secondary OLDI node, which serves as a 5297246e092SAradhya Bhatia * companion to the primary OLDI, when it is configured 5307246e092SAradhya Bhatia * for the dual-link mode. Since the primary OLDI will 5317246e092SAradhya Bhatia * be a part of bridge chain, no need to put this one 5327246e092SAradhya Bhatia * too. Continue onto the next OLDI node. 5337246e092SAradhya Bhatia */ 5347246e092SAradhya Bhatia continue; 5357246e092SAradhya Bhatia } 5367246e092SAradhya Bhatia 537*b213eb34SJayesh Choudhary oldi = devm_drm_bridge_alloc(tidss->dev, struct tidss_oldi, bridge, 538*b213eb34SJayesh Choudhary &tidss_oldi_bridge_funcs); 539*b213eb34SJayesh Choudhary if (IS_ERR(oldi)) { 540*b213eb34SJayesh Choudhary ret = PTR_ERR(oldi); 5417246e092SAradhya Bhatia goto err_put_node; 5427246e092SAradhya Bhatia } 5437246e092SAradhya Bhatia 5447246e092SAradhya Bhatia oldi->parent_vp = parent_vp; 5457246e092SAradhya Bhatia oldi->oldi_instance = oldi_instance; 5467246e092SAradhya Bhatia oldi->companion_instance = companion_instance; 5477246e092SAradhya Bhatia oldi->link_type = link_type; 5487246e092SAradhya Bhatia oldi->dev = tidss->dev; 5497246e092SAradhya Bhatia oldi->next_bridge = bridge; 5507246e092SAradhya Bhatia 5517246e092SAradhya Bhatia /* 5527246e092SAradhya Bhatia * Only the primary OLDI needs to reference the io-ctrl system 5537246e092SAradhya Bhatia * registers, and the serial clock. 5547246e092SAradhya Bhatia * We don't require a check for secondary OLDI in dual-link mode 5557246e092SAradhya Bhatia * because the driver will not create a drm_bridge instance. 5567246e092SAradhya Bhatia * But the driver will need to create a drm_bridge instance, 5577246e092SAradhya Bhatia * for secondary OLDI in clone mode (once it is supported). 5587246e092SAradhya Bhatia */ 5597246e092SAradhya Bhatia if (link_type != OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) { 5607246e092SAradhya Bhatia oldi->io_ctrl = syscon_regmap_lookup_by_phandle(child, 5617246e092SAradhya Bhatia "ti,oldi-io-ctrl"); 5627246e092SAradhya Bhatia if (IS_ERR(oldi->io_ctrl)) { 5637246e092SAradhya Bhatia ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->io_ctrl), 5647246e092SAradhya Bhatia "OLDI%u: syscon_regmap_lookup_by_phandle failed.\n", 5657246e092SAradhya Bhatia oldi_instance); 5667246e092SAradhya Bhatia goto err_put_node; 5677246e092SAradhya Bhatia } 5687246e092SAradhya Bhatia 5697246e092SAradhya Bhatia oldi->serial = of_clk_get_by_name(child, "serial"); 5707246e092SAradhya Bhatia if (IS_ERR(oldi->serial)) { 5717246e092SAradhya Bhatia ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->serial), 5727246e092SAradhya Bhatia "OLDI%u: Failed to get serial clock.\n", 5737246e092SAradhya Bhatia oldi_instance); 5747246e092SAradhya Bhatia goto err_put_node; 5757246e092SAradhya Bhatia } 5767246e092SAradhya Bhatia } 5777246e092SAradhya Bhatia 5787246e092SAradhya Bhatia /* Register the bridge. */ 5797246e092SAradhya Bhatia oldi->bridge.of_node = child; 5807246e092SAradhya Bhatia oldi->bridge.driver_private = oldi; 5817246e092SAradhya Bhatia oldi->bridge.timings = &default_tidss_oldi_timings; 5827246e092SAradhya Bhatia 5837246e092SAradhya Bhatia tidss->oldis[tidss->num_oldis++] = oldi; 5847246e092SAradhya Bhatia oldi->tidss = tidss; 5857246e092SAradhya Bhatia 5867246e092SAradhya Bhatia drm_bridge_add(&oldi->bridge); 5877246e092SAradhya Bhatia } 5887246e092SAradhya Bhatia 5897246e092SAradhya Bhatia of_node_put(child); 5907246e092SAradhya Bhatia of_node_put(oldi_parent); 5917246e092SAradhya Bhatia 5927246e092SAradhya Bhatia return 0; 5937246e092SAradhya Bhatia 5947246e092SAradhya Bhatia err_put_node: 5957246e092SAradhya Bhatia of_node_put(child); 5967246e092SAradhya Bhatia of_node_put(oldi_parent); 5977246e092SAradhya Bhatia return ret; 5987246e092SAradhya Bhatia } 599