summaryrefslogtreecommitdiffstats
path: root/drivers/staging/media/solo6x10/v4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/solo6x10/v4l2.c')
-rw-r--r--drivers/staging/media/solo6x10/v4l2.c290
1 files changed, 98 insertions, 192 deletions
diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 87f3c0452ca..ba603ce1f74 100644
--- a/drivers/staging/media/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
@@ -1,6 +1,11 @@
/*
- * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
- * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
+ * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
+ *
+ * Original author:
+ * Ben Collins <bcollins@ubuntu.com>
+ *
+ * Additional work by:
+ * John Brooks <john.brooks@bluecherry.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,47 +26,43 @@
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dma-contig.h>
+
#include "solo6x10.h"
#include "tw28.h"
-#define SOLO_HW_BPL 2048
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
-/* Image size is two fields, SOLO_HW_BPL is one horizontal line */
+/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */
+#define SOLO_HW_BPL 2048
#define solo_vlines(__solo) (__solo->video_vsize * 2)
#define solo_image_size(__solo) (solo_bytesperline(__solo) * \
solo_vlines(__solo))
#define solo_bytesperline(__solo) (__solo->video_hsize * 2)
-#define MIN_VID_BUFFERS 4
+#define MIN_VID_BUFFERS 2
/* Simple file handle */
struct solo_filehandle {
- struct solo_dev *solo_dev;
+ struct solo_dev *solo_dev;
struct videobuf_queue vidq;
struct task_struct *kthread;
spinlock_t slock;
int old_write;
struct list_head vidq_active;
- struct p2m_desc desc[SOLO_NR_P2M_DESC];
- int desc_idx;
};
-unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
-MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)");
-
-static void erase_on(struct solo_dev *solo_dev)
+static inline void erase_on(struct solo_dev *solo_dev)
{
solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);
solo_dev->erasing = 1;
solo_dev->frame_blank = 0;
}
-static int erase_off(struct solo_dev *solo_dev)
+static inline int erase_off(struct solo_dev *solo_dev)
{
if (!solo_dev->erasing)
return 0;
@@ -78,8 +79,7 @@ static int erase_off(struct solo_dev *solo_dev)
void solo_video_in_isr(struct solo_dev *solo_dev)
{
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN);
- wake_up_interruptible(&solo_dev->disp_thread_wait);
+ wake_up_interruptible_all(&solo_dev->disp_thread_wait);
}
static void solo_win_setup(struct solo_dev *solo_dev, u8 ch,
@@ -202,165 +202,61 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch)
return 0;
}
-static void disp_reset_desc(struct solo_filehandle *fh)
-{
- /* We use desc mode, which ignores desc 0 */
- memset(fh->desc, 0, sizeof(*fh->desc));
- fh->desc_idx = 1;
-}
-
-static int disp_flush_descs(struct solo_filehandle *fh)
-{
- int ret;
-
- if (!fh->desc_idx)
- return 0;
-
- ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP,
- fh->desc, fh->desc_idx);
- disp_reset_desc(fh);
-
- return ret;
-}
-
-static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr,
- u32 ext_addr, int size, int repeat, int ext_size)
-{
- if (fh->desc_idx >= SOLO_NR_P2M_DESC) {
- int ret = disp_flush_descs(fh);
- if (ret)
- return ret;
- }
-
- solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr,
- size, repeat, ext_size);
- fh->desc_idx++;
-
- return 0;
-}
-
static void solo_fillbuf(struct solo_filehandle *fh,
struct videobuf_buffer *vb)
{
struct solo_dev *solo_dev = fh->solo_dev;
- struct videobuf_dmabuf *vbuf;
+ dma_addr_t vbuf;
unsigned int fdma_addr;
- int error = 1;
+ int error = -1;
int i;
- struct scatterlist *sg;
- dma_addr_t sg_dma;
- int sg_size_left;
- vbuf = videobuf_to_dma(vb);
+ vbuf = videobuf_to_dma_contig(vb);
if (!vbuf)
goto finish_buf;
if (erase_off(solo_dev)) {
- int i;
-
- /* Just blit to the entire sg list, ignoring size */
- for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) {
- void *p = sg_virt(sg);
- size_t len = sg_dma_len(sg);
-
- for (i = 0; i < len; i += 2) {
- ((u8 *)p)[i] = 0x80;
- ((u8 *)p)[i + 1] = 0x00;
- }
+ void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
+ int image_size = solo_image_size(solo_dev);
+ for (i = 0; i < image_size; i += 2) {
+ ((u8 *)p)[i] = 0x80;
+ ((u8 *)p)[i + 1] = 0x00;
}
-
error = 0;
- goto finish_buf;
- }
-
- disp_reset_desc(fh);
- sg = vbuf->sglist;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
-
- fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
- (SOLO_HW_BPL * solo_vlines(solo_dev)));
-
- for (i = 0; i < solo_vlines(solo_dev); i++) {
- int line_len = solo_bytesperline(solo_dev);
- int lines;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- /* No room for an entire line, so chunk it up */
- if (sg_size_left < line_len) {
- int this_addr = fdma_addr;
-
- while (line_len > 0) {
- int this_write;
-
- if (!sg_size_left) {
- sg = sg_next(sg);
- if (sg == NULL)
- goto finish_buf;
- sg_dma = sg_dma_address(sg);
- sg_size_left = sg_dma_len(sg);
- }
-
- this_write = min(sg_size_left, line_len);
-
- if (disp_push_desc(fh, sg_dma, this_addr,
- this_write, 0, 0))
- goto finish_buf;
-
- line_len -= this_write;
- sg_size_left -= this_write;
- sg_dma += this_write;
- this_addr += this_write;
- }
-
- fdma_addr += SOLO_HW_BPL;
- continue;
- }
-
- /* Shove as many lines into a repeating descriptor as possible */
- lines = min(sg_size_left / line_len,
- solo_vlines(solo_dev) - i);
-
- if (disp_push_desc(fh, sg_dma, fdma_addr, line_len,
- lines - 1, SOLO_HW_BPL))
- goto finish_buf;
+ } else {
+ fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write *
+ (SOLO_HW_BPL * solo_vlines(solo_dev)));
- i += lines - 1;
- fdma_addr += SOLO_HW_BPL * lines;
- sg_dma += lines * line_len;
- sg_size_left -= lines * line_len;
+ error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr,
+ solo_bytesperline(solo_dev),
+ solo_vlines(solo_dev), SOLO_HW_BPL);
}
- error = disp_flush_descs(fh);
-
finish_buf:
if (error) {
vb->state = VIDEOBUF_ERROR;
} else {
- vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev);
vb->state = VIDEOBUF_DONE;
vb->field_count++;
- do_gettimeofday(&vb->ts);
}
wake_up(&vb->done);
-
- return;
}
static void solo_thread_try(struct solo_filehandle *fh)
{
struct videobuf_buffer *vb;
- unsigned int cur_write;
+ /* Only "break" from this loop if slock is held, otherwise
+ * just return. */
for (;;) {
+ unsigned int cur_write;
+
+ cur_write = SOLO_VI_STATUS0_PAGE(
+ solo_reg_read(fh->solo_dev, SOLO_VI_STATUS0));
+ if (cur_write == fh->old_write)
+ return;
+
spin_lock(&fh->slock);
if (list_empty(&fh->vidq_active))
@@ -372,13 +268,9 @@ static void solo_thread_try(struct solo_filehandle *fh)
if (!waitqueue_active(&vb->done))
break;
- cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev,
- SOLO_VI_STATUS0));
- if (cur_write == fh->old_write)
- break;
-
fh->old_write = cur_write;
list_del(&vb->queue);
+ vb->state = VIDEOBUF_ACTIVE;
spin_unlock(&fh->slock);
@@ -413,17 +305,31 @@ static int solo_thread(void *data)
static int solo_start_thread(struct solo_filehandle *fh)
{
+ int ret = 0;
+
+ if (atomic_inc_return(&fh->solo_dev->disp_users) == 1)
+ solo_irq_on(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
+
fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp");
- return PTR_RET(fh->kthread);
+ if (IS_ERR(fh->kthread)) {
+ ret = PTR_ERR(fh->kthread);
+ fh->kthread = NULL;
+ }
+
+ return ret;
}
static void solo_stop_thread(struct solo_filehandle *fh)
{
- if (fh->kthread) {
- kthread_stop(fh->kthread);
- fh->kthread = NULL;
- }
+ if (!fh->kthread)
+ return;
+
+ kthread_stop(fh->kthread);
+ fh->kthread = NULL;
+
+ if (atomic_dec_return(&fh->solo_dev->disp_users) == 0)
+ solo_irq_off(fh->solo_dev, SOLO_IRQ_VIDEO_IN);
}
static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count,
@@ -459,9 +365,7 @@ static int solo_buf_prepare(struct videobuf_queue *vq,
if (vb->state == VIDEOBUF_NEEDS_INIT) {
int rc = videobuf_iolock(vq, vb, NULL);
if (rc < 0) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
+ videobuf_dma_contig_free(vq, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
return rc;
}
@@ -485,14 +389,11 @@ static void solo_buf_queue(struct videobuf_queue *vq,
static void solo_buf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(vq->dev, dma);
- videobuf_dma_free(dma);
+ videobuf_dma_contig_free(vq, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
-static struct videobuf_queue_ops solo_video_qops = {
+static const struct videobuf_queue_ops solo_video_qops = {
.buf_setup = solo_buf_setup,
.buf_prepare = solo_buf_prepare,
.buf_queue = solo_buf_queue,
@@ -535,12 +436,12 @@ static int solo_v4l2_open(struct file *file)
return ret;
}
- videobuf_queue_sg_init(&fh->vidq, &solo_video_qops,
- &solo_dev->pdev->dev, &fh->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- SOLO_DISP_PIX_FIELD,
- sizeof(struct videobuf_buffer), fh, NULL);
-
+ videobuf_queue_dma_contig_init(&fh->vidq, &solo_video_qops,
+ &solo_dev->pdev->dev, &fh->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ SOLO_DISP_PIX_FIELD,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
return 0;
}
@@ -557,9 +458,11 @@ static int solo_v4l2_release(struct file *file)
{
struct solo_filehandle *fh = file->private_data;
+ solo_stop_thread(fh);
+
videobuf_stop(&fh->vidq);
videobuf_mmap_free(&fh->vidq);
- solo_stop_thread(fh);
+
kfree(fh);
return 0;
@@ -585,12 +488,12 @@ static int solo_querycap(struct file *file, void *priv,
static int solo_enum_ext_input(struct solo_dev *solo_dev,
struct v4l2_input *input)
{
- static const char *dispnames_1[] = { "4UP" };
- static const char *dispnames_2[] = { "4UP-1", "4UP-2" };
- static const char *dispnames_5[] = {
+ static const char * const dispnames_1[] = { "4UP" };
+ static const char * const dispnames_2[] = { "4UP-1", "4UP-2" };
+ static const char * const dispnames_5[] = {
"4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
};
- const char **dispnames;
+ const char * const *dispnames;
if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext))
return -EINVAL;
@@ -640,8 +543,14 @@ static int solo_enum_input(struct file *file, void *priv,
static int solo_set_input(struct file *file, void *priv, unsigned int index)
{
struct solo_filehandle *fh = priv;
+ int ret = solo_v4l2_set_ch(fh->solo_dev, index);
+
+ if (!ret) {
+ while (erase_off(fh->solo_dev))
+ /* Do nothing */;
+ }
- return solo_v4l2_set_ch(fh->solo_dev, index);
+ return ret;
}
static int solo_get_input(struct file *file, void *priv, unsigned int *index)
@@ -732,7 +641,8 @@ static int solo_reqbufs(struct file *file, void *priv,
return videobuf_reqbufs(&fh->vidq, req);
}
-static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int solo_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
{
struct solo_filehandle *fh = priv;
@@ -901,11 +811,12 @@ static struct video_device solo_v4l2_template = {
.current_norm = V4L2_STD_NTSC_M,
};
-int solo_v4l2_init(struct solo_dev *solo_dev)
+int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
{
int ret;
int i;
+ atomic_set(&solo_dev->disp_users, 0);
init_waitqueue_head(&solo_dev->disp_thread_wait);
solo_dev->vfd = video_device_alloc();
@@ -915,7 +826,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev)
*solo_dev->vfd = solo_v4l2_template;
solo_dev->vfd->parent = &solo_dev->pdev->dev;
- ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr);
+ ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr);
if (ret < 0) {
video_device_release(solo_dev->vfd);
solo_dev->vfd = NULL;
@@ -927,35 +838,30 @@ int solo_v4l2_init(struct solo_dev *solo_dev)
snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)",
SOLO6X10_NAME, solo_dev->vfd->num);
- if (video_nr != -1)
- video_nr++;
-
- dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with "
- "%d inputs (%d extended)\n", solo_dev->vfd->num,
- solo_dev->nr_chans, solo_dev->nr_ext);
+ dev_info(&solo_dev->pdev->dev,
+ "Display as /dev/video%d with %d inputs (%d extended)\n",
+ solo_dev->vfd->num, solo_dev->nr_chans, solo_dev->nr_ext);
/* Cycle all the channels and clear */
for (i = 0; i < solo_dev->nr_chans; i++) {
solo_v4l2_set_ch(solo_dev, i);
while (erase_off(solo_dev))
- ;/* Do nothing */
+ /* Do nothing */;
}
/* Set the default display channel */
solo_v4l2_set_ch(solo_dev, 0);
while (erase_off(solo_dev))
- ;/* Do nothing */
-
- solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN);
+ /* Do nothing */;
return 0;
}
void solo_v4l2_exit(struct solo_dev *solo_dev)
{
- solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN);
- if (solo_dev->vfd) {
- video_unregister_device(solo_dev->vfd);
- solo_dev->vfd = NULL;
- }
+ if (solo_dev->vfd == NULL)
+ return;
+
+ video_unregister_device(solo_dev->vfd);
+ solo_dev->vfd = NULL;
}