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


static int mxc_isi_video_streamon(struct file *file, void *priv,
				  enum v4l2_buf_type type)
{
	struct mxc_isi_video *video = video_drvdata(file);
	struct media_device *mdev = &video->pipe->isi->media_dev;
	struct media_pipeline *pipe;
	int ret;

printk("%s(%d) \n",__FUNCTION__,__LINE__);
	if (vb2_queue_is_busy(&video->vb2_q, file))
		return -EBUSY;

	if (video->is_streaming)
		return 0;

printk("%s(%d) \n",__FUNCTION__,__LINE__);
	/*
	 * Get a pipeline for the video node and start it. This must be done
	 * here and not in the queue .start_streaming() handler, so that
	 * pipeline start errors can be reported from VIDIOC_STREAMON and not
	 * delayed until subsequent VIDIOC_QBUF calls.
	 */
	mutex_lock(&mdev->graph_mutex);

	ret = mxc_isi_pipe_acquire(video->pipe, &mxc_isi_video_frame_write_done);
    printk("%s(%d) ret=%d\n",__FUNCTION__,__LINE__,ret);
	if (ret) {
		mutex_unlock(&mdev->graph_mutex);
		return ret;
	}

	pipe = media_entity_pipeline(&video->vdev.entity) ? : &video->pipe->pipe;

	ret = __video_device_pipeline_start(&video->vdev, pipe);
        printk("%s(%d) ret=%d\n",__FUNCTION__,__LINE__,ret);
	if (ret) {
		mutex_unlock(&mdev->graph_mutex);
		goto err_release;
	}

	mutex_unlock(&mdev->graph_mutex);

	/* Verify that the video format matches the output of the subdev. */
	ret = mxc_isi_video_validate_format(video);
        printk("%s(%d) ret=%d\n",__FUNCTION__,__LINE__,ret);
	if (ret)
		goto err_stop;

	/* Allocate buffers for discard operation. */
	ret = mxc_isi_video_alloc_discard_buffers(video);
        printk("%s(%d) ret=%d\n",__FUNCTION__,__LINE__,ret);
	if (ret)
		goto err_stop;

	ret = vb2_streamon(&video->vb2_q, type);
        printk("%s(%d) ret=%d\n",__FUNCTION__,__LINE__,ret);
	if (ret)
		goto err_free;

	video->is_streaming = true;

    printk("%s(%d) OK\n",__FUNCTION__,__LINE__);
	return 0;

err_free:
	mxc_isi_video_free_discard_buffers(video);
err_stop:
	video_device_pipeline_stop(&video->vdev);
err_release:
	mxc_isi_pipe_release(video->pipe);
	return ret;
}

static void mxc_isi_video_cleanup_streaming(struct mxc_isi_video *video)
{
	lockdep_assert_held(&video->lock);

	if (!video->is_streaming)
		return;

	mxc_isi_video_free_discard_buffers(video);
	video_device_pipeline_stop(&video->vdev);
	mxc_isi_pipe_release(video->pipe);

	video->is_streaming = false;
}

static int mxc_isi_video_streamoff(struct file *file, void *priv,
				   enum v4l2_buf_type type)
{
	struct mxc_isi_video *video = video_drvdata(file);
	int ret;

	ret = vb2_ioctl_streamoff(file, priv, type);
	if (ret)
		return ret;

	mxc_isi_video_cleanup_streaming(video);

	return 0;
}

static int mxc_isi_video_enum_framesizes(struct file *file, void *priv,
					 struct v4l2_frmsizeenum *fsize)
{
	struct mxc_isi_video *video = video_drvdata(file);
	const struct mxc_isi_pipe *pipe = video->pipe;
	const struct mxc_isi_format_info *info;
	unsigned int max_width;
	unsigned int h_align;
	unsigned int v_align;

	if (fsize->index)
		return -EINVAL;

	info = mxc_isi_format_by_fourcc(fsize->pixel_format, MXC_ISI_VIDEO_CAP);
	if (!info)
		return -EINVAL;

	h_align = max_t(unsigned int, info->hsub, 1);
	v_align = max_t(unsigned int, info->vsub, 1);

