diff mbox series

[07/10] media: qcom: camss: csiphy: probe any present children CSIPHY subdevices

Message ID 20250612011531.2923701-8-vladimir.zapolskiy@linaro.org
State New
Headers show
Series [01/10] media: qcom: camss: remove never used camss_vfe_get()/camss_vfe_put() | expand

Commit Message

Vladimir Zapolskiy June 12, 2025, 1:15 a.m. UTC
Add CSIPHY driver routines to catch any possible csiphy subdevices
under CAMSS device tree node.

Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
---
 .../media/platform/qcom/camss/camss-csiphy.c  | 248 ++++++++++++++++++
 .../media/platform/qcom/camss/camss-csiphy.h  |   3 +
 drivers/media/platform/qcom/camss/camss.c     |   2 +
 3 files changed, 253 insertions(+)
diff mbox series

Patch

diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index f561811b7617..3020f7d0f621 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -17,6 +17,7 @@ 
 #include <linux/pm_runtime.h>
 #include <media/media-entity.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
 #include "camss-csiphy.h"
@@ -24,6 +25,17 @@ 
 
 #define MSM_CSIPHY_NAME "msm_csiphy"
 
+struct csiphy_priv {
+	struct device *dev;
+	unsigned int id;
+	struct camss *camss;
+	struct csiphy_device *csiphy;
+
+	struct v4l2_async_notifier notifier;
+
+	bool combo_mode;
+};
+
 static const struct csiphy_format_info formats_8x16[] = {
 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
@@ -836,3 +848,239 @@  void msm_csiphy_unregister_entity(struct csiphy_device *csiphy)
 	v4l2_device_unregister_subdev(&csiphy->subdev);
 	media_entity_cleanup(&csiphy->subdev.entity);
 }
