media: ti-vpe: cal: Move DT parsing to CAMERARX
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 6 Jul 2020 18:36:19 +0000 (20:36 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Sun, 19 Jul 2020 07:41:23 +0000 (09:41 +0200)
The DT port nodes corresponding to the CSI-2 inputs belong to the
CAMERARX instances. Move parsing of the DT properties to a new
cal_camerarx_parse_dt() function, called by cal_camerarx_create().

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Benoit Parrot <bparrot@ti.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/platform/ti-vpe/cal.c

index 492141f..a2c245a 100644 (file)
@@ -269,6 +269,7 @@ struct cal_camerarx {
        unsigned int            instance;
 
        struct v4l2_fwnode_endpoint     endpoint;
+       struct device_node      *sensor_node;
        struct v4l2_subdev      *sensor;
        unsigned int            external_rate;
 };
@@ -925,6 +926,73 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal,
        return 0;
 }
 
+static int cal_camerarx_parse_dt(struct cal_camerarx *phy)
+{
+       struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;
+       struct platform_device *pdev = phy->cal->pdev;
+       struct device_node *ep_node;
+       char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2];
+       unsigned int i;
+       int ret;
+
+       /*
+        * Find the endpoint node for the port corresponding to the PHY
+        * instance, and parse its CSI-2-related properties.
+        */
+       ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node,
+                                               phy->instance, 0);
+       if (!ep_node) {
+               /*
+                * The endpoint is not mandatory, not all PHY instances need to
+                * be connected in DT.
+                */
+               phy_dbg(3, phy, "Port has no endpoint\n");
+               return 0;
+       }
+
+       endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
+       ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);
+       if (ret < 0) {
+               phy_err(phy, "Failed to parse endpoint\n");
+               goto done;
+       }
+
+       for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) {
+               unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i];
+
+               if (lane > 4) {
+                       phy_err(phy, "Invalid position %u for data lane %u\n",
+                               lane, i);
+                       ret = -EINVAL;
+                       goto done;
+               }
+
+               data_lanes[i*2] = '0' + lane;
+               data_lanes[i*2+1] = ' ';
+       }
+
+       data_lanes[i*2-1] = '\0';
+
+       phy_dbg(3, phy,
+               "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n",
+               endpoint->bus.mipi_csi2.clock_lane, data_lanes,
+               endpoint->bus.mipi_csi2.flags);
+
+       /* Retrieve the connected device and store it for later use. */
+       phy->sensor_node = of_graph_get_remote_port_parent(ep_node);
+       if (!phy->sensor_node) {
+               phy_dbg(3, phy, "Can't get remote parent\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node);
+
+done:
+       of_node_put(ep_node);
+       return ret;
+}
+
 static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
                                                unsigned int instance)
 {
@@ -958,6 +1026,10 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
        if (ret)
                goto error;
 
+       ret = cal_camerarx_parse_dt(phy);
+       if (ret)
+               goto error;
+
        return phy;
 
 error:
@@ -970,6 +1042,7 @@ static void cal_camerarx_destroy(struct cal_camerarx *phy)
        if (!phy)
                return;
 
+       of_node_put(phy->sensor_node);
        kfree(phy);
 }
 
@@ -1972,7 +2045,7 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx)
 }
 
 /* ------------------------------------------------------------------
- *     Initialization and module stuff
+ *     Asynchronous V4L2 subdev binding
  * ------------------------------------------------------------------
  */
 
@@ -2071,87 +2144,37 @@ static const struct v4l2_async_notifier_operations cal_async_ops = {
 
 static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
 {
-       struct platform_device *pdev = ctx->cal->pdev;
-       struct device_node *ep_node, *sensor_node;
-       struct v4l2_fwnode_endpoint *endpoint;
        struct v4l2_async_subdev *asd;
-       int ret = -EINVAL, lane;
-
-       endpoint = &ctx->phy->endpoint;
-
-       ctx_dbg(3, ctx, "Getting endpoint for port@%d\n", inst);
-       ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, inst, 0);
-       if (!ep_node) {
-               ctx_dbg(3, ctx, "Can't get endpoint\n");
-               return -EINVAL;
-       }
-
-       sensor_node = of_graph_get_remote_port_parent(ep_node);
-       if (!sensor_node) {
-               ctx_dbg(3, ctx, "Can't get remote parent\n");
-               goto cleanup_exit;
-       }
-
-       endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
-       ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);
-       if (ret < 0) {
-               ctx_err(ctx, "Failed to parse endpoint\n");
-               goto cleanup_exit;
-       }
-
-       ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
-       ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
-       ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
-       ctx_dbg(3, ctx, "num_data_lanes=%d\n",
-               endpoint->bus.mipi_csi2.num_data_lanes);
-       ctx_dbg(3, ctx, "data_lanes= <\n");
-       for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
-               ctx_dbg(3, ctx, "\t%d\n",
-                       endpoint->bus.mipi_csi2.data_lanes[lane]);
-       ctx_dbg(3, ctx, "\t>\n");
-
-       ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n",
-               inst, sensor_node);
+       struct fwnode_handle *fwnode;
+       int ret;
 
        v4l2_async_notifier_init(&ctx->notifier);
