Bläddra i källkod

video: driver: add asynchronous probe support

Added async probe for video driver to reduce bootup latency.

In insmod/probe sequence, registered all video sub-devices with
component_model framework, so that once all sub-device probe
completes master/vidc node bind() callback willbe called. So as
part of bind, video driver will do core_init sequence. Due to
async probe all sub-device probe willbe called asynchronously
from different threads.

During rmmod/remove sequence master/vidc node may not be called
first and instead child node remove might be called, which will
intern calls component_del() api. 1st call to this api will
internally invoke unbind() callback of master. So as part of
unbind() video driver will do core_deinit() sequence.

of_platform_depopulate() will act as sync point on master node.
Which will make sure all residual sub-device removal completion.
So it will guarantee video device/master cleanup happens only
after all sub-devices removal.

Change-Id: Ia40d4b1aa54633ef26fff2c207da78f15e305363
Signed-off-by: Govindaraj Rajagopal <[email protected]>
Govindaraj Rajagopal 3 år sedan
förälder
incheckning
12adb66124
1 ändrade filer med 121 tillägg och 17 borttagningar
  1. 121 17
      driver/vidc/src/msm_vidc_probe.c

+ 121 - 17
driver/vidc/src/msm_vidc_probe.c

@@ -8,6 +8,7 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/component.h>
 #include <linux/interrupt.h>
 
 #include "msm_vidc_internal.h"
@@ -341,6 +342,88 @@ exit:
 	return rc;
 }
 
+static int msm_vidc_component_compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static void msm_vidc_component_release_of(struct device *dev, void *data)
+{
+	d_vpr_h("%s(): %s\n", __func__, of_node_full_name(data));
+	of_node_put(data);
+}
+
+static int msm_vidc_component_cb_bind(struct device *dev,
+	struct device *master, void *data)
+{
+	d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+	return 0;
+}
+
+static void msm_vidc_component_cb_unbind(struct device *dev,
+	struct device *master, void *data)
+{
+	d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+}
+
+static int msm_vidc_component_bind(struct device *dev)
+{
+	struct msm_vidc_core *core = dev_get_drvdata(dev);
+	int rc = 0;
+
+	d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+
+	rc = component_bind_all(dev, core);
+	if (rc) {
+		d_vpr_e("%s: sub-device bind failed\n", __func__);
+		return rc;
+	}
+
+	rc = venus_hfi_interface_queues_init(core);
+	if (rc) {
+		d_vpr_e("%s: interface queues init failed\n", __func__);
+		goto queues_deinit;
+	}
+
+	rc = msm_vidc_core_init(core);
+	if (rc) {
+		d_vpr_e("%s: sys init failed\n", __func__);
+		goto queues_deinit;
+	}
+
+	d_vpr_h("%s(): succssful\n", __func__);
+
+	return rc;
+
+queues_deinit:
+	venus_hfi_interface_queues_deinit(core);
+	component_unbind_all(dev, core);
+	return rc;
+}
+
+static void msm_vidc_component_unbind(struct device *dev)
+{
+	struct msm_vidc_core *core = dev_get_drvdata(dev);
+
+	d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+
+	msm_vidc_core_deinit(core, true);
+	venus_hfi_interface_queues_deinit(core);
+	component_unbind_all(dev, core);
+
+	d_vpr_h("%s(): succssful\n", __func__);
+}
+
+static const struct component_ops msm_vidc_component_cb_ops = {
+	.bind           = msm_vidc_component_cb_bind,
+	.unbind         = msm_vidc_component_cb_unbind,
+};
+
+static const struct component_master_ops msm_vidc_component_ops = {
+	.bind           = msm_vidc_component_bind,
+	.unbind         = msm_vidc_component_unbind,
+};
+
 static int msm_vidc_remove_video_device(struct platform_device *pdev)
 {
 	struct msm_vidc_core* core;
@@ -357,9 +440,8 @@ static int msm_vidc_remove_video_device(struct platform_device *pdev)
 
 	d_vpr_h("%s()\n", __func__);
 
-	msm_vidc_core_deinit(core, true);
-
-	venus_hfi_interface_queues_deinit(core);
+	/* destroy component master and deallocate match data */
+	component_master_del(&pdev->dev, &msm_vidc_component_ops);
 
 	d_vpr_h("depopulating sub devices\n");
 	/*
@@ -396,6 +478,7 @@ static int msm_vidc_remove_video_device(struct platform_device *pdev)
 	debugfs_remove_recursive(core->debugfs_parent);
 	msm_vidc_vmem_free((void **)&core);
 	g_core = NULL;
+	d_vpr_h("%s(): succssful\n", __func__);
 
 	return 0;
 }
@@ -405,6 +488,8 @@ static int msm_vidc_remove_context_bank(struct platform_device *pdev)
 	d_vpr_h("%s(): Detached %s and destroyed mapping\n",
 		__func__, dev_name(&pdev->dev));
 
+	component_del(&pdev->dev, &msm_vidc_component_cb_ops);
+
 	return 0;
 }
 
@@ -430,8 +515,10 @@ static int msm_vidc_remove(struct platform_device *pdev)
 static int msm_vidc_probe_video_device(struct platform_device *pdev)
 {
 	int rc = 0;
+	struct component_match *match = NULL;
 	struct msm_vidc_core *core = NULL;
-	int nr = BASE_DEVICE_NUMBER;
+	struct device_node *child = NULL;
+	int sub_device_count = 0, nr = BASE_DEVICE_NUMBER;
 
 	d_vpr_h("%s()\n", __func__);
 
@@ -535,7 +622,21 @@ static int msm_vidc_probe_video_device(struct platform_device *pdev)
 	if (!core->debugfs_root)
 		d_vpr_h("Failed to init debugfs core\n");
 
-	d_vpr_h("populating sub devices\n");
+	/* registering sub-device with component model framework */
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		sub_device_count++;
+		of_node_get(child);
+		component_match_add_release(&pdev->dev, &match, msm_vidc_component_release_of,
+			msm_vidc_component_compare_of, child);
+		if (IS_ERR(match)) {
+			of_node_put(child);
+			rc = PTR_ERR(match) ? PTR_ERR(match) : -ENOMEM;
+			d_vpr_e("%s: component match add release failed\n", __func__);
+			goto sub_dev_failed;
+		}
+	}
+
+	d_vpr_h("populating sub devices. count %d\n", sub_device_count);
 	/*
 	 * Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank.
 	 * When msm_vidc_probe is called for each sub-device, parse the
@@ -549,24 +650,18 @@ static int msm_vidc_probe_video_device(struct platform_device *pdev)
 		goto sub_dev_failed;
 	}
 
-	rc = venus_hfi_interface_queues_init(core);
+	/* create component master and add match data */
+	rc = component_master_add_with_match(&pdev->dev, &msm_vidc_component_ops, match);
 	if (rc) {
-		d_vpr_e("%s: interface queues init failed\n", __func__);
-		goto queues_init_failed;
+		d_vpr_e("%s: component master add with match failed\n", __func__);
+		goto master_add_failed;
 	}
 
