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


static void mxc_isi_vb2_buffer_queue(struct vb2_buffer *vb2)
{
	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2);
	struct mxc_isi_buffer *buf = to_isi_buffer(v4l2_buf);
	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);

	spin_lock_irq(&video->buf_lock);
	list_add_tail(&buf->list, &video->out_pending);
	spin_unlock_irq(&video->buf_lock);
}

static void mxc_isi_video_init_channel(struct mxc_isi_video *video)
{
	struct mxc_isi_pipe *pipe = video->pipe;

	mxc_isi_channel_get(pipe);

	mutex_lock(video->ctrls.handler.lock);
	mxc_isi_channel_set_alpha(pipe, video->ctrls.alpha);
	mxc_isi_channel_set_flip(pipe, video->ctrls.hflip, video->ctrls.vflip);
	mutex_unlock(video->ctrls.handler.lock);

	mxc_isi_channel_set_output_format(pipe, video->fmtinfo, &video->pix);
}

static int mxc_isi_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
{
	struct mxc_isi_video *video = vb2_get_drv_priv(q);
	unsigned int i;
	int ret;

	/* Initialize the ISI channel. */
	mxc_isi_video_init_channel(video);

	spin_lock_irq(&video->buf_lock);

	/* Add the discard buffers to the out_discard list. */
	for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {
		struct mxc_isi_buffer *buf = &video->buf_discard[i];

		list_add_tail(&buf->list, &video->out_discard);
	}

	/* Queue the first buffers. */
	mxc_isi_video_queue_first_buffers(video);

	/* Clear frame count */
	video->frame_count = 0;

	spin_unlock_irq(&video->buf_lock);

	ret = mxc_isi_pipe_enable(video->pipe);
	if (ret)
		goto error;

	return 0;

error:
	mxc_isi_channel_put(video->pipe);
	mxc_isi_video_return_buffers(video, VB2_BUF_STATE_QUEUED);
	return ret;
}

static void mxc_isi_vb2_stop_streaming(struct vb2_queue *q)
{
	struct mxc_isi_video *video = vb2_get_drv_priv(q);

	mxc_isi_pipe_disable(video->pipe);
	mxc_isi_channel_put(video->pipe);

	mxc_isi_video_return_buffers(video, VB2_BUF_STATE_ERROR);
}

static const struct vb2_ops mxc_isi_vb2_qops = {
	.queue_setup		= mxc_isi_vb2_queue_setup,
	.buf_init		= mxc_isi_vb2_buffer_init,
	.buf_prepare		= mxc_isi_vb2_buffer_prepare,
	.buf_queue		= mxc_isi_vb2_buffer_queue,
	.wait_prepare		= vb2_ops_wait_prepare,
	.wait_finish		= vb2_ops_wait_finish,
	.start_streaming	= mxc_isi_vb2_start_streaming,
	.stop_streaming		= mxc_isi_vb2_stop_streaming,
};

/* -----------------------------------------------------------------------------
 * V4L2 controls
 */

static inline struct mxc_isi_video *ctrl_to_isi_video(struct v4l2_ctrl *ctrl)
{
	return container_of(ctrl->handler, struct mxc_isi_video, ctrls.handler);
}

static int mxc_isi_video_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct mxc_isi_video *video = ctrl_to_isi_video(ctrl);

	switch (ctrl->id) {
	case V4L2_CID_ALPHA_COMPONENT:
		video->ctrls.alpha = ctrl->val;
		break;
	case V4L2_CID_VFLIP:
		video->ctrls.vflip = ctrl->val;
		break;
	case V4L2_CID_HFLIP:
		video->ctrls.hflip = ctrl->val;
		break;
	}

	return 0;
}

static const struct v4l2_ctrl_ops mxc_isi_video_ctrl_ops = {
	.s_ctrl = mxc_isi_video_s_ctrl,
};

static int mxc_isi_video_ctrls_create(struct mxc_isi_video *video)
{
	struct v4l2_ctrl_handler *handler = &video->ctrls.handler;
	int ret;

	v4l2_ctrl_handler_init(handler, 3);

	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,
			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);

	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,
			  V4L2_CID_VFLIP, 0, 1, 1, 0);

	v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,
			  V4L2_CID_HFLIP, 0, 1, 1, 0);

	if (handler->error) {
		ret = handler->error;
		v4l2_ctrl_handler_free(handler);
		return ret;
	}

	video->vdev.ctrl_handler = handler;

	return 0;
}

static void mxc_isi_video_ctrls_delete(struct mxc_isi_video *video)
{
	v4l2_ctrl_handler_free(&video->ctrls.handler);
}

/* -----------------------------------------------------------------------------
 * V4L2 ioctls
 */

static int mxc_isi_video_querycap(struct file *file, void *priv,
				  struct v4l2_capability *cap)
{
	strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));
	strscpy(cap->card, MXC_ISI_CAPTURE, sizeof(cap->card));

	return 0;
}

static int mxc_isi_video_enum_fmt(struct file *file, void *priv,
				  struct v4l2_fmtdesc *f)
{
	const struct mxc_isi_format_info *fmt;
	unsigned int index = f->index;
	unsigned int i;

	if (f->mbus_code) {
		/*
		 * If a media bus code is specified, only enumerate formats
		 * compatible with it.
		 */
		for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {
			fmt = &mxc_isi_formats[i];
			if (fmt->mbus_code != f->mbus_code)
				continue;

			if (index == 0)
				break;

			index--;
		}

		if (i == ARRAY_SIZE(mxc_isi_formats))
			return -EINVAL;
	} else {
		/* Otherwise, enumerate all formatS. */
		if (f->index >= ARRAY_SIZE(mxc_isi_formats))
			return -EINVAL;

		fmt = &mxc_isi_formats[f->index];
	}

	f->pixelformat = fmt->fourcc;
	f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC
		 |  V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;

	return 0;
}

static int mxc_isi_video_g_fmt(struct file *file, void *fh,
			       struct v4l2_format *f)
{
	struct mxc_isi_video *video = video_drvdata(file);

	f->fmt.pix_mp = video->pix;

	return 0;
}

static int mxc_isi_video_try_fmt(struct file *file, void *fh,
				 struct v4l2_format *f)
{
	struct mxc_isi_video *video = video_drvdata(file);

	mxc_isi_format_try(video->pipe, &f->fmt.pix_mp, MXC_ISI_VIDEO_CAP);
	return 0;
}

static int mxc_isi_video_s_fmt(struct file *file, void *priv,
			       struct v4l2_format *f)
{
	struct mxc_isi_video *video = video_drvdata(file);
	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;

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

	video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP);
	video->pix = *pix;

	return 0;
}