@@ -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);
+}
@@ -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;
@@ -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);
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(+)