	max_width = (!pipe->bypass && pipe->id == pipe->isi->pdata->num_channels - 1)
		  ? MXC_ISI_MAX_WIDTH_UNCHAINED
		  : MXC_ISI_MAX_WIDTH_CHAINED;

	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
	fsize->stepwise.min_width = ALIGN(MXC_ISI_MIN_WIDTH, h_align);
	fsize->stepwise.min_height = ALIGN(MXC_ISI_MIN_HEIGHT, v_align);
	fsize->stepwise.max_width = ALIGN_DOWN(max_width, h_align);
	fsize->stepwise.max_height = ALIGN_DOWN(MXC_ISI_MAX_HEIGHT, v_align);
	fsize->stepwise.step_width = h_align;
	fsize->stepwise.step_height = v_align;

	/*
	 * The width can be further restricted due to line buffer sharing
	 * between pipelines when scaling, but we have no way to know here if
	 * the scaler will be used.
	 */

	return 0;
}

static const struct v4l2_ioctl_ops mxc_isi_video_ioctl_ops = {
	.vidioc_querycap		= mxc_isi_video_querycap,

	.vidioc_enum_fmt_vid_cap	= mxc_isi_video_enum_fmt,
	.vidioc_try_fmt_vid_cap_mplane	= mxc_isi_video_try_fmt,
	.vidioc_s_fmt_vid_cap_mplane	= mxc_isi_video_s_fmt,
	.vidioc_g_fmt_vid_cap_mplane	= mxc_isi_video_g_fmt,

	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
	.vidioc_querybuf		= vb2_ioctl_querybuf,
	.vidioc_qbuf			= vb2_ioctl_qbuf,
	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
	.vidioc_expbuf			= vb2_ioctl_expbuf,
	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
	.vidioc_create_bufs		= vb2_ioctl_create_bufs,

	.vidioc_streamon		= mxc_isi_video_streamon,
	.vidioc_streamoff		= mxc_isi_video_streamoff,

	.vidioc_enum_framesizes		= mxc_isi_video_enum_framesizes,

	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
};

/* -----------------------------------------------------------------------------
 * Video device file operations
 */

static int mxc_isi_video_open(struct file *file)
{
	struct mxc_isi_video *video = video_drvdata(file);
	int ret;

	ret = v4l2_fh_open(file);
	if (ret)
		return ret;

	ret = pm_runtime_resume_and_get(video->pipe->isi->dev);
	if (ret) {
		v4l2_fh_release(file);
		return ret;
	}

	return 0;
}

static int mxc_isi_video_release(struct file *file)
{
	struct mxc_isi_video *video = video_drvdata(file);
	int ret;

	ret = vb2_fop_release(file);
	if (ret)
		dev_err(video->pipe->isi->dev, "%s fail\n", __func__);

	mutex_lock(&video->lock);
	if (!video->vdev.queue->owner)
		mxc_isi_video_cleanup_streaming(video);
	mutex_unlock(&video->lock);

	pm_runtime_put(video->pipe->isi->dev);
	return ret;
}

static const struct v4l2_file_operations mxc_isi_video_fops = {
	.owner		= THIS_MODULE,
	.open		= mxc_isi_video_open,
	.release	= mxc_isi_video_release,
	.poll		= vb2_fop_poll,
	.unlocked_ioctl	= video_ioctl2,
	.mmap		= vb2_fop_mmap,
};

/* -----------------------------------------------------------------------------
 * Suspend & resume
 */

void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe)
{
	struct mxc_isi_video *video = &pipe->video;

	if (!video->is_streaming)
		return;

	mxc_isi_pipe_disable(pipe);
	mxc_isi_channel_put(pipe);

	spin_lock_irq(&video->buf_lock);

	/*
	 * Move the active buffers back to the pending or discard list. We must
	 * iterate the active list backward and move the buffers to the head of
	 * the pending list to preserve the buffer queueing order.
	 */
	while (!list_empty(&video->out_active)) {
		struct mxc_isi_buffer *buf =
			list_last_entry(&video->out_active,
					struct mxc_isi_buffer, list);

		if (buf->discard)
			list_move(&buf->list, &video->out_discard);
		else
			list_move(&buf->list, &video->out_pending);
	}

	spin_unlock_irq(&video->buf_lock);
}

int mxc_isi_video_resume(struct mxc_isi_pipe *pipe)
{
	struct mxc_isi_video *video = &pipe->video;

	if (!video->is_streaming)
		return 0;

	mxc_isi_video_init_channel(video);

	spin_lock_irq(&video->buf_lock);
	mxc_isi_video_queue_first_buffers(video);
	spin_unlock_irq(&video->buf_lock);

	return mxc_isi_pipe_enable(pipe);
}