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