+       ctx->notifier.ops = &cal_async_ops;
 
-       asd = kzalloc(sizeof(*asd), GFP_KERNEL);
-       if (!asd)
-               goto cleanup_exit;
-
-       asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
-       asd->match.fwnode = of_fwnode_handle(sensor_node);
-
-       ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd);
-       if (ret) {
-               ctx_err(ctx, "Error adding asd\n");
-               kfree(asd);
-               goto cleanup_exit;
+       fwnode = of_fwnode_handle(ctx->phy->sensor_node);
+       asd = v4l2_async_notifier_add_fwnode_subdev(&ctx->notifier, fwnode,
+                                                   sizeof(*asd));
+       if (IS_ERR(asd)) {
+               ctx_err(ctx, "Failed to add subdev to notifier\n");
+               return PTR_ERR(asd);
        }
 
-       ctx->notifier.ops = &cal_async_ops;
        ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev,
                                           &ctx->notifier);
        if (ret) {
                ctx_err(ctx, "Error registering async notifier\n");
                v4l2_async_notifier_cleanup(&ctx->notifier);
-               ret = -EINVAL;
+               return ret;
        }
 
-       /*
-        * On success we need to keep reference on sensor_node, or
-        * if notifier_cleanup was called above, sensor_node was
-        * already put.
-        */
-       sensor_node = NULL;
-
-cleanup_exit:
-       of_node_put(sensor_node);
-       of_node_put(ep_node);
-
-       return ret;
+       return 0;
 }
 
+/* ------------------------------------------------------------------
+ *     Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
+
 static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
 {
        struct cal_ctx *ctx;
@@ -2219,6 +2242,7 @@ static int cal_probe(struct platform_device *pdev)
 {
        struct cal_dev *cal;
        struct cal_ctx *ctx;
+       bool connected = false;
        unsigned int i;
        int ret;
        int irq;
@@ -2271,6 +2295,15 @@ static int cal_probe(struct platform_device *pdev)
                        cal->phy[i] = NULL;
                        goto error_camerarx;
                }
+
+               if (cal->phy[i]->sensor_node)
+                       connected = true;
+       }
+
+       if (!connected) {
+               cal_err(cal, "Neither port is configured, no point in staying up\n");
+               ret = -ENODEV;
+               goto error_camerarx;
        }
 
        /* Register the V4L2 device. */
@@ -2281,13 +2314,16 @@ static int cal_probe(struct platform_device *pdev)
        }
 
        /* Create contexts. */
-       for (i = 0; i < cal->data->num_csi2_phy; ++i)
-               cal->ctx[i] = cal_ctx_create(cal, i);
+       for (i = 0; i < cal->data->num_csi2_phy; ++i) {
+               if (!cal->phy[i]->sensor_node)
+                       continue;
 
-       if (!cal->ctx[0] && !cal->ctx[1]) {
-               cal_err(cal, "Neither port is configured, no point in staying up\n");
-               ret = -ENODEV;
-               goto error_v4l2;
+               cal->ctx[i] = cal_ctx_create(cal, i);
+               if (!cal->ctx[i]) {
+                       cal_err(cal, "Failed to create context %u\n", i);
+                       ret = -ENODEV;
+                       goto error_context;
+               }
        }
 
        vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
@@ -2307,6 +2343,8 @@ error_pm_runtime:
        vb2_dma_contig_clear_max_seg_size(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
+
+error_context:
        for (i = 0; i < CAL_NUM_CONTEXT; i++) {
                ctx = cal->ctx[i];
                if (ctx) {
@@ -2316,7 +2354,6 @@ error_pm_runtime:
                }
        }
 
-error_v4l2:
        v4l2_device_unregister(&cal->v4l2_dev);
 
 error_camerarx: