Загрузка данных


static inline struct mxc_isi_crossbar *to_isi_crossbar(struct v4l2_subdev *sd)
{
	return container_of(sd, struct mxc_isi_crossbar, sd);
}

static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar,
					  struct v4l2_subdev_state *state,
					  struct v4l2_subdev *remote_sd,
					  u32 remote_pad, unsigned int port)
{
	struct mxc_isi_dev *isi = xbar->isi;
	const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;
	const struct v4l2_mbus_framefmt *fmt;
	struct v4l2_mbus_frame_desc fd;
	unsigned int stream;
	int ret;

	if (!gasket_ops)
		return 0;

	/*
	 * Configure and enable the gasket with the frame size and CSI-2 data
	 * type. For YUV422 8-bit, enable dual component mode unconditionally,
	 * to match the configuration of the CSIS.
	 */

	ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, remote_pad, &fd);
	if (ret) {
		dev_err(isi->dev,
			"failed to get frame descriptor from '%s':%u: %d\n",
			remote_sd->name, remote_pad, ret);
		return ret;
	}

	/*
	 * For single or multiple stream, only the first stream be used
	 * since gasket enable callback be called only once.
	 */
	stream = fd.num_entries > 0 ? fd.entry[0].stream : 0;
	fmt = v4l2_subdev_state_get_format(state, port, stream);
	if (!fmt)
		return -EINVAL;

	gasket_ops->enable(isi, &fd, fmt, port);
	return 0;
}

static void mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar *xbar,
					    unsigned int port)
{
	struct mxc_isi_dev *isi = xbar->isi;
	const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops;

	if (!gasket_ops)
		return;

	gasket_ops->disable(isi, port);
}

/* -----------------------------------------------------------------------------
 * V4L2 subdev operations
 */

static const struct v4l2_mbus_framefmt mxc_isi_crossbar_default_format = {
	.code = MXC_ISI_DEF_MBUS_CODE_SINK,
	.width = MXC_ISI_DEF_WIDTH,
	.height = MXC_ISI_DEF_HEIGHT,
	.field = V4L2_FIELD_NONE,
	.colorspace = MXC_ISI_DEF_COLOR_SPACE,
	.ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC,
	.quantization = MXC_ISI_DEF_QUANTIZATION,
	.xfer_func = MXC_ISI_DEF_XFER_FUNC,
};

static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
					  struct v4l2_subdev_state *state,
					  struct v4l2_subdev_krouting *routing)
{
	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
	struct v4l2_subdev_route *route;
	int ret;

	ret = v4l2_subdev_routing_validate(sd, routing,
					   V4L2_SUBDEV_ROUTING_NO_N_TO_1);
	if (ret)
		return ret;

	/* The memory input can be routed to the first pipeline only. */
	for_each_active_route(&state->routing, route) {
		if (route->sink_pad == xbar->num_sinks - 1 &&
		    route->source_pad != xbar->num_sinks) {
			dev_dbg(xbar->isi->dev,
				"invalid route from memory input (%u) to pipe %u\n",
				route->sink_pad,
				route->source_pad - xbar->num_sinks);
			return -EINVAL;
		}
	}

	return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
						&mxc_isi_crossbar_default_format);
}

static struct v4l2_subdev *
mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar,
			       struct v4l2_subdev_state *state,
			       u32 source_pad, u64 source_streams,
			       u32 *__sink_pad, u64 *__sink_streams,
			       u32 *remote_pad)
{
	struct v4l2_subdev_route *route;
	struct v4l2_subdev *sd;
	struct media_pad *pad;
	u64 sink_streams = 0;
	int sink_pad = -1;

	/*
	 * Translate the source pad and streams to the sink side. The routing
	 * validation forbids stream merging, so all matching entries in the
	 * routing table are guaranteed to have the same sink pad.
	 *
	 * TODO: This is likely worth a helper function, it could perhaps be
	 * supported by v4l2_subdev_state_xlate_streams() with pad1 set to -1.
	 */
	for_each_active_route(&state->routing, route) {
		if (route->source_pad != source_pad ||
		    !(source_streams & BIT(route->source_stream)))
			continue;

		sink_streams |= BIT(route->sink_stream);
		sink_pad = route->sink_pad;
	}

	if (sink_pad < 0) {
		dev_dbg(xbar->isi->dev,
			"no stream connected to pipeline %u\n",
			source_pad - xbar->num_sinks);
		return ERR_PTR(-EPIPE);
	}

	pad = media_pad_remote_pad_first(&xbar->pads[sink_pad]);
	if (!pad) {
		dev_err(xbar->isi->dev,
			"no remote pad found for sink pad %u\n",
			sink_pad);
		return ERR_PTR(-EPIPE);
	}

	sd = media_entity_to_v4l2_subdev(pad->entity);
	if (!sd) {
		dev_dbg(xbar->isi->dev,
			"no entity connected to crossbar input %u\n",
			sink_pad);
		return ERR_PTR(-EPIPE);
	}

	*__sink_pad = sink_pad;
	*__sink_streams = sink_streams;
	*remote_pad = pad->index;

	return sd;
}

static int mxc_isi_create_default_routing(struct mxc_isi_crossbar *xbar,
					   struct v4l2_subdev_route *routes)
{
	struct device *dev = xbar->isi->dev;
	struct device_node *node;
	unsigned int index = 0;
	int i, j;

	for_each_endpoint_of_node(dev->of_node, node) {
		struct of_endpoint ep;

		of_graph_parse_endpoint(node, &ep);

		if (ep.port > xbar->isi->pdata->num_ports) {
			dev_err(dev, "Invalid port number(%d)\n", ep.port);
			return -EINVAL;
		}

		xbar->inputs[ep.port].connected = true;
	}

	for (i = 0; i < xbar->num_sources; ++i) {
		struct v4l2_subdev_route *route = &routes[i];

		j = index;
		while (j < xbar->num_sinks) {
			if (!xbar->inputs[j].connected) {
				j = (++index) % xbar->num_sinks;
				continue;
			}

			route->sink_pad = j;
			route->source_pad = i + xbar->num_sinks;
			route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;

			index = (j + 1) % xbar->num_sinks;
			dev_dbg(dev, "route: sink(%d) -> source(%d)\n",
				route->sink_pad, route->source_pad);
			break;
		}
	}

	return 0;
}

static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
				       struct v4l2_subdev_state *state)
{
	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
	struct v4l2_subdev_krouting routing = { };
	struct v4l2_subdev_route *routes;
	int ret;

	/*
	 * Create a 1:N mapping between pixel link inputs and outputs to
	 * pipelines by according to the number of pixel links inputs and
	 * ISI channels. The algorithm will divide the ISI channel equally
	 * to each pixel link input which connect to a remote device.
	 */
	routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL);
	if (!routes)
		return -ENOMEM;

	ret = mxc_isi_create_default_routing(xbar, routes);
	if (ret < 0) {
		kfree(routes);
		return ret;
	}

	routing.num_routes = xbar->num_sources;
	routing.routes = routes;

	ret = __mxc_isi_crossbar_set_routing(sd, state, &routing);

	kfree(routes);

	return ret;
}