+
+static int csiphy_notify_bound(struct v4l2_async_notifier *notifier,
+			       struct v4l2_subdev *subdev,
+			       struct v4l2_async_connection *asd)
+{
+	struct csiphy_priv *csiphy_priv = container_of(notifier,
+						struct csiphy_priv, notifier);
+	struct camss_async_subdev *csd = container_of(asd,
+						struct camss_async_subdev, asd);
+	struct csiphy_device *csiphy = csiphy_priv->csiphy;
+	struct media_entity *sensor = &subdev->entity;
+	unsigned int i;
+
+	/* Keep parsed media interface data, but set the correct port id */
+	csiphy->id = csiphy_priv->id;
+	csiphy->cfg.csi2 = &csd->interface.csi2;
+
+	for (i = 0; i < sensor->num_pads; i++)
+		if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+			break;
+
+	if (i == sensor->num_pads) {
+		dev_err(csiphy_priv->dev, "No source pad in external entity\n");
+		return -EINVAL;
+	}
+
+	return media_create_pad_link(sensor, i, &csiphy->subdev.entity,
+				MSM_CSIPHY_PAD_SINK,
+				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+}
+
+static int csiphy_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct csiphy_priv *csiphy = container_of(notifier, struct csiphy_priv,
+						  notifier);
+	struct camss *camss = csiphy->camss;
+
+	return v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations csiphy_notify_ops = {
+	.bound = csiphy_notify_bound,
+	.complete = csiphy_notify_complete,
+};
+
+static int msm_csiphy_parse_ports(struct csiphy_priv *csiphy)
+{
+	struct device *dev = csiphy->dev;
+	struct fwnode_handle *fwnode = dev_fwnode(dev), *ep;
+	unsigned int num_endpoints = fwnode_graph_get_endpoint_count(fwnode,
+						FWNODE_GRAPH_DEVICE_DISABLED);
+	int ret;
+
+	switch (num_endpoints) {
+	case 0:
+		return 0;
+	case 1:
+		break;
+	case 2:
+		csiphy->combo_mode = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_async_nf_init(&csiphy->notifier, &csiphy->camss->v4l2_dev);
+	csiphy->notifier.ops = &csiphy_notify_ops;
+
+	fwnode_graph_for_each_endpoint(fwnode, ep) {
+		struct camss_async_subdev *csd;
+
+		csd = v4l2_async_nf_add_fwnode_remote(&csiphy->notifier, ep,
+						struct camss_async_subdev);
+		if (IS_ERR(csd)) {
+			ret = PTR_ERR(csd);
+			goto err_remote;
+		}
+
+		ret = camss_parse_endpoint_node(dev, ep, csd);
+		if (ret < 0)
+			goto err_remote;
+	}
+
+	ret = v4l2_async_nf_register(&csiphy->notifier);
+	if (ret)
+		goto err_cleanup;
+
+	return 0;
+
+err_remote:
+	fwnode_handle_put(ep);
+err_cleanup:
+	v4l2_async_nf_cleanup(&csiphy->notifier);
+
+	return ret;
+}
+
+static int msm_csiphy_init(struct csiphy_priv *csiphy)
+{
+	struct camss *camss = csiphy->camss;
+	unsigned int i = csiphy->id, j;
+	int ret;
+
+	ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], i);
+	if (ret < 0) {
+		dev_err(csiphy->dev, "Failed to init csiphy%d sub-device: %d\n",
+			i, ret);
+		return ret;
+	}
+
+	ret = msm_csiphy_register_entity(&camss->csiphy[i], &camss->v4l2_dev);
+	if (ret < 0) {
+		dev_err(csiphy->dev, "Failed to register csiphy%d entity: %d\n",
+			i, ret);
+		return ret;
+	}
+
+	for (j = 0; j < camss->res->csid_num; j++) {
+		ret = media_create_pad_link(&camss->csiphy[i].subdev.entity,
+					    1 /* source */,
+					    &camss->csid[j].subdev.entity,
+					    0 /* sink */,
+					    0);
+		if (ret < 0) {
+			dev_err(csiphy->dev,
+				"Failed to link csiphy%d to csid: %d\n",
+				i, ret);
+			msm_csiphy_unregister_entity(&camss->csiphy[i]);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int msm_csiphy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *phy_node = dev_of_node(dev), *camss_node;
+	struct csiphy_priv *csiphy_priv;
+	struct of_phandle_iterator it;
+	struct camss *camss;
+	unsigned int i = 0;
+	int ret;
+
+	/* Bail out if camss device driver has not yet been registered */
+	camss = dev_get_drvdata(dev->parent);
+	if (!camss || !camss->v4l2_dev.dev)
+		return -EPROBE_DEFER;
+
+	camss_node = of_get_parent(phy_node);
+	if (!camss_node)
+		return -ENODEV;
+
+	of_for_each_phandle(&it, ret, camss_node, "phys", "#phy-cells", -1) {
+		if (it.node != phy_node) {
+			i++;
+			continue;
+		}
+
+		if (!of_node_name_eq(it.node, "phy") ||
+		    !of_device_is_available(it.node))
+			ret = -ENODEV;
+
+		of_node_put(it.node);
+		break;
+	}
+	of_node_put(camss_node);
+
+	if (ret)
+		return ret;
+
+	if (i >= camss->res->csiphy_num)
+		return -EINVAL;
+
+	csiphy_priv = devm_kzalloc(dev, sizeof(*csiphy_priv), GFP_KERNEL);
+	if (!csiphy_priv)
+		return -ENOMEM;
+
+	csiphy_priv->dev = dev;
+	csiphy_priv->camss = camss;
+	csiphy_priv->id = i;
+	csiphy_priv->csiphy = &camss->csiphy[i];
+
+	ret = msm_csiphy_init(csiphy_priv);
+	if (ret < 0)
+		goto err_parse;
+
+	ret = msm_csiphy_parse_ports(csiphy_priv);
+	if (ret < 0)
+		goto err_cleanup;
+
+	dev_set_drvdata(dev, csiphy_priv);
+
+	return 0;
+
+err_cleanup:
+	msm_csiphy_unregister_entity(csiphy_priv->csiphy);
+err_parse:
+	v4l2_async_nf_unregister(&csiphy_priv->notifier);
+	v4l2_async_nf_cleanup(&csiphy_priv->notifier);
+
+	return ret;
+}
+
+static void msm_csiphy_remove(struct platform_device *pdev)
+{
+	struct csiphy_priv *csiphy_priv = dev_get_drvdata(&pdev->dev);
+
+	msm_csiphy_unregister_entity(csiphy_priv->csiphy);
+
+	v4l2_async_nf_unregister(&csiphy_priv->notifier);
+	v4l2_async_nf_cleanup(&csiphy_priv->notifier);
+}
+
+static const struct of_device_id csiphy_dt_match[] = {
+	{ .compatible = "qcom,csiphy", },
+	{}
+};
+
+static struct platform_driver csiphy_platform_driver = {
+	.probe	= msm_csiphy_probe,
+	.remove	= msm_csiphy_remove,
+	.driver	= {
+		.name		= "qcom-csiphy",
+		.of_match_table	= csiphy_dt_match,
+	},
+};
+
+void __init msm_csiphy_driver_register(void) {
+	platform_driver_register(&csiphy_platform_driver);
+}
+
+void __exit msm_csiphy_driver_unregister(void) {
+	platform_driver_unregister(&csiphy_platform_driver);
+}
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index f092b7ff2f26..b984aa745c78 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -120,6 +120,9 @@  int msm_csiphy_register_entity(struct csiphy_device *csiphy,
 
 void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
 
+void msm_csiphy_driver_register(void);
+void msm_csiphy_driver_unregister(void);
+
 extern const struct csiphy_formats csiphy_formats_8x16;
 extern const struct csiphy_formats csiphy_formats_8x96;
 extern const struct csiphy_formats csiphy_formats_sdm845;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 40bb20bbe8b4..57a522fcb8c0 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -3955,6 +3955,7 @@  static struct platform_driver qcom_camss_driver = {
 
 static int __init qcom_camss_init(void)
 {
+	msm_csiphy_driver_register();
 	return platform_driver_register(&qcom_camss_driver);
 }
 module_init(qcom_camss_init);
@@ -3962,6 +3963,7 @@  module_init(qcom_camss_init);
 static void __exit qcom_camss_exit(void)
 {
 	platform_driver_unregister(&qcom_camss_driver);
+	msm_csiphy_driver_unregister();
 }
 module_exit(qcom_camss_exit);