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


static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd,
				       struct v4l2_subdev_state *state,
				       struct v4l2_subdev_mbus_code_enum *code)
{
	static const u32 output_codes[] = {
		MEDIA_BUS_FMT_YUV8_1X24,
		MEDIA_BUS_FMT_RGB888_1X24,
	};
	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
	const struct mxc_isi_bus_format_info *info;
	unsigned int index;
	unsigned int i;

	if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) {
		const struct v4l2_mbus_framefmt *format;

		format = mxc_isi_pipe_get_pad_format(pipe, state,
						     MXC_ISI_PIPE_PAD_SINK);
		info = mxc_isi_bus_format_by_code(format->code,
						  MXC_ISI_PIPE_PAD_SINK);

		if (info->encoding == MXC_ISI_ENC_RAW) {
			/*
			 * For RAW formats, the sink and source media bus codes
			 * must match.
			 */
			if (code->index)
				return -EINVAL;

			code->code = info->output;
		} else {
			/*
			 * For RGB or YUV formats, the ISI supports format
			 * conversion. Either of the two output formats can be
			 * used regardless of the input.
			 */
			if (code->index > 1)
				return -EINVAL;

			code->code = output_codes[code->index];
		}

		return 0;
	}

	index = code->index;

	for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) {
		info = &mxc_isi_bus_formats[i];

		if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK)))
			continue;

		if (index == 0) {
			code->code = info->mbus_code;
			return 0;
		}

		index--;
	}

	return -EINVAL;
}

static inline bool is_chan_bypass(const struct v4l2_mbus_framefmt *sink_fmt,
				  const struct v4l2_mbus_framefmt *src_fmt,
				  const struct mxc_isi_bus_format_info *sink_info,
				  const struct mxc_isi_bus_format_info *src_info)
{
	return sink_fmt->width == src_fmt->width &&
	       sink_fmt->height == src_fmt->height &&
	       sink_info->encoding == src_info->encoding;

}

static void update_pads_format(struct mxc_isi_pipe *pipe,
			       struct v4l2_subdev_state *state,
			       struct v4l2_subdev_format *fmt)
{
	struct v4l2_mbus_framefmt *mf = &fmt->format;
	const struct mxc_isi_bus_format_info *sink_info;
	const struct mxc_isi_bus_format_info *src_info;
	struct v4l2_mbus_framefmt *sink_fmt;
	struct v4l2_mbus_framefmt *src_fmt;
	struct v4l2_rect *comp, *crop;
	unsigned int max_width;
	bool bypass;

	sink_fmt = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK);
	src_fmt = mxc_isi_pipe_get_pad_format(pipe, 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);

	bypass = is_chan_bypass(sink_fmt, src_fmt, sink_info, src_info);
	pipe->bypass = bypass;

	/*
	 * Limit the max line length if there's no adjacent pipe to
	 * chain with and not bypass ISI channel.
	 */
	max_width = (!bypass && pipe->id == pipe->isi->pdata->num_channels - 1)
		  ? MXC_ISI_MAX_WIDTH_UNCHAINED
		  : MXC_ISI_MAX_WIDTH_CHAINED;

	sink_fmt->width = clamp(sink_fmt->width, MXC_ISI_MIN_WIDTH, max_width);

	/*
	 * Need to update pad format since its value may out of bound.
	 */
	comp = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK);
	comp->width = clamp(comp->width, MXC_ISI_MIN_WIDTH, sink_fmt->width);

	crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);
	crop->width = clamp(crop->width, MXC_ISI_MIN_WIDTH, comp->width);
	src_fmt->width = crop->width;

	*mf = *mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
}

static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
				struct v4l2_subdev_state *state,
				struct v4l2_subdev_format *fmt)
{
	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
	struct v4l2_mbus_framefmt *mf = &fmt->format;
	const struct mxc_isi_bus_format_info *info;
	struct v4l2_mbus_framefmt *format;
	struct v4l2_rect *rect;

	if (vb2_is_busy(&pipe->video.vb2_q))
		return -EBUSY;

	if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) {
		info = mxc_isi_bus_format_by_code(mf->code,
						  MXC_ISI_PIPE_PAD_SINK);
		if (!info)
			info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK,
							  MXC_ISI_PIPE_PAD_SINK);

		/*
		 * Source and sink pad media bus format are always equal
		 * when user set sink pad format since the sink pad bus
		 * format will be propagated to the source pad, so the
		 * maximum width for ISI channel is 4K. No adjacent line
		 * buffer is needed.
		 */
		mf->code = info->mbus_code;
		mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH,
				  MXC_ISI_MAX_WIDTH_CHAINED);
		mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT,
				   MXC_ISI_MAX_HEIGHT);

		/* Propagate the format to the source pad. */
		rect = mxc_isi_pipe_get_pad_compose(pipe, state,
						    MXC_ISI_PIPE_PAD_SINK);
		rect->width = mf->width;
		rect->height = mf->height;

		rect = mxc_isi_pipe_get_pad_crop(pipe, state,
						 MXC_ISI_PIPE_PAD_SOURCE);
		rect->left = 0;
		rect->top = 0;
		rect->width = mf->width;
		rect->height = mf->height;

		format = mxc_isi_pipe_get_pad_format(pipe, state,
						     MXC_ISI_PIPE_PAD_SOURCE);
		format->code = info->output;
		format->width = mf->width;
		format->height = mf->height;
	} else {
		/*
		 * For RGB or YUV formats, the ISI supports RGB <-> YUV format
		 * conversion. For RAW formats, the sink and source media bus
		 * codes must match.
		 */
		format = mxc_isi_pipe_get_pad_format(pipe, state,
						     MXC_ISI_PIPE_PAD_SINK);
		info = mxc_isi_bus_format_by_code(format->code,
						  MXC_ISI_PIPE_PAD_SINK);

		if (info->encoding != MXC_ISI_ENC_RAW) {
			if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 &&
			    mf->code != MEDIA_BUS_FMT_RGB888_1X24)
				mf->code = info->output;

			info = mxc_isi_bus_format_by_code(mf->code,
							  MXC_ISI_PIPE_PAD_SOURCE);
		}

		mf->code = info->output;

		/*
		 * The width and height on the source can't be changed, they
		 * must match the crop rectangle size.
		 */
		rect = mxc_isi_pipe_get_pad_crop(pipe, state,
						 MXC_ISI_PIPE_PAD_SOURCE);

		mf->width = rect->width;
		mf->height = rect->height;
	}

	format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
	*format = *mf;

	update_pads_format(pipe, state, fmt);

	dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",
		fmt->pad, mf->code, mf->width, mf->height);

	return 0;
}