blob: 01f14c3f1a0c8f8dc2f520cab572122741747b4d [file] [log] [blame]
#include <linux/module.h>
#include <media/v4l2-ioctl.h>
#include "avicam_v4l2.h"
void avicam_stats_done(struct avi_capture_context *ctx,
struct avi_dma_buffer *frame,
enum avi_field f)
{
struct avicam *avicam = ctx->priv;
struct avicam_vbuf *vbuf = frame->priv;
enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
if (frame->status != AVI_BUFFER_DONE)
dev_err(avicam->dev, "couldn't capture stats\n");
v4l2_get_timestamp(&vbuf->vb.v4l2_buf.timestamp);
vbuf->vb.v4l2_buf.sequence = avicam->frame_count >> 1;
if (frame->status != AVI_BUFFER_DONE)
state = VB2_BUF_STATE_ERROR;
vb2_buffer_done(&vbuf->vb, state);
}
void avicam_stats_next(struct avi_capture_context *ctx,
struct avi_dma_buffer *frame,
enum avi_field f)
{
struct avicam *avicam = ctx->priv;
struct avicam_stats *stats = &avicam->stats;
struct avicam_vbuf *vbuf = NULL;
unsigned long flags;
spin_lock_irqsave(&stats->vbq_lock, flags);
if (stats->streaming && !list_empty(&stats->bufqueue)) {
vbuf = list_first_entry(&stats->bufqueue,
struct avicam_vbuf,
list);
list_del(&vbuf->list);
vbuf->vb.state = VB2_BUF_STATE_ACTIVE;
}
spin_unlock_irqrestore(&stats->vbq_lock, flags);
if (!vbuf)
/* exhausted buffer queue */
return;
/* XXX add YUV offset */
frame->plane0.dma_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb, 0);
frame->priv = vbuf;
}
static int avicam_stats_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct avicam *avicam = vb2_get_drv_priv(vq);
sizes[0] = AVI_CAPTURE_STATS_FRAMESIZE;
alloc_ctxs[0] = avicam->stats.alloc_ctx;
*num_planes = 1;
if (*count == 0)
/* Default to 4 buffers */
*count = 4;
return 0;
}
static int avicam_stats_vbuf_prepare(struct vb2_buffer* vb)
{
vb2_set_plane_payload(vb, 0, AVI_CAPTURE_STATS_FRAMESIZE);
vb->v4l2_buf.field = V4L2_FIELD_NONE;
return 0;
}
/* This function is called with stats->vbq_lock held (no need to protect
* ourselves when we play with the dmaqueue) */
static void avicam_stats_vbuf_queue(struct vb2_buffer *vb)
{
struct avicam *avicam = vb2_get_drv_priv(vb->vb2_queue);
struct avicam_vbuf *buf = to_avicam_vbuf(vb);
struct avicam_stats *stats = &avicam->stats;
unsigned long flags;
spin_lock_irqsave(&stats->vbq_lock, flags);
list_add_tail(&buf->list, &stats->bufqueue);
spin_unlock_irqrestore(&stats->vbq_lock, flags);
}
static int avicam_stats_querycap(struct file *file,
void *fh,
struct v4l2_capability *cap)
{
struct avicam *avicam = video_drvdata(file);
struct avicam_stats *stats = &avicam->stats;
mutex_lock(&stats->lock);
strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver));
snprintf(cap->card, sizeof(cap->card), "avi-stats.%d",
avicam_instance(avicam));
cap->version = DRIVER_VERSION;
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
mutex_unlock(&stats->lock);
return 0;
}
static int avicam_stats_enum_fmt_vid_cap(struct file *file,
void *priv,
struct v4l2_fmtdesc *f)
{
/* We only have one hardcoded format for the stats */
if (f->index != 0)
return -EINVAL;
f->flags = 0;
f->pixelformat = V4L2_PIX_FMT_BGR32;
strlcpy(f->description, "bayer statistics", sizeof(f->description));
return 0;
}
static int avicam_stats_fill_fmt(struct file *file,
void *priv,
struct v4l2_format *f)
{
struct v4l2_pix_format *pix = &f->fmt.pix;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pix->pixelformat = V4L2_PIX_FMT_BGR32;
pix->colorspace = 0;
pix->width = AVI_CAPTURE_STATS_WIDTH;
pix->height = AVI_CAPTURE_STATS_HEIGHT;
pix->bytesperline = pix->width * 4;
pix->sizeimage = pix->bytesperline * pix->height;
pix->field = V4L2_FIELD_NONE;
return 0;
}
static int avicam_stats_streamon(struct vb2_queue* vq, unsigned int count)
{
struct avicam *avicam = vb2_get_drv_priv(vq);
struct avicam_stats *stats = &avicam->stats;
if (stats->streaming)
return -EBUSY;
/* Nothing else to do here, the complicated stuff is done in avicam_streamon */
stats->streaming = 1;
return 0;
}
static int avicam_stats_do_streamoff(struct avicam *avicam)
{
struct avicam_stats *stats = &avicam->stats;
struct avicam_vbuf *buf;
struct avicam_vbuf *node;
unsigned long flags;
if (!stats->streaming)
return -EINVAL;
spin_lock_irqsave(&stats->vbq_lock, flags);
list_for_each_entry_safe(buf, node, &stats->bufqueue, list) {
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
list_del(&buf->list);
}
spin_unlock_irqrestore(&stats->vbq_lock, flags);
stats->streaming = 0;
return 0;
}
static int avicam_stats_streamoff(struct vb2_queue* vq)
{
struct avicam *avicam = vb2_get_drv_priv(vq);
return avicam_stats_do_streamoff(avicam);
}
static struct vb2_ops avicam_stats_vqueue_ops = {
.queue_setup = avicam_stats_queue_setup,
.buf_prepare = avicam_stats_vbuf_prepare,
.buf_queue = avicam_stats_vbuf_queue,
.start_streaming = avicam_stats_streamon,
.stop_streaming = avicam_stats_streamoff,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
static const struct v4l2_ioctl_ops avicam_stats_ioctl_ops = {
.vidioc_querycap = avicam_stats_querycap,
.vidioc_enum_fmt_vid_cap = avicam_stats_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = avicam_stats_fill_fmt,
.vidioc_try_fmt_vid_cap = avicam_stats_fill_fmt,
.vidioc_s_fmt_vid_cap = avicam_stats_fill_fmt,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
};
static int avicam_stats_open(struct file *file)
{
struct avicam *avicam = video_drvdata(file);
struct avicam_stats *stats = &avicam->stats;
struct vb2_queue *q = &stats->vb_vidq;
mutex_lock(&stats->lock);
if (stats->use_count != 0)
goto done;
memset(q, 0, sizeof(*q));
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->mem_ops = &vb2_dma_contig_memops;
q->lock = &stats->lock;
q->ops = &avicam_stats_vqueue_ops;
q->drv_priv = avicam;
q->buf_struct_size = sizeof(struct avicam_vbuf);
vb2_queue_init(q);
done:
stats->use_count++;
mutex_unlock(&stats->lock);
return 0;
}
static int avicam_stats_release(struct file *file)
{
struct avicam *avicam = video_drvdata(file);
struct avicam_stats *stats = &avicam->stats;
int ret = 0;
mutex_lock(&stats->lock);
stats->use_count--;
if (!stats->use_count)
ret = vb2_fop_release(file);
mutex_unlock(&stats->lock);
return ret;
}
static struct v4l2_file_operations avicam_stats_fops = {
.owner = THIS_MODULE,
.open = avicam_stats_open,
.release = avicam_stats_release,
.mmap = vb2_fop_mmap,
.poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2,
.read = vb2_fop_read,
};
int __devinit avicam_stats_init(struct avicam *avicam)
{
struct avicam_stats *stats = &avicam->stats;
struct video_device *vdev;
int ret;
if (!avicam->pdata->enable_stats)
/* Nothing to do */
return 0;
spin_lock_init(&stats->vbq_lock);
INIT_LIST_HEAD(&stats->bufqueue);
vdev = video_device_alloc();
if (vdev == NULL) {
ret = -ENODEV;
goto vdev_alloc_failed;
}
stats->vdev = vdev;
mutex_init(&stats->lock);
strlcpy(vdev->name, DRIVER_NAME "-stats", sizeof(vdev->name));
vdev->parent = avicam->dev;
vdev->current_norm = V4L2_STD_UNKNOWN;;
vdev->fops = &avicam_stats_fops;
vdev->ioctl_ops = &avicam_stats_ioctl_ops;
vdev->release = &video_device_release;
vdev->vfl_type = VFL_TYPE_GRABBER;
vdev->tvnorms = V4L2_STD_UNKNOWN;
vdev->queue = &stats->vb_vidq;
/* We handle the locking ourselves */
vdev->lock = NULL;
video_set_drvdata(vdev, avicam);
vdev->v4l2_dev = avicam->v4l2_dev;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret)
goto video_register_failed;
stats->alloc_ctx = vb2_dma_contig_init_ctx(&vdev->dev);
dev_info(avicam->dev,
"stats video device successfuly registered as %s\n",
video_device_node_name(stats->vdev));
return 0;
video_register_failed:
video_unregister_device(stats->vdev);
vdev_alloc_failed:
return ret;
}
void __devexit avicam_stats_destroy(struct avicam *avicam)
{
if (!avicam->pdata->enable_stats)
/* Nothing to do */
return;
vb2_dma_contig_cleanup_ctx(avicam->stats.alloc_ctx);
video_unregister_device(avicam->stats.vdev);
}