V4L/DVB (9800): cx18: Eliminate q_io from stream buffer handling
[safe/jmp/linux-2.6] / drivers / media / video / cx18 / cx18-streams.c
index 210a241..a1cf434 100644 (file)
@@ -4,6 +4,7 @@
  *  Derived from ivtv-streams.c
  *
  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@radix.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
@@ -22,6 +23,7 @@
  */
 
 #include "cx18-driver.h"
+#include "cx18-io.h"
 #include "cx18-fileops.h"
 #include "cx18-mailbox.h"
 #include "cx18-i2c.h"
@@ -56,7 +58,7 @@ static struct file_operations cx18_v4l2_enc_fops = {
 static struct {
        const char *name;
        int vfl_type;
-       int minor_offset;
+       int num_offset;
        int dma;
        enum v4l2_buf_type buf_type;
        struct file_operations *fops;
@@ -119,7 +121,7 @@ static void cx18_stream_init(struct cx18 *cx, int type)
        s->cx = cx;
        s->type = type;
        s->name = cx18_stream_info[type].name;
-       s->handle = 0xffffffff;
+       s->handle = CX18_INVALID_TASK_HANDLE;
 
        s->dma = cx18_stream_info[type].dma;
        s->buf_size = cx->stream_buf_size[type];
@@ -131,20 +133,19 @@ static void cx18_stream_init(struct cx18 *cx, int type)
                s->buffers = 63;
                s->buf_size = (max_size / s->buffers) & ~0xfff;
        }
-       spin_lock_init(&s->qlock);
+       mutex_init(&s->qlock);
        init_waitqueue_head(&s->waitq);
        s->id = -1;
        cx18_queue_init(&s->q_free);
        cx18_queue_init(&s->q_full);
-       cx18_queue_init(&s->q_io);
 }
 
 static int cx18_prep_dev(struct cx18 *cx, int type)
 {
        struct cx18_stream *s = &cx->streams[type];
        u32 cap = cx->v4l2_cap;
-       int minor_offset = cx18_stream_info[type].minor_offset;
-       int minor;
+       int num_offset = cx18_stream_info[type].num_offset;
+       int num = cx->num + cx18_first_minor + num_offset;
 
        /* These four fields are always initialized. If v4l2dev == NULL, then
           this stream is not in use. In that case no other fields but these
@@ -163,9 +164,6 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
            !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
                return 0;
 
-       /* card number + user defined offset + device offset */
-       minor = cx->num + cx18_first_minor + minor_offset;
-
        /* User explicitly selected 0 buffers for these streams, so don't
           create them. */
        if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
@@ -176,7 +174,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
 
        cx18_stream_init(cx, type);
 
-       if (minor_offset == -1)
+       if (num_offset == -1)
                return 0;
 
        /* allocate and initialize the v4l2 video device structure */
@@ -187,13 +185,10 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
                return -ENOMEM;
        }
 
-       s->v4l2dev->type =
-               VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
-               VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
        snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
                        cx->num);
 
-       s->v4l2dev->minor = minor;
+       s->v4l2dev->num = num;
        s->v4l2dev->parent = &cx->dev->dev;
        s->v4l2dev->fops = cx18_stream_info[type].fops;
        s->v4l2dev->release = video_device_release;
@@ -205,16 +200,18 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
 /* Initialize v4l2 variables and register v4l2 devices */
 int cx18_streams_setup(struct cx18 *cx)
 {
-       int type;
+       int type, ret;
 
        /* Setup V4L2 Devices */
        for (type = 0; type < CX18_MAX_STREAMS; type++) {
                /* Prepare device */
-               if (cx18_prep_dev(cx, type))
+               ret = cx18_prep_dev(cx, type);
+               if (ret < 0)
                        break;
 
                /* Allocate Stream */
-               if (cx18_stream_alloc(&cx->streams[type]))
+               ret = cx18_stream_alloc(&cx->streams[type]);
+               if (ret < 0)
                        break;
        }
        if (type == CX18_MAX_STREAMS)
@@ -222,14 +219,14 @@ int cx18_streams_setup(struct cx18 *cx)
 
        /* One or more streams could not be initialized. Clean 'em all up. */
        cx18_streams_cleanup(cx, 0);
-       return -ENOMEM;
+       return ret;
 }
 
 static int cx18_reg_dev(struct cx18 *cx, int type)
 {
        struct cx18_stream *s = &cx->streams[type];
        int vfl_type = cx18_stream_info[type].vfl_type;
-       int minor;
+       int num, ret;
 
        /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
         * We need a VFL_TYPE_TS defined.
@@ -238,47 +235,55 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
                /* just return if no DVB is supported */
                if ((cx->card->hw_all & CX18_HW_DVB) == 0)
                        return 0;
-               if (cx18_dvb_register(s) < 0) {
+               ret = cx18_dvb_register(s);
+               if (ret < 0) {
                        CX18_ERR("DVB failed to register\n");
-                       return -EINVAL;
+                       return ret;
                }
        }
 
        if (s->v4l2dev == NULL)
                return 0;
 
-       minor = s->v4l2dev->minor;
+       num = s->v4l2dev->num;
+       /* card number + user defined offset + device offset */
+       if (type != CX18_ENC_STREAM_TYPE_MPG) {
+               struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+               if (s_mpg->v4l2dev)
+                       num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset;
+       }
 
        /* Register device. First try the desired minor, then any free one. */
-       if (video_register_device(s->v4l2dev, vfl_type, minor) &&
-                       video_register_device(s->v4l2dev, vfl_type, -1)) {
-               CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
-                       s->name, minor);
+       ret = video_register_device(s->v4l2dev, vfl_type, num);
+       if (ret < 0) {
+               CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
+                       s->name, num);
                video_device_release(s->v4l2dev);
                s->v4l2dev = NULL;
-               return -ENOMEM;
+               return ret;
        }
