xref: /linux/drivers/gpu/drm/tidss/tidss_oldi.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
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