[PATCH] drivers: video: tidss: Refactor tidss_drv

Swamil Jain s-jain1 at ti.com
Fri Jun 13 11:29:48 CEST 2025



On 03/06/25 16:27, Swamil Jain wrote:
> - Refactor tidss_drv to improve modularity, enabling support for more
>    display interfaces beyond OLDI in the future
> - Add detection and initialization of active OLDI panels using the DT
> - Port tidss_oldi.c from the upstream Linux kernel oldi series[0] and
>    derive several APIs from it to determine the dual link pixel order
> - Add tidss_oldi_init() and helper routines to handle OLDI-specific
>    setup and move related helper routines to tidss_oldi.c
> 
> [0]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/
> 

Gentle ping on this.

Regards,
Swamil.

> Signed-off-by: Swamil Jain <s-jain1 at ti.com>
> ---
>   drivers/video/tidss/Makefile     |   2 +-
>   drivers/video/tidss/tidss_drv.c  | 122 +++++++++----
>   drivers/video/tidss/tidss_drv.h  |  14 +-
>   drivers/video/tidss/tidss_oldi.c | 298 +++++++++++++++++++++++++++++++
>   drivers/video/tidss/tidss_oldi.h |  67 +++++++
>   5 files changed, 461 insertions(+), 42 deletions(-)
>   create mode 100644 drivers/video/tidss/tidss_oldi.c
>   create mode 100644 drivers/video/tidss/tidss_oldi.h
> 
> diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile
> index 3381a5fec57..846c19c5a7b 100644
> --- a/drivers/video/tidss/Makefile
> +++ b/drivers/video/tidss/Makefile
> @@ -9,4 +9,4 @@
>   # Author: Tomi Valkeinen <tomi.valkeinen at ti.com>
>   
>   
> -obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} = tidss_drv.o
> +obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} = tidss_drv.o tidss_oldi.o
> diff --git a/drivers/video/tidss/tidss_drv.c b/drivers/video/tidss/tidss_drv.c
> index 865d4bddb7f..93081ed1dc0 100644
> --- a/drivers/video/tidss/tidss_drv.c
> +++ b/drivers/video/tidss/tidss_drv.c
> @@ -32,6 +32,7 @@
>   #include <dm/of_access.h>
>   #include <dm/device_compat.h>
>   #include <dm/device-internal.h>
> +#include <dm/ofnode_graph.h>
>   
>   #include <linux/bug.h>
>   #include <linux/err.h>
> @@ -40,6 +41,7 @@
>   
>   #include "tidss_drv.h"
>   #include "tidss_regs.h"
> +#include "tidss_oldi.h"
>   
>   DECLARE_GLOBAL_DATA_PTR;
>   
> @@ -253,7 +255,7 @@ static void dss_oldi_tx_power(struct tidss_drv_priv *priv, bool power)
>   {
>   	u32 val;
>   
> -	if (WARN_ON(!priv->oldi_io_ctrl))
> +	if (WARN_ON(!priv->oldis[0]->io_ctrl))
>   		return;
>   
>   	if (priv->feat->subrev == DSS_AM625) {
> @@ -275,7 +277,7 @@ static void dss_oldi_tx_power(struct tidss_drv_priv *priv, bool power)
>   		} else {
>   			val = OLDI_BANDGAP_PWR | OLDI0_PWRDN_TX | OLDI1_PWRDN_TX;
>   		}
> -		regmap_update_bits(priv->oldi_io_ctrl, OLDI_PD_CTRL,
> +		regmap_update_bits(priv->oldis[0]->io_ctrl, OLDI_PD_CTRL,
>   				   OLDI_BANDGAP_PWR | OLDI0_PWRDN_TX | OLDI1_PWRDN_TX, val);
>   	}
>   }
> @@ -562,7 +564,7 @@ int dss_vp_enable_clk(struct tidss_drv_priv *priv, u32 hw_videoport)
>   void dss_vp_prepare(struct tidss_drv_priv *priv, u32 hw_videoport)
>   {
>   	dss_vp_set_gamma(priv, hw_videoport, NULL, 0);
> -	dss_vp_set_default_color(priv, 0, 0);
> +	dss_vp_set_default_color(priv, hw_videoport, 0);
>   
>   	if (priv->feat->vp_bus_type[hw_videoport] == DSS_VP_OLDI) {
>   		dss_oldi_tx_power(priv, true);
> @@ -734,27 +736,72 @@ static void dss_vp_init(struct tidss_drv_priv *priv)
>   		VP_REG_FLD_MOD(priv, i, DSS_VP_CONFIG, 1, 2, 2);
>   }
>   
> -static int dss_init_am65x_oldi_io_ctrl(struct udevice *dev,
> -				       struct tidss_drv_priv *priv)
> +static bool is_panel_enabled(ofnode endpoint)
>   {
> -	struct udevice *syscon;
> -	struct regmap *regmap;
> -	int ret = 0;
> +	ofnode port_parent = ofnode_graph_get_remote_port_parent(endpoint);
>   
> -	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "ti,am65x-oldi-io-ctrl",
> -					   &syscon);
> -	if (ret) {
> -		debug("unable to find ti,am65x-oldi-io-ctrl syscon device (%d)\n", ret);
> -		return ret;
> -	}
> +	/* ports_parent is the bridge connected in between */
> +	ofnode ports_parent = ofnode_get_parent(port_parent);
> +
> +	/* Checking for both port_parent and ports_parent node, as if any
> +	 * bridge is there then it should also be enabled for the panel to
> +	 * show output.
> +	 */
> +	return ofnode_is_enabled(port_parent) && ofnode_is_enabled(ports_parent);
> +}
> +
> +static int tidss_attach_active_panels(struct tidss_drv_priv *priv)
> +{
> +	ofnode dss_node = dev_ofnode(priv->dev);
> +	ofnode dss_ports = ofnode_find_subnode(dss_node, "ports");
> +	ofnode port, remote_port, local_endpoint;
> +	int hw_videoport;
> +	int active_panels = 0;
> +	int ret;
>   
> -	/* get grf-reg base address */
> -	regmap = syscon_get_regmap(syscon);
> -	if (!regmap) {
> -		debug("unable to find rockchip grf regmap\n");
> -		return -ENODEV;
> +	ofnode_for_each_subnode(port, dss_ports) {
> +		if (strncmp(ofnode_get_name(port), "port", 4))
> +			continue;
> +
> +		ofnode_for_each_subnode(local_endpoint, port) {
> +			if (strncmp(ofnode_get_name(local_endpoint), "endpoint", 8))
> +				continue;
> +
> +			if (!is_panel_enabled(local_endpoint))
> +				continue;
> +
> +			/* Get videoport id*/
> +			ret = ofnode_read_u32(port, "reg", &hw_videoport);
> +			if (ret) {
> +				dev_warn(priv->dev,
> +					 "Failed to read videoport id, reg property not found for node: %s\n",
> +					 ofnode_get_name(local_endpoint));
> +				/* Check for other video ports */
> +				continue;
> +			}
> +
> +			remote_port = ofnode_graph_get_remote_port_parent(local_endpoint);
> +			if (strstr(ofnode_get_name(remote_port), "oldi")) {
> +				/* Initialize oldi */
> +				ret = tidss_oldi_init(priv->dev);
> +				if (ret) {
> +					if (ret != -ENODEV)
> +						dev_warn(priv->dev, "oldi panel error %d\n", ret);
> +					break;
> +				}
> +
> +				active_panels++;
> +				priv->active_hw_videoport_id = hw_videoport;
> +				/* Only one dual-link oldi panel supported at a time so
> +				 * initialize it only and then check for other videoports
> +				 */
> +				break;
> +			}
> +		}
>   	}
> -	priv->oldi_io_ctrl = regmap;
> +	priv->active_panels = active_panels;
> +	if (active_panels == 0)
> +		return -1;
>   	return 0;
>   }
>   
> @@ -772,7 +819,6 @@ static int tidss_drv_probe(struct udevice *dev)
>   	priv->dev = dev;
>   
>   	priv->feat = &dss_am625_feats;
> -
>       /*
>        * set your plane format based on your bmp image
>        * Supported 24bpp and 32bpp bmpimages
> @@ -782,6 +828,13 @@ static int tidss_drv_probe(struct udevice *dev)
>   
>   	dss_common_regmap = priv->feat->common_regs;
>   
> +	ret = tidss_attach_active_panels(priv);
> +	if (ret) {
> +		if (ret == -1)
> +			dev_warn(priv->dev, "NO active panels detected, check status of panel nodes\n");
> +		return ret;
> +	}
> +
>   	ret = uclass_first_device_err(UCLASS_PANEL, &panel);
>   	if (ret) {
>   		if (ret != -ENODEV)
> @@ -829,7 +882,7 @@ static int tidss_drv_probe(struct udevice *dev)
>   	dss_vid_write(priv, 0, DSS_VID_BA_1, uc_plat->base & 0xffffffff);
>   	dss_vid_write(priv, 0, DSS_VID_BA_EXT_1, (u64)uc_plat->base >> 32);
>   
> -	ret = dss_plane_setup(priv, 0, 0);
> +	ret = dss_plane_setup(priv, 0, priv->active_hw_videoport_id);
>   	if (ret) {
>   		dss_plane_enable(priv, 0, false);
>   			return ret;
> @@ -844,34 +897,27 @@ static int tidss_drv_probe(struct udevice *dev)
>   		priv->base_vp[i] = dev_remap_addr_name(dev, priv->feat->vp_name[i]);
>   	}
>   
> -	ret = clk_get_by_name(dev, "vp1", &priv->vp_clk[0]);
> +	ret = clk_get_by_name(dev,
> +			      dss_am625_feats.vpclk_name[priv->active_hw_videoport_id],
> +			      &priv->vp_clk[priv->active_hw_videoport_id]);
>   	if (ret) {
>   		dev_err(dev, "video port %d clock enable error %d\n", i, ret);
>   		return ret;
>   	}
>   
> -	dss_ovr_set_plane(priv, 1, 0, 0, 0, 0);
> -	dss_ovr_enable_layer(priv, 0, 0, true);
> +	dss_ovr_set_plane(priv, 1, priv->active_hw_videoport_id, 0, 0, 0);
> +	dss_ovr_enable_layer(priv, priv->active_hw_videoport_id, 0, true);
>   
>   	/* Video Port cloks */
> -	dss_vp_enable_clk(priv, 0);
> +	dss_vp_enable_clk(priv, priv->active_hw_videoport_id);
>   
> -	dss_vp_set_clk_rate(priv, 0, timings.pixelclock.typ * 1000);
> +	dss_vp_set_clk_rate(priv, priv->active_hw_videoport_id, timings.pixelclock.typ * 1000);
>   
> -	priv->oldi_mode = OLDI_MODE_OFF;
>   	uc_priv->xsize = timings.hactive.typ;
>   	uc_priv->ysize = timings.vactive.typ;
> -	if (priv->feat->subrev == DSS_AM65X || priv->feat->subrev == DSS_AM625) {
> -		priv->oldi_mode = OLDI_DUAL_LINK;
> -		if (priv->oldi_mode) {
> -			ret = dss_init_am65x_oldi_io_ctrl(dev, priv);
> -			if (ret)
> -				return ret;
> -		}
> -	}
>   
> -	dss_vp_prepare(priv, 0);
> -	dss_vp_enable(priv, 0, &timings);
> +	dss_vp_prepare(priv, priv->active_hw_videoport_id);
> +	dss_vp_enable(priv, priv->active_hw_videoport_id, &timings);
>   	dss_vp_init(priv);
>   
>   	ret = clk_get_by_name(dev, "fck", &priv->fclk);
> diff --git a/drivers/video/tidss/tidss_drv.h b/drivers/video/tidss/tidss_drv.h
> index e229d975ff4..0431f41ae95 100644
> --- a/drivers/video/tidss/tidss_drv.h
> +++ b/drivers/video/tidss/tidss_drv.h
> @@ -13,9 +13,12 @@
>   #define __TIDSS_DRV_H__
>   
>   #include <media_bus_format.h>
> +#include <syscon.h>
> +#include <regmap.h>
>   
>   #define TIDSS_MAX_PORTS 4
>   #define TIDSS_MAX_PLANES 4
> +#define TIDSS_MAX_OLDI_TXES 2
>   
>   enum dss_vp_bus_type {
>   	DSS_VP_DPI,		/* DPI output */
> @@ -31,6 +34,8 @@ enum dss_oldi_modes {
>   	OLDI_DUAL_LINK,				/* Combined Output over OLDI 0 and 1. */
>   };
>   
> +enum oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
> +
>   struct dss_features_scaling {
>   	u32 in_width_max_5tap_rgb;
>   	u32 in_width_max_3tap_rgb;
> @@ -96,13 +101,11 @@ struct dss_features {
>   	u32 vid_order[TIDSS_MAX_PLANES];
>   };
>   
> -enum dss_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
> -
>   struct dss_bus_format {
>   	u32 bus_fmt;
>   	u32 data_width;
>   	bool is_oldi_fmt;
> -	enum dss_oldi_mode_reg_val oldi_mode_reg_val;
> +	enum oldi_mode_reg_val oldi_mode_reg_val;
>   };
>   
>   static struct dss_bus_format dss_bus_formats[] = {
> @@ -132,6 +135,11 @@ struct tidss_drv_priv {
>   	struct dss_bus_format *bus_format;
>   	u32 pixel_format;
>   	u32 memory_bandwidth_limit;
> +	unsigned int num_oldis;
> +	struct tidss_oldi *oldis[TIDSS_MAX_OLDI_TXES];
> +	int active_hw_videoport_id;
> +	int active_panels;
>   };
>   
> +struct tidss_oldi;
>   #endif
> diff --git a/drivers/video/tidss/tidss_oldi.c b/drivers/video/tidss/tidss_oldi.c
> new file mode 100644
> index 00000000000..a2f232b3e3d
> --- /dev/null
> +++ b/drivers/video/tidss/tidss_oldi.c
> @@ -0,0 +1,298 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/
> + * Swamil Jain <s-jain1 at ti.com>
> + *
> + * based on the linux tidss_oldi.c, which is
> + *
> + * Copyright (C) 2024 - Texas Instruments Incorporated
> + * Author: Aradhya Bhatia <a-bhatia1 at ti.com>
> + */
> +
> +#include <dm.h>
> +#include <dm/ofnode_graph.h>
> +#include <dm/ofnode.h>
> +#include <malloc.h>
> +#include <syscon.h>
> +#include <clk.h>
> +#include <regmap.h>
> +#include <dm/device_compat.h>
> +
> +#include "tidss_oldi.h"
> +
> +enum tidss_oldi_pixels {
> +	OLDI_PIXELS_EVEN = BIT(0),
> +	OLDI_PIXELS_ODD = BIT(1),
> +};
> +
> +/**
> + * enum tidss_oldi_dual_link_pixels - Pixel order of an OLDI dual-link connection
> + * @TIDSS_OLDI_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
> + *    from the first port, odd pixels from the second port
> + * @TIDSS_OLDI_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated
> + *    from the first port, even pixels from the second port
> + */
> +enum tidss_oldi_dual_link_pixels {
> +	TIDSS_OLDI_DUAL_LINK_EVEN_ODD_PIXELS = 0,
> +	TIDSS_OLDI_DUAL_LINK_ODD_EVEN_PIXELS = 1,
> +};
> +
> +static const struct oldi_bus_format oldi_bus_formats[] = {
> +	{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,	18, SPWG_18,	MEDIA_BUS_FMT_RGB666_1X18 },
> +	{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,	24, SPWG_24,	MEDIA_BUS_FMT_RGB888_1X24 },
> +	{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,	24, JEIDA_24,	MEDIA_BUS_FMT_RGB888_1X24 },
> +};
> +
> +static int tidss_oldi_get_port_pixels_type(ofnode port_node)
> +{
> +	bool even_pixels =
> +		ofnode_has_property(port_node, "dual-lvds-even-pixels");
> +	bool odd_pixels =
> +		ofnode_has_property(port_node, "dual-lvds-odd-pixels");
> +	return (even_pixels ? OLDI_PIXELS_EVEN : 0) |
> +	       (odd_pixels ? OLDI_PIXELS_ODD : 0);
> +}
> +
> +static int tidss_oldi_get_remote_pixels_type(ofnode port_node)
> +{
> +	ofnode endpoint = ofnode_null();
> +	int pixels_type = -EPIPE;
> +
> +	ofnode_for_each_subnode(endpoint, port_node) {
> +		ofnode remote_port;
> +		int current_pt;
> +
> +		if (!ofnode_name_eq(endpoint, "endpoint"))
> +			continue;
> +
> +		remote_port = ofnode_graph_get_remote_port(endpoint);
> +		if (!ofnode_valid(remote_port))
> +			return -EPIPE;
> +
> +		current_pt = tidss_oldi_get_port_pixels_type(remote_port);
> +		if (pixels_type < 0)
> +			pixels_type = current_pt;
> +
> +		if (!current_pt || pixels_type != current_pt)
> +			return -EINVAL;
> +	}
> +
> +	return pixels_type;
> +}
> +
> +int tidss_oldi_get_dual_link_pixel_order(ofnode port1, ofnode port2)
> +{
> +	int remote_p1_pt, remote_p2_pt;
> +
> +	if (!ofnode_valid(port1) || !ofnode_valid(port2))
> +		return -EINVAL;
> +
> +	remote_p1_pt = tidss_oldi_get_remote_pixels_type(port1);
> +	if (remote_p1_pt < 0)
> +		return remote_p1_pt;
> +
> +	remote_p2_pt = tidss_oldi_get_remote_pixels_type(port2);
> +	if (remote_p2_pt < 0)
> +		return remote_p2_pt;
> +
> +	/*
> +	 * A valid dual-lVDS bus is found when one remote port is marked with
> +	 * "dual-lvds-even-pixels", and the other remote port is marked with
> +	 * "dual-lvds-odd-pixels", bail out if the markers are not right.
> +	 */
> +	if (remote_p1_pt + remote_p2_pt != OLDI_PIXELS_EVEN + OLDI_PIXELS_ODD)
> +		return -EINVAL;
> +
> +	return remote_p1_pt == OLDI_PIXELS_EVEN ?
> +		TIDSS_OLDI_DUAL_LINK_EVEN_ODD_PIXELS :
> +		TIDSS_OLDI_DUAL_LINK_ODD_EVEN_PIXELS;
> +}
> +
> +static int get_oldi_mode(ofnode oldi_tx, u32 *companion_instance)
> +{
> +	ofnode companion;
> +	ofnode port0, port1;
> +	int pixel_order;
> +	int ret;
> +	/*
> +	 * Find if the OLDI is paired with another OLDI for combined OLDI
> +	 * operation (dual-lvds or clone).
> +	 */
> +	companion = ofnode_parse_phandle(oldi_tx, "ti,companion-oldi", 0);
> +	if (!ofnode_valid(companion)) {
> +		/*
> +		 * OLDI TXes in Single Link mode do not have companion
> +		 * OLDI TXes and, Secondary OLDI nodes don't need this
> +		 * information.
> +		 */
> +		if (ofnode_has_property(oldi_tx, "ti,secondary-oldi"))
> +			return OLDI_MODE_SECONDARY;
> +
> +		/*
> +		 * The OLDI TX does not have a companion, nor is it a
> +		 * secondary OLDI. It will operate independently.
> +		 */
> +		return OLDI_MODE_SINGLE_LINK;
> +	}
> +
> +	ret = ofnode_read_u32(companion, "reg", companion_instance);
> +	if (ret)
> +		return OLDI_MODE_UNSUPPORTED;
> +
> +	/*
> +	 * We need to work out if the sink is expecting us to function in
> +	 * dual-link mode. We do this by looking at the DT port nodes we are
> +	 * connected to, if they are marked as expecting even pixels and
> +	 * odd pixels than we need to enable vertical stripe output.
> +	 */
> +	port0 = ofnode_graph_get_port_by_id(oldi_tx, 1);
> +	port1 = ofnode_graph_get_port_by_id(companion, 1);
> +	pixel_order = tidss_oldi_get_dual_link_pixel_order(port0, port1);
> +	switch (pixel_order) {
> +	case -EINVAL:
> +		/*
> +		 * The dual link properties were not found in at least
> +		 * one of the sink nodes. Since 2 OLDI ports are present
> +		 * in the DT, it can be safely assumed that the required
> +		 * configuration is Clone Mode.
> +		 */
> +		return OLDI_MODE_CLONE_SINGLE_LINK;
> +
> +	case TIDSS_OLDI_DUAL_LINK_ODD_EVEN_PIXELS:
> +		return OLDI_MODE_DUAL_LINK;
> +
> +	/* Unsupported OLDI Modes */
> +	case TIDSS_OLDI_DUAL_LINK_EVEN_ODD_PIXELS:
> +	default:
> +		return OLDI_MODE_UNSUPPORTED;
> +	}
> +}
> +
> +static int get_parent_dss_vp(ofnode oldi_tx, u32 *parent_vp)
> +{
> +	ofnode ep, dss_port;
> +	int ret;
> +
> +	ep = ofnode_graph_get_endpoint_by_regs(oldi_tx, 0, -1);
> +	if (ofnode_valid(ep)) {
> +		dss_port = ofnode_graph_get_remote_port(ep);
> +		if (!ofnode_valid(dss_port))
> +			ret = -ENODEV;
> +
> +		ret = ofnode_read_u32(dss_port, "reg", parent_vp);
> +		if (ret)
> +			return -ENODEV;
> +		return 0;
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +static int tidss_init_oldi_io_ctrl(struct udevice *dev, struct tidss_oldi *tidss_oldi)
> +{
> +	struct udevice *syscon;
> +	struct regmap *regmap = NULL;
> +	int ret = 0;
> +
> +	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "ti,am65x-oldi-io-ctrl",
> +					   &syscon);
> +	if (ret) {
> +		debug("unable to find ti,am65x-oldi-io-ctrl syscon device (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* get grf-reg base address */
> +	regmap = syscon_get_regmap(syscon);
> +	if (!regmap) {
> +		debug("unable to find rockchip grf regmap\n");
> +		return -ENODEV;
> +	}
> +	tidss_oldi->io_ctrl = regmap;
> +	return 0;
> +}
> +
> +int tidss_oldi_init(struct udevice *dev)
> +{
> +	struct tidss_drv_priv *priv = dev_get_priv(dev);
> +	u32 parent_vp, oldi_instance, companion_instance;
> +	int ret, tidss_oldi_panel_count = 0;
> +	enum tidss_oldi_link_type link_type;
> +	struct tidss_oldi *tidss_oldi;
> +	struct clk *serial;
> +	ofnode child;
> +	ofnode oldi_parent = ofnode_find_subnode(dev_ofnode(dev), "oldi-transmitters");
> +
> +	if (!ofnode_valid(oldi_parent))
> +		/* Return gracefully */
> +		return 0;
> +
> +	ofnode_for_each_subnode(child, oldi_parent) {
> +		priv->oldis[tidss_oldi_panel_count] = NULL;
> +
> +		ret = get_parent_dss_vp(child, &parent_vp);
> +		if (ret == -ENODEV) {
> +			/*
> +			 * ENODEV means that this particular OLDI node
> +			 * is not connected with the DSS, which is not
> +			 * a harmful case. There could be another OLDI
> +			 * which may still be connected.
> +			 * Continue to search for that.
> +			 */
> +			ret = 0;
> +			continue;
> +		}
> +
> +		ret = ofnode_read_u32(child, "reg", &oldi_instance);
> +		if (ret) {
> +			ret = -ENODEV;
> +			break;
> +		}
> +
> +		link_type = get_oldi_mode(child, &companion_instance);
> +		if (link_type == OLDI_MODE_UNSUPPORTED) {
> +			dev_warn(dev, "OLDI%u: Unsupported OLDI connection.\n", oldi_instance);
> +
> +			ret = OLDI_MODE_UNSUPPORTED;
> +			/* Return gracefully, no supported OLDI panel found */
> +			break;
> +		} else if (link_type == OLDI_MODE_SECONDARY) {
> +			/*
> +			 * This is the secondary OLDI node, which serves as a
> +			 * companinon to the primary OLDI, when it is configured
> +			 * for the dual-lvds mode. Since the primary OLDI will
> +			 * be a part of bridge chain, no need to put this one
> +			 * too. Continue onto the next OLDI node.
> +			 */
> +			continue;
> +		}
> +
> +		serial = malloc(sizeof(struct clk));
> +		ret = clk_get_by_name_nodev(child, "serial", serial);
> +		if (ret) {
> +			dev_err(dev, "video port %d clock enable error %d\n", parent_vp, ret);
> +			free(serial);
> +			return ret;
> +		}
> +
> +		tidss_oldi = malloc(sizeof(struct tidss_oldi));
> +		ret = tidss_init_oldi_io_ctrl(dev, tidss_oldi);
> +		if (ret) {
> +			debug("tidss could not initialize oldi_io_ctrl\n");
> +			free(serial);
> +			free(tidss_oldi);
> +			return ret;
> +		}
> +
> +		tidss_oldi->dev = dev;
> +		tidss_oldi->parent_vp = parent_vp;
> +		tidss_oldi->oldi_instance = oldi_instance;
> +		tidss_oldi->companion_instance = companion_instance;
> +		tidss_oldi->link_type = link_type;
> +		tidss_oldi->serial = serial;
> +		priv->oldis[tidss_oldi_panel_count] = tidss_oldi;
> +		priv->oldi_mode = link_type;
> +		tidss_oldi_panel_count++;
> +	}
> +	priv->num_oldis = tidss_oldi_panel_count;
> +	return 0;
> +}
> diff --git a/drivers/video/tidss/tidss_oldi.h b/drivers/video/tidss/tidss_oldi.h
> new file mode 100644
> index 00000000000..82dcd72fc0e
> --- /dev/null
> +++ b/drivers/video/tidss/tidss_oldi.h
> @@ -0,0 +1,67 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/
> + * Swamil Jain <s-jain1 at ti.com>
> + *
> + * based on the linux tidss_oldi.h, which is
> + *
> + * Copyright (C) 2024 - Texas Instruments Incorporated
> + * Author: Aradhya Bhatia <a-bhatia1 at ti.com>
> + */
> +
> +#ifndef __TIDSS_OLDI_H__
> +#define __TIDSS_OLDI_H__
> +
> +#include <dm/ofnode.h>
> +#include <dm/of_access.h>
> +#include <media_bus_format.h>
> +
> +#include "tidss_drv.h"
> +
> +/* OLDI PORTS */
> +#define OLDI_INPUT_PORT    0
> +#define OLDI_OURPUT_PORT   1
> +
> +/* Control MMR Registers */
> +
> +/* Register offsets */
> +#define OLDI_PD_CTRL            0x100
> +#define OLDI_LB_CTRL            0x104
> +
> +/* Power control bits */
> +#define OLDI_PWRDOWN_TX(n)	BIT(n)
> +
> +/* LVDS Bandgap reference Enable/Disable */
> +#define OLDI_PWRDN_BG		BIT(8)
> +
> +enum tidss_oldi_link_type {
> +	OLDI_MODE_UNSUPPORTED,
> +	OLDI_MODE_SINGLE_LINK,
> +	OLDI_MODE_CLONE_SINGLE_LINK,
> +	OLDI_MODE_DUAL_LINK,
> +	OLDI_MODE_SECONDARY,
> +};
> +
> +struct oldi_bus_format {
> +	u32 bus_fmt;
> +	u32 data_width;
> +	enum oldi_mode_reg_val oldi_mode_reg_val;
> +	u32 input_bus_fmt;
> +};
> +
> +struct tidss_oldi {
> +	struct udevice *dev;
> +
> +	enum tidss_oldi_link_type link_type;
> +	const struct oldi_bus_format *bus_format;
> +	u32 oldi_instance;
> +	u32 companion_instance;
> +	u32 parent_vp;
> +
> +	struct clk *serial;
> +	struct regmap *io_ctrl;
> +};
> +
> +int tidss_oldi_init(struct udevice *dev);
> +
> +#endif /* __TIDSS_OLDI_H__ */


More information about the U-Boot mailing list