Загрузка данных
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);
}