Commit f5ddf19e authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

media: ti-vpe: cal: Move DT parsing to CAMERARX



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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: default avatarBenoit Parrot <bparrot@ti.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent d3665f3b
Loading
Loading
Loading
Loading
+111 −74
Original line number Diff line number Diff line
@@ -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");
		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_v4l2;
			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: