[PATCH v3 12/15] drm/tidss: oldi: Convert OLDI to an aux driver
Swamil Jain
s-jain1 at ti.com
Wed Jun 24 03:49:36 PDT 2026
On 29-05-2026 14:15, Tomi Valkeinen wrote:
> Currently in the DT, OLDI is defined in child nodes under the DSS node.
> The tidss driver will parse the DT, and create DRM bridges for the
> OLDIs, and there are no Linux devices for the OLDIs.
>
> On new SoCs the OLDIs have their own power-domains which we need to
> control. The cleanest way to do this in DT is to add the PDs to the OLDI
> nodes. But this means the OLDI bridge code would somehow have to
> manually manage the PDs, the PDs not being under a Linux device, and
> there isn't much support for that kind of setup in the PD framework.
>
> A solution to this is to convert the OLDI to an auxiliary device/driver,
> created by tidss:
>
> - At module load time the tidss module will, in addition to registering
> the tidss DRM driver, register an oldi auxiliary driver.
> - At probe time tidss will parse the DT, and create an auxiliary device
> for each OLDI.
>
> The aux driver will probe, and as its of_node points to the OLDI node
> containing the PD, the driver framework will take care of enabling and
> disabling the PD when OLDI is used ("used" as in pm_runtime context).
>
> Tested-by: Swamil Jain <s-jain1 at ti.com>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> ---
Reviewed-by: Swamil Jain <s-jain1 at ti.com>
> drivers/gpu/drm/tidss/tidss_drv.c | 51 +++-
> drivers/gpu/drm/tidss/tidss_drv.h | 5 +-
> drivers/gpu/drm/tidss/tidss_oldi.c | 502 ++++++++++++++++++++++++++-----------
> drivers/gpu/drm/tidss/tidss_oldi.h | 7 +-
> 4 files changed, 405 insertions(+), 160 deletions(-)
>
> diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c
> index 5cb3e746aeb3..aef945101be4 100644
> --- a/drivers/gpu/drm/tidss/tidss_drv.c
> +++ b/drivers/gpu/drm/tidss/tidss_drv.c
> @@ -133,10 +133,6 @@ static int tidss_probe(struct platform_device *pdev)
> return ret;
> }
>
> - ret = tidss_oldi_init(tidss);
> - if (ret)
> - return dev_err_probe(dev, ret, "failed to init OLDI\n");
> -
> pm_runtime_enable(dev);
>
> pm_runtime_set_autosuspend_delay(dev, 1000);
> @@ -147,24 +143,30 @@ static int tidss_probe(struct platform_device *pdev)
> dispc_runtime_resume(tidss->dispc);
> #endif
>
> + ret = tidss_oldi_create_devices(tidss);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to create OLDI devices\n");
> + goto err_runtime_suspend;
> + }
> +
> ret = tidss_modeset_init(tidss);
> if (ret < 0) {
> if (ret != -EPROBE_DEFER)
> dev_err(dev, "failed to init DRM/KMS (%d)\n", ret);
> - goto err_runtime_suspend;
> + goto err_destroy_oldis;
> }
>
> irq = platform_get_irq(pdev, 0);
> if (irq < 0) {
> ret = irq;
> - goto err_runtime_suspend;
> + goto err_destroy_oldis;
> }
> tidss->irq = irq;
>
> ret = tidss_irq_install(ddev, irq);
> if (ret) {
> dev_err(dev, "tidss_irq_install failed: %d\n", ret);
> - goto err_runtime_suspend;
> + goto err_destroy_oldis;
> }
>
> drm_kms_helper_poll_init(ddev);
> @@ -194,6 +196,9 @@ static int tidss_probe(struct platform_device *pdev)
> err_irq_uninstall:
> tidss_irq_uninstall(ddev);
>
> +err_destroy_oldis:
> + tidss_oldi_destroy_devices(tidss);
> +
> err_runtime_suspend:
> #ifndef CONFIG_PM
> dispc_runtime_suspend(tidss->dispc);
> @@ -201,8 +206,6 @@ static int tidss_probe(struct platform_device *pdev)
> pm_runtime_dont_use_autosuspend(dev);
> pm_runtime_disable(dev);
>
> - tidss_oldi_deinit(tidss);
> -
> return ret;
> }
>
> @@ -218,6 +221,8 @@ static void tidss_remove(struct platform_device *pdev)
>
> tidss_irq_uninstall(ddev);
>
> + tidss_oldi_destroy_devices(tidss);
> +
> #ifndef CONFIG_PM
> /* If we don't have PM, we need to call suspend manually */
> dispc_runtime_suspend(tidss->dispc);
> @@ -225,8 +230,6 @@ static void tidss_remove(struct platform_device *pdev)
> pm_runtime_dont_use_autosuspend(dev);
> pm_runtime_disable(dev);
>
> - tidss_oldi_deinit(tidss);
> -
> /* devm allocated dispc goes away with the dev so mark it NULL */
> dispc_remove(tidss);
>
> @@ -262,7 +265,31 @@ static struct platform_driver tidss_platform_driver = {
> },
> };
>
> -drm_module_platform_driver(tidss_platform_driver);
> +static int __init tidss_platform_driver_init(void)
> +{
> + int ret;
> +
> + ret = tidss_oldi_register_driver();
> + if (ret)
> + return ret;
> +
> + ret = drm_platform_driver_register(&tidss_platform_driver);
> + if (ret) {
> + tidss_oldi_unregister_driver();
> + return ret;
> + }
> +
> + return 0;
> +}
> +module_init(tidss_platform_driver_init);
> +
> +static void __exit tidss_platform_driver_exit(void)
> +{
> + platform_driver_unregister(&tidss_platform_driver);
> + tidss_oldi_unregister_driver();
> +}
> +module_exit(tidss_platform_driver_exit);
> +
>
> MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen at ti.com>");
> MODULE_DESCRIPTION("TI Keystone DSS Driver");
> diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
> index e1c1f41d8b4b..d3dba639b278 100644
> --- a/drivers/gpu/drm/tidss/tidss_drv.h
> +++ b/drivers/gpu/drm/tidss/tidss_drv.h
> @@ -16,7 +16,8 @@
> #define TIDSS_MAX_OLDI_TXES 2
>
> typedef u32 dispc_irq_t;
> -struct tidss_oldi;
> +
> +struct auxiliary_device;
>
> struct tidss_device {
> struct drm_device ddev; /* DRM device for DSS */
> @@ -34,7 +35,7 @@ struct tidss_device {
> struct drm_plane *planes[TIDSS_MAX_PLANES];
>
> unsigned int num_oldis;
> - struct tidss_oldi *oldis[TIDSS_MAX_OLDI_TXES];
> + struct auxiliary_device *oldis[TIDSS_MAX_OLDI_TXES];
>
> unsigned int irq;
>
> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
> index e925ddaa4fd6..188f31b57c6a 100644
> --- a/drivers/gpu/drm/tidss/tidss_oldi.c
> +++ b/drivers/gpu/drm/tidss/tidss_oldi.c
> @@ -5,11 +5,13 @@
> * Aradhya Bhatia <a-bhatia1 at ti.com>
> */
>
> +#include <linux/auxiliary_bus.h>
> #include <linux/clk.h>
> #include <linux/of.h>
> #include <linux/of_graph.h>
> #include <linux/mfd/syscon.h>
> #include <linux/media-bus-format.h>
> +#include <linux/pm_runtime.h>
> #include <linux/regmap.h>
>
> #include <drm/drm_atomic_helper.h>
> @@ -20,6 +22,12 @@
> #include "tidss_dispc_regs.h"
> #include "tidss_oldi.h"
>
> +static DEFINE_IDA(tidss_oldi_ida);
> +
> +struct tidss_oldi_platform_data {
> + struct tidss_device *tidss;
> +};
> +
> struct tidss_oldi {
> struct tidss_device *tidss;
> struct device *dev;
> @@ -251,6 +259,8 @@ static void tidss_oldi_atomic_pre_enable(struct drm_bridge *bridge,
> if (oldi->link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK)
> return;
>
> + WARN_ON(pm_runtime_get_sync(oldi->dev) < 0);
> +
> connector = drm_atomic_get_new_connector_for_encoder(state,
> bridge->encoder);
> if (WARN_ON(!connector))
> @@ -296,6 +306,8 @@ static void tidss_oldi_atomic_post_disable(struct drm_bridge *bridge,
>
> /* Clear OLDI Config */
> tidss_disable_oldi(oldi->tidss, oldi->parent_vp);
> +
> + pm_runtime_put_autosuspend(oldi->dev);
> }
>
> #define MAX_INPUT_SEL_FORMATS 1
> @@ -383,12 +395,16 @@ static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
> */
> return OLDI_MODE_SINGLE_LINK;
>
> - if (of_property_read_u32(companion, "reg", &companion_reg))
> + if (of_property_read_u32(companion, "reg", &companion_reg)) {
> + of_node_put(companion);
> return OLDI_MODE_UNSUPPORTED;
> + }
>
> - if (companion_reg > (TIDSS_MAX_OLDI_TXES - 1))
> + if (companion_reg > (TIDSS_MAX_OLDI_TXES - 1)) {
> /* Invalid companion OLDI reg value. */
> + of_node_put(companion);
> return OLDI_MODE_UNSUPPORTED;
> + }
>
> *companion_instance = (int)companion_reg;
>
> @@ -464,174 +480,372 @@ static int get_parent_dss_vp(struct device_node *oldi_tx, u32 *parent_vp)
> return -ENODEV;
> }
>
> -void tidss_oldi_deinit(struct tidss_device *tidss)
> +static int tidss_oldi_probe(struct auxiliary_device *auxdev,
> + const struct auxiliary_device_id *id)
> {
> - for (int i = 0; i < tidss->num_oldis; i++) {
> - if (tidss->oldis[i]) {
> - drm_bridge_remove(&tidss->oldis[i]->bridge);
> - tidss->is_ext_vp_clk[tidss->oldis[i]->parent_vp] = false;
> - tidss->oldis[i] = NULL;
> + struct device *dev = &auxdev->dev;
> + struct tidss_oldi_platform_data *oldi_pdata = dev_get_platdata(dev);
> + struct tidss_device *tidss = oldi_pdata->tidss;
> + struct device_node *node = auxdev->dev.of_node;
> + enum tidss_oldi_link_type link_type = OLDI_MODE_UNSUPPORTED;
> + int companion_instance = -1;
> + struct drm_bridge *bridge;
> + struct device_link *link;
> + struct tidss_oldi *oldi;
> + u32 oldi_instance;
> + u32 parent_vp;
> + int ret;
> +
> + ret = of_property_read_u32(node, "reg", &oldi_instance);
> + if (ret)
> + return ret;
> +
> + ret = get_parent_dss_vp(node, &parent_vp);
> + if (ret)
> + return ret;
> +
> + /*
> + * Now that it's confirmed that OLDI is connected with DSS,
> + * let's continue getting the OLDI sinks ahead and other OLDI
> + * properties.
> + */
> + bridge = devm_drm_of_get_bridge(dev, node, OLDI_OUTPUT_PORT, 0);
> + if (IS_ERR(bridge)) {
> + /*
> + * Either there was no OLDI sink in the devicetree, or the OLDI
> + * sink has not been added yet. In any case, return.
> + */
> + return dev_err_probe(dev, PTR_ERR(bridge),
> + "no panel/bridge for OLDI%u.\n",
> + oldi_instance);
> + }
> +
> + link_type = get_oldi_mode(node, &companion_instance);
> + if (link_type == OLDI_MODE_UNSUPPORTED) {
> + return dev_err_probe(dev, -EINVAL,
> + "OLDI%u: Unsupported OLDI connection.\n",
> + oldi_instance);
> + } else if ((link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) ||
> + (link_type == OLDI_MODE_CLONE_SINGLE_LINK)) {
> + /*
> + * The OLDI driver cannot support OLDI clone mode properly at
> + * present. The clone mode requires 2 working encoder-bridge
> + * pipelines, generating from the same crtc. The DRM framework
> + * does not support this at present. If there were to be, say, 2
> + * OLDI sink bridges each connected to an OLDI TXes, they
> + * couldn't both be supported simultaneously. This driver still
> + * has some code pertaining to OLDI clone mode configuration in
> + * DSS hardware for future, when there is a better
> + * infrastructure in the DRM framework to support 2
> + * encoder-bridge pipelines simultaneously. Till that time, this
> + * driver shall error out if it detects a clone mode
> + * configuration.
> + */
> + return dev_err_probe(dev, -EOPNOTSUPP,
> + "The OLDI driver does not support Clone Mode at present.\n");
> + }
> +
> + oldi = devm_drm_bridge_alloc(dev, struct tidss_oldi, bridge,
> + &tidss_oldi_bridge_funcs);
> + if (IS_ERR(oldi))
> + return PTR_ERR(oldi);
> +
> + oldi->parent_vp = parent_vp;
> + oldi->oldi_instance = oldi_instance;
> + oldi->companion_instance = companion_instance;
> + oldi->link_type = link_type;
> + oldi->dev = dev;
> + oldi->next_bridge = bridge;
> + oldi->tidss = tidss;
> +
> + auxiliary_set_drvdata(auxdev, oldi);
> +
> + /*
> + * Only the primary OLDI needs to reference the io-ctrl system
> + * registers, and the serial clock.
> + * We don't require a check for secondary OLDI in dual-link mode
> + * because the driver will not create a drm_bridge instance.
> + * But the driver will need to create a drm_bridge instance,
> + * for secondary OLDI in clone mode (once it is supported).
> + */
> + if (link_type != OLDI_MODE_SECONDARY_DUAL_LINK &&
> + link_type != OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) {
> + oldi->io_ctrl = syscon_regmap_lookup_by_phandle(node,
> + "ti,oldi-io-ctrl");
> + if (IS_ERR(oldi->io_ctrl)) {
> + return dev_err_probe(oldi->dev, PTR_ERR(oldi->io_ctrl),
> + "OLDI%u: syscon_regmap_lookup_by_phandle failed.\n",
> + oldi_instance);
> + }
> +
> + oldi->serial = of_clk_get_by_name(node, "serial");
> + if (IS_ERR(oldi->serial)) {
> + return dev_err_probe(oldi->dev, PTR_ERR(oldi->serial),
> + "OLDI%u: Failed to get serial clock.\n",
> + oldi_instance);
> }
> }
> +
> + if (link_type != OLDI_MODE_SECONDARY_DUAL_LINK) {
> + /* Register the bridge. */
> + oldi->bridge.of_node = node;
> + oldi->bridge.driver_private = oldi;
> +
> + tidss->is_ext_vp_clk[oldi->parent_vp] = true;
> +
> + drm_bridge_add(&oldi->bridge);
> + }
> +
> + link = device_link_add(&auxdev->dev, tidss->dev,
> + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER);
> + if (!link) {
> + ret = -EINVAL;
> + goto err_bridge_remove;
> + }
> +
> + pm_runtime_enable(dev);
> + pm_runtime_set_autosuspend_delay(dev, 500);
> + pm_runtime_use_autosuspend(dev);
> +
> + return 0;
> +
> +err_bridge_remove:
> + if (link_type != OLDI_MODE_SECONDARY_DUAL_LINK) {
> + drm_bridge_remove(&oldi->bridge);
> + tidss->is_ext_vp_clk[oldi->parent_vp] = false;
> + }
> +
> + clk_put(oldi->serial);
> +
> + return ret;
> }
>
> -int tidss_oldi_init(struct tidss_device *tidss)
> +static void tidss_oldi_remove(struct auxiliary_device *auxdev)
> {
> - struct tidss_oldi *oldi;
> - struct device_node *child;
> - struct drm_bridge *bridge;
> - u32 parent_vp, oldi_instance;
> - int companion_instance = -1;
> - enum tidss_oldi_link_type link_type = OLDI_MODE_UNSUPPORTED;
> - struct device_node *oldi_parent;
> - int ret = 0;
> + struct tidss_oldi *oldi = auxiliary_get_drvdata(auxdev);
> + struct tidss_device *tidss = oldi->tidss;
> + struct device *dev = &auxdev->dev;
>
> - tidss->num_oldis = 0;
> + pm_runtime_dont_use_autosuspend(dev);
> + pm_runtime_disable(dev);
>
> - oldi_parent = of_get_child_by_name(tidss->dev->of_node, "oldi-transmitters");
> - if (!oldi_parent)
> - /* Return gracefully */
> - return 0;
> + if (oldi->link_type != OLDI_MODE_SECONDARY_DUAL_LINK) {
> + drm_bridge_remove(&oldi->bridge);
> +
> + tidss->is_ext_vp_clk[oldi->parent_vp] = false;
> + }
> +
> + clk_put(oldi->serial);
> +}
> +
> +static const struct auxiliary_device_id tidss_oldi_aux_id_table[] = {
> + { .name = "tidss.oldi" },
> + {}
> +};
> +
> +static struct auxiliary_driver oldi_aux_driver = {
> + .name = "oldi",
> + .probe = tidss_oldi_probe,
> + .remove = tidss_oldi_remove,
> + .id_table = tidss_oldi_aux_id_table,
> +};
> +
> +static void tidss_oldi_aux_device_release(struct device *dev)
> +{
> + struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
> +
> + ida_free(&tidss_oldi_ida, auxdev->id);
> +
> + of_node_put(auxdev->dev.of_node);
>
> - for_each_available_child_of_node(oldi_parent, child) {
> - ret = get_parent_dss_vp(child, &parent_vp);
> - if (ret) {
> - 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.
> - */
> - continue;
> + kfree(auxdev->dev.platform_data);
> + kfree(auxdev);
> +}
> +
> +static struct auxiliary_device *
> +tidss_oldi_create_device(struct tidss_device *tidss,
> + struct device_node *oldi_tx)
> +{
> + struct tidss_oldi_platform_data *oldi_pdata;
> + struct auxiliary_device *companion_auxdev;
> + struct auxiliary_device *auxdev;
> + struct device_link *link = NULL;
> + u32 oldi_aux_id;
> + int ret;
> +
> + /*
> + * Allocate the ID first, so that we get a lower ID for the primary
> + * OLDI, instead of the companion grabbing it in the call below. Note
> + * that the ID allocated here often matches the OLDI hardware index,
> + * but not always.
> + * The OLDI hardware index cannot be used as an ID, as, say, OLDI 1 on
> + * two DSS instances would produce the exact same device name
> + * ("tidss.oldi.1").
> + */
> + ret = ida_alloc(&tidss_oldi_ida, GFP_KERNEL);
> + if (ret < 0)
> + return ERR_PTR(ret);
> +
> + oldi_aux_id = ret;
> +
> + companion_auxdev = NULL;
> +
> + /*
> + * If this is the primary OLDI and there is a companion, create the
> + * auxdev for the secondary OLDI first, as the secondary will act as
> + * a supplier for the primary OLDI.
> + */
> + if (!of_property_read_bool(oldi_tx, "ti,secondary-oldi")) {
> + struct device_node *companion_node;
> +
> + companion_node = of_parse_phandle(oldi_tx, "ti,companion-oldi", 0);
> + if (companion_node) {
> + companion_auxdev =
> + tidss_oldi_create_device(tidss, companion_node);
> +
> + of_node_put(companion_node);
> +
> + if (IS_ERR(companion_auxdev)) {
> + dev_err(tidss->dev,
> + "Failed to create secondary oldi device\n");
> + ret = PTR_ERR(companion_auxdev);
> + goto err_free_ida;
> }
> - goto err_put_node;
> }
> + }
>
> - ret = of_property_read_u32(child, "reg", &oldi_instance);
> - if (ret)
> - goto err_put_node;
> + oldi_pdata = kzalloc_obj(*oldi_pdata);
> + if (!oldi_pdata) {
> + ret = -ENOMEM;
> + goto err_free_ida;
> + }
>
> - /*
> - * Now that it's confirmed that OLDI is connected with DSS,
> - * let's continue getting the OLDI sinks ahead and other OLDI
> - * properties.
> - */
> - bridge = devm_drm_of_get_bridge(tidss->dev, child,
> - OLDI_OUTPUT_PORT, 0);
> - if (IS_ERR(bridge)) {
> - /*
> - * Either there was no OLDI sink in the devicetree, or
> - * the OLDI sink has not been added yet. In any case,
> - * return.
> - * We don't want to have an OLDI node connected to DSS
> - * but not to any sink.
> - */
> - ret = dev_err_probe(tidss->dev, PTR_ERR(bridge),
> - "no panel/bridge for OLDI%u.\n",
> - oldi_instance);
> - goto err_put_node;
> - }
> + oldi_pdata->tidss = tidss;
>
> - link_type = get_oldi_mode(child, &companion_instance);
> - if (link_type == OLDI_MODE_UNSUPPORTED) {
> - ret = dev_err_probe(tidss->dev, -EINVAL,
> - "OLDI%u: Unsupported OLDI connection.\n",
> - oldi_instance);
> - goto err_put_node;
> - } else if ((link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) ||
> - (link_type == OLDI_MODE_CLONE_SINGLE_LINK)) {
> - /*
> - * The OLDI driver cannot support OLDI clone mode
> - * properly at present.
> - * The clone mode requires 2 working encoder-bridge
> - * pipelines, generating from the same crtc. The DRM
> - * framework does not support this at present. If
> - * there were to be, say, 2 OLDI sink bridges each
> - * connected to an OLDI TXes, they couldn't both be
> - * supported simultaneously.
> - * This driver still has some code pertaining to OLDI
> - * clone mode configuration in DSS hardware for future,
> - * when there is a better infrastructure in the DRM
> - * framework to support 2 encoder-bridge pipelines
> - * simultaneously.
> - * Till that time, this driver shall error out if it
> - * detects a clone mode configuration.
> - */
> - ret = dev_err_probe(tidss->dev, -EOPNOTSUPP,
> - "The OLDI driver does not support Clone Mode at present.\n");
> - goto err_put_node;
> - } else if (link_type == OLDI_MODE_SECONDARY_DUAL_LINK) {
> - /*
> - * This is the secondary OLDI node, which serves as a
> - * companion to the primary OLDI, when it is configured
> - * for the dual-link 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;
> - }
> + auxdev = kzalloc_obj(*auxdev);
> + if (!auxdev) {
> + ret = -ENOMEM;
> + goto err_free_pdata;
> + }
> +
> + *auxdev = (struct auxiliary_device) {
> + .name = "oldi",
> + .id = oldi_aux_id,
> + .dev = {
> + .parent = tidss->dev,
> + .of_node = of_node_get(oldi_tx),
> + .release = tidss_oldi_aux_device_release,
> + .platform_data = oldi_pdata,
> + },
> + };
> +
> + ret = auxiliary_device_init(auxdev);
> + if (ret) {
> + dev_err(tidss->dev, "OLDI auxiliary_device_init failed: %d\n",
> + ret);
> + goto err_free_auxdev;
> + }
>
> - oldi = devm_drm_bridge_alloc(tidss->dev, struct tidss_oldi, bridge,
> - &tidss_oldi_bridge_funcs);
> - if (IS_ERR(oldi)) {
> - ret = PTR_ERR(oldi);
> - goto err_put_node;
> + /*
> + * Create a device-link between the primary and the secondary, so that
> + * the secondary will be powered on when the primary is used.
> + */
> + if (companion_auxdev) {
> + link = device_link_add(&auxdev->dev, &companion_auxdev->dev,
> + DL_FLAG_PM_RUNTIME |
> + DL_FLAG_AUTOREMOVE_CONSUMER |
> + DL_FLAG_AUTOREMOVE_SUPPLIER);
> + if (!link) {
> + dev_err(tidss->dev,
> + "device_link_add failed between primary and secondary OLDI\n");
> + ret = -EINVAL;
> + goto err_uninit_auxdev;
> }
> + }
>
> - oldi->parent_vp = parent_vp;
> - oldi->oldi_instance = oldi_instance;
> - oldi->companion_instance = companion_instance;
> - oldi->link_type = link_type;
> - oldi->dev = tidss->dev;
> - oldi->next_bridge = bridge;
> + ret = auxiliary_device_add(auxdev);
> + if (ret) {
> + dev_err(tidss->dev, "OLDI auxiliary_device_add failed: %d\n",
> + ret);
> + goto err_link_del;
> + }
>
> - /*
> - * Only the primary OLDI needs to reference the io-ctrl system
> - * registers, and the serial clock.
> - * We don't require a check for secondary OLDI in dual-link mode
> - * because the driver will not create a drm_bridge instance.
> - * But the driver will need to create a drm_bridge instance,
> - * for secondary OLDI in clone mode (once it is supported).
> - */
> - if (link_type != OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) {
> - oldi->io_ctrl = syscon_regmap_lookup_by_phandle(child,
> - "ti,oldi-io-ctrl");
> - if (IS_ERR(oldi->io_ctrl)) {
> - ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->io_ctrl),
> - "OLDI%u: syscon_regmap_lookup_by_phandle failed.\n",
> - oldi_instance);
> - goto err_put_node;
> - }
> + tidss->oldis[tidss->num_oldis++] = auxdev;
>
> - oldi->serial = of_clk_get_by_name(child, "serial");
> - if (IS_ERR(oldi->serial)) {
> - ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->serial),
> - "OLDI%u: Failed to get serial clock.\n",
> - oldi_instance);
> - goto err_put_node;
> - }
> - }
> + return auxdev;
>
> - /* Register the bridge. */
> - oldi->bridge.of_node = child;
> - oldi->bridge.driver_private = oldi;
> +err_link_del:
> + if (link)
> + device_link_del(link);
> +err_uninit_auxdev:
> + auxiliary_device_uninit(auxdev);
> + /* return here, as the rest are done in auxdev's release */
> + return ERR_PTR(ret);
>
> - tidss->oldis[tidss->num_oldis++] = oldi;
> - tidss->is_ext_vp_clk[oldi->parent_vp] = true;
> - oldi->tidss = tidss;
> +err_free_auxdev:
> + kfree(auxdev);
> +err_free_pdata:
> + kfree(oldi_pdata);
> +err_free_ida:
> + ida_free(&tidss_oldi_ida, oldi_aux_id);
>
> - drm_bridge_add(&oldi->bridge);
> + return ERR_PTR(ret);
> +}
> +
> +int tidss_oldi_create_devices(struct tidss_device *tidss)
> +{
> + struct device_node *oldi_txes;
> + struct device_node *oldi_tx;
> + int ret;
> +
> + oldi_txes = of_get_child_by_name(tidss->dev->of_node,
> + "oldi-transmitters");
> + if (!oldi_txes)
> + return 0;
> +
> + /*
> + * Look for primary OLDIs and create devices for them. For dual-link
> + * cases, the primary's create_device call will also create the
> + * secondary device.
> + */
> + for_each_available_child_of_node(oldi_txes, oldi_tx) {
> + struct auxiliary_device *auxdev;
> +
> + if (of_property_read_bool(oldi_tx, "ti,secondary-oldi"))
> + continue;
> +
> + auxdev = tidss_oldi_create_device(tidss, oldi_tx);
> + if (IS_ERR(auxdev)) {
> + ret = PTR_ERR(auxdev);
> + goto err_destroy_oldis;
> + }
> }
>
> - of_node_put(child);
> - of_node_put(oldi_parent);
> + of_node_put(oldi_txes);
>
> return 0;
>
> -err_put_node:
> - of_node_put(child);
> - of_node_put(oldi_parent);
> +err_destroy_oldis:
> + tidss_oldi_destroy_devices(tidss);
> +
> + of_node_put(oldi_tx);
> + of_node_put(oldi_txes);
> +
> return ret;
> }
> +
> +void tidss_oldi_destroy_devices(struct tidss_device *tidss)
> +{
> + for (unsigned int i = 0; i < tidss->num_oldis; ++i)
> + auxiliary_device_destroy(tidss->oldis[i]);
> +}
> +
> +int tidss_oldi_register_driver(void)
> +{
> + return auxiliary_driver_register(&oldi_aux_driver);
> +}
> +
> +void tidss_oldi_unregister_driver(void)
> +{
> + auxiliary_driver_unregister(&oldi_aux_driver);
> +}
> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.h b/drivers/gpu/drm/tidss/tidss_oldi.h
> index a361e6dbfce3..2069bd46aaae 100644
> --- a/drivers/gpu/drm/tidss/tidss_oldi.h
> +++ b/drivers/gpu/drm/tidss/tidss_oldi.h
> @@ -36,7 +36,10 @@ enum tidss_oldi_link_type {
> OLDI_MODE_SECONDARY_DUAL_LINK,
> };
>
> -int tidss_oldi_init(struct tidss_device *tidss);
> -void tidss_oldi_deinit(struct tidss_device *tidss);
> +int tidss_oldi_create_devices(struct tidss_device *tidss);
> +void tidss_oldi_destroy_devices(struct tidss_device *tidss);
> +
> +int tidss_oldi_register_driver(void);
> +void tidss_oldi_unregister_driver(void);
>
> #endif /* __TIDSS_OLDI_H__ */
>
More information about the linux-arm-kernel
mailing list