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 <[email protected]>");
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 <[email protected]>
*/
+#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__ */