-	rc = msm_vidc_core_init(core);
-	if (rc) {
-		d_vpr_e("%s: sys init failed\n", __func__);
-		goto core_init_failed;
-	}
 	d_vpr_h("%s(): succssful\n", __func__);
 
 	return rc;
 
-core_init_failed:
-	venus_hfi_interface_queues_deinit(core);
-queues_init_failed:
+master_add_failed:
 	of_platform_depopulate(&pdev->dev);
 sub_dev_failed:
 #ifdef CONFIG_MEDIA_CONTROLLER
@@ -603,9 +698,15 @@ init_core_failed:
 
 static int msm_vidc_probe_context_bank(struct platform_device *pdev)
 {
+	int rc = 0;
+
 	d_vpr_h("%s()\n", __func__);
 
-	return msm_vidc_read_context_bank_resources_from_dt(pdev);
+	rc = msm_vidc_read_context_bank_resources_from_dt(pdev);
+	if (rc)
+		return rc;
+
+	return component_add(&pdev->dev, &msm_vidc_component_cb_ops);
 }
 
 static int msm_vidc_probe(struct platform_device *pdev)
@@ -698,6 +799,7 @@ struct platform_driver msm_vidc_driver = {
 		.name = "msm_vidc_v4l2",
 		.of_match_table = msm_vidc_dt_match,
 		.pm = &msm_vidc_pm_ops,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
 	},
 };
 
@@ -712,6 +814,7 @@ static int __init msm_vidc_init(void)
 		d_vpr_e("Failed to register platform driver\n");
 		return rc;
 	}
+	d_vpr_h("%s(): succssful\n", __func__);
 
 	return 0;
 }
@@ -721,6 +824,7 @@ static void __exit msm_vidc_exit(void)
 	d_vpr_h("%s()\n", __func__);
 
 	platform_driver_unregister(&msm_vidc_driver);
+	d_vpr_h("%s(): succssful\n", __func__);
 }
 
 module_init(msm_vidc_init);