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


static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)
{
	return container_of(sd, struct mxc_isi_pipe, sd);
}

static int mxc_isi_get_vc(struct mxc_isi_pipe *pipe)
{
	struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
	struct device *dev = pipe->isi->dev;
	struct v4l2_mbus_frame_desc source_fd;
	struct v4l2_mbus_frame_desc_entry *entry = NULL;
	unsigned int i;
	int ret;

	ret = v4l2_subdev_call(&xbar->sd, pad, get_frame_desc,
			       xbar->num_sinks + pipe->id, &source_fd);
	if (ret < 0) {
		dev_err(dev, "Failed to get source frame desc from pad %u\n",
			xbar->num_sinks + pipe->id);
		return ret;
	}

	for (i = 0; i < source_fd.num_entries; i++) {
		if (source_fd.entry[i].stream == 0) {
			entry = &source_fd.entry[i];
			break;
		}
	}

	if (source_fd.num_entries == 0) {
		/* If there is no source fd entries we assume virtual channel is 0 */
		pipe->vc = 0;
		return 0;
	}

	if (!entry) {
		dev_err(dev, "Failed to find stream from source frame desc\n");
		return -EPIPE;
	}

	if (entry->bus.csi2.vc > pipe->isi->pdata->num_channels) {
		dev_err(dev, "Virtual channel(%d) out of range\n",
			entry->bus.csi2.vc);
		return -EINVAL;
	}

	pipe->vc = entry->bus.csi2.vc;
	return 0;
}

int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
{
	struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
	const struct mxc_isi_bus_format_info *sink_info;
	const struct mxc_isi_bus_format_info *src_info;
	const struct v4l2_mbus_framefmt *sink_fmt;
	const struct v4l2_mbus_framefmt *src_fmt;
	const struct v4l2_rect *compose;
	struct v4l2_subdev_state *state;
	struct v4l2_subdev *sd = &pipe->sd;
	struct v4l2_area in_size, scale;
	struct v4l2_rect crop;
	u32 input;
	int ret;

	/*
	 * Find the connected input by inspecting the crossbar switch routing
	 * table.
	 */
	state = v4l2_subdev_lock_and_get_active_state(&xbar->sd);
	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
						    xbar->num_sinks + pipe->id,
						    0, &input, NULL);
	v4l2_subdev_unlock_state(state);

	if (ret)
		return -EPIPE;

	/* Configure the pipeline. */
	state = v4l2_subdev_lock_and_get_active_state(sd);

	sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);
	src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);
	compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK);
	crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE);

	sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
					       MXC_ISI_PIPE_PAD_SINK);
	src_info = mxc_isi_bus_format_by_code(src_fmt->code,
					      MXC_ISI_PIPE_PAD_SOURCE);

	in_size.width = sink_fmt->width;
	in_size.height = sink_fmt->height;
	scale.width = compose->width;
	scale.height = compose->height;

	v4l2_subdev_unlock_state(state);

	ret = mxc_isi_get_vc(pipe);
	if (ret)
		return ret;

	/* Configure the ISI channel. */
	mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
			       sink_info->encoding, src_info->encoding);

	mxc_isi_channel_enable(pipe);

	/* Enable streams on the crossbar switch. */
	ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
					 BIT(0));
	if (ret) {
		mxc_isi_channel_disable(pipe);
		dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",
			pipe->id);
		return ret;
	}

	return 0;
}

void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)
{
	struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
	int ret;

	ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
					  BIT(0));
	if (ret)
		dev_err(pipe->isi->dev, "Failed to disable pipe %u\n",
			pipe->id);

	mxc_isi_channel_disable(pipe);
}

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

static struct v4l2_mbus_framefmt *
mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe,
			    struct v4l2_subdev_state *state,
			    unsigned int pad)
{
	return v4l2_subdev_state_get_format(state, pad);
}

static struct v4l2_rect *
mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe,
			  struct v4l2_subdev_state *state,
			  unsigned int pad)
{
	return v4l2_subdev_state_get_crop(state, pad);
}

static struct v4l2_rect *
mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe,
			     struct v4l2_subdev_state *state,
			     unsigned int pad)
{
	return v4l2_subdev_state_get_compose(state, pad);
}

static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd,
				   struct v4l2_subdev_state *state)
{
	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
	struct v4l2_mbus_framefmt *fmt_source;
	struct v4l2_mbus_framefmt *fmt_sink;
	struct v4l2_rect *compose;
	struct v4l2_rect *crop;

	fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state,
					       MXC_ISI_PIPE_PAD_SINK);
	fmt_source = mxc_isi_pipe_get_pad_format(pipe, state,
						 MXC_ISI_PIPE_PAD_SOURCE);

	fmt_sink->width = MXC_ISI_DEF_WIDTH;
	fmt_sink->height = MXC_ISI_DEF_HEIGHT;
	fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK;
	fmt_sink->field = V4L2_FIELD_NONE;
	fmt_sink->colorspace = V4L2_COLORSPACE_JPEG;
	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
	fmt_sink->quantization =
		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
					      fmt_sink->ycbcr_enc);
	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);

	*fmt_source = *fmt_sink;
	fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;

	compose = mxc_isi_pipe_get_pad_compose(pipe, state,
					       MXC_ISI_PIPE_PAD_SINK);
	crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);

	compose->left = 0;
	compose->top = 0;
	compose->width = MXC_ISI_DEF_WIDTH;
	compose->height = MXC_ISI_DEF_HEIGHT;

	*crop = *compose;

	return 0;
}