-       minor = s->v4l2dev->minor;
+       num = s->v4l2dev->num;
 
        switch (vfl_type) {
        case VFL_TYPE_GRABBER:
                CX18_INFO("Registered device video%d for %s (%d MB)\n",
-                       minor, s->name, cx->options.megabytes[type]);
+                       num, s->name, cx->options.megabytes[type]);
                break;
 
        case VFL_TYPE_RADIO:
                CX18_INFO("Registered device radio%d for %s\n",
-                       minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+                       num, s->name);
                break;
 
        case VFL_TYPE_VBI:
                if (cx->options.megabytes[type])
                        CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
-                               minor - MINOR_VFL_TYPE_VBI_MIN,
+                               num,
                                s->name, cx->options.megabytes[type]);
                else
                        CX18_INFO("Registered device vbi%d for %s\n",
-                               minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+                               num, s->name);
                break;
        }
 
@@ -289,18 +294,22 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
 int cx18_streams_register(struct cx18 *cx)
 {
        int type;
-       int err = 0;
+       int err;
+       int ret = 0;
 
        /* Register V4L2 devices */
-       for (type = 0; type < CX18_MAX_STREAMS; type++)
-               err |= cx18_reg_dev(cx, type);
+       for (type = 0; type < CX18_MAX_STREAMS; type++) {
+               err = cx18_reg_dev(cx, type);
+               if (err && ret == 0)
+                       ret = err;
+       }
 
-       if (err == 0)
+       if (ret == 0)
                return 0;
 
        /* One or more streams could not be initialized. Clean 'em all up. */
        cx18_streams_cleanup(cx, 1);
-       return -ENOMEM;
+       return ret;
 }
 
 /* Unregister v4l2 devices */
@@ -435,7 +444,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
        default:
                return -EINVAL;
        }
-       s->buffers_stolen = 0;
 
        /* mute/unmute video */
        cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
@@ -473,7 +481,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
 
        if (atomic_read(&cx->tot_capturing) == 0) {
                clear_bit(CX18_F_I_EOS, &cx->i_flags);
-               write_reg(7, CX18_DSP0_INTERRUPT_MASK);
+               cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
        }
 
        cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
@@ -483,8 +491,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
        list_for_each(p, &s->q_free.list) {
                struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
 
-               writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr);
-               writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
+               cx18_writel(cx, buf->dma_handle,
+                                       &cx->scb->cpu_mdl[buf->id].paddr);
+               cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
                cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
                        (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
                        1, buf->id, s->buf_size);
@@ -492,7 +501,14 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
        /* begin_capture */
        if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
                CX18_DEBUG_WARN("Error starting capture!\n");
+               /* Ensure we're really not capturing before releasing MDLs */
+               if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+                       cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
+               else
+                       cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+               cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
                cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+               /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
                return -EINVAL;
        }
 
@@ -551,13 +567,16 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
        /* Clear capture and no-read bits */
        clear_bit(CX18_F_S_STREAMING, &s->s_flags);
 
+       /* Tell the CX23418 it can't use our buffers anymore */
+       cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
        cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
-       s->handle = 0xffffffff;
+       s->handle = CX18_INVALID_TASK_HANDLE;
 
        if (atomic_read(&cx->tot_capturing) > 0)
                return 0;
 
-       write_reg(5, CX18_DSP0_INTERRUPT_MASK);
+       cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
        wake_up(&s->waitq);
 
        return 0;
@@ -571,8 +590,26 @@ u32 cx18_find_handle(struct cx18 *cx)
        for (i = 0; i < CX18_MAX_STREAMS; i++) {
                struct cx18_stream *s = &cx->streams[i];
 
-               if (s->v4l2dev && s->handle)
+               if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE))
                        return s->handle;
        }
-       return 0;
+       return CX18_INVALID_TASK_HANDLE;
+}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+       int i;
+       struct cx18_stream *s;
+
+       if (handle == CX18_INVALID_TASK_HANDLE)
+               return NULL;
+
+       for (i = 0; i < CX18_MAX_STREAMS; i++) {
+               s = &cx->streams[i];
+               if (s->handle != handle)
+                       continue;
+               if (s->v4l2dev || s->dvb.enabled)
+                       return s;
+       }
+       return NULL;
 }