V4L/DVB (6342): ivtv: fix circular locking (bug 9037)
[powerpc.git] / drivers / media / video / ivtv / ivtv-streams.c
index fae151a..6c954b3 100644 (file)
@@ -38,6 +38,7 @@
 #include "ivtv-queue.h"
 #include "ivtv-mailbox.h"
 #include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
 #include "ivtv-yuv.h"
 #include "ivtv-cards.h"
 #include "ivtv-streams.h"
@@ -62,6 +63,13 @@ static struct file_operations ivtv_v4l2_dec_fops = {
       .poll = ivtv_v4l2_dec_poll,
 };
 
+#define IVTV_V4L2_DEC_MPG_OFFSET  16   /* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET  24   /* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET  32   /* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET  48   /* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET   8   /* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16   /* offset from 0 to register vbi output v4l2 minors on */
+
 static struct {
        const char *name;
        int vfl_type;
@@ -158,10 +166,9 @@ static void ivtv_stream_init(struct ivtv *itv, int type)
        ivtv_queue_init(&s->q_io);
 }
 
-static int ivtv_reg_dev(struct ivtv *itv, int type)
+static int ivtv_prep_dev(struct ivtv *itv, int type)
 {
        struct ivtv_stream *s = &itv->streams[type];
-       int vfl_type = ivtv_stream_info[type].vfl_type;
        int minor_offset = ivtv_stream_info[type].minor_offset;
        int minor;
 
@@ -179,15 +186,12 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
        if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
                return 0;
 
-       if (minor_offset >= 0)
-               /* card number + user defined offset + device offset */
-               minor = itv->num + ivtv_first_minor + minor_offset;
-       else
-               minor = -1;
+       /* card number + user defined offset + device offset */
+       minor = itv->num + ivtv_first_minor + minor_offset;
 
        /* User explicitly selected 0 buffers for these streams, so don't
           create them. */
-       if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+       if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
            itv->options.kilobytes[type] == 0) {
                IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
                return 0;
@@ -215,21 +219,53 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
        s->v4l2dev->fops = ivtv_stream_info[type].fops;
        s->v4l2dev->release = video_device_release;
 
-       if (minor >= 0) {
-               /* 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)) {
-                       IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
-                                       s->name, minor);
-                       video_device_release(s->v4l2dev);
-                       s->v4l2dev = NULL;
-                       return -ENOMEM;
-               }
+       return 0;
+}
+
+/* Initialize v4l2 variables and prepare v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+       int type;
+
+       /* Setup V4L2 Devices */
+       for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+               /* Prepare device */
+               if (ivtv_prep_dev(itv, type))
+                       break;
+
+               if (itv->streams[type].v4l2dev == NULL)
+                       continue;
+
+               /* Allocate Stream */
+               if (ivtv_stream_alloc(&itv->streams[type]))
+                       break;
        }
-       else {
-               /* Don't register a 'hidden' stream (OSD) */
-               IVTV_INFO("Created framebuffer stream for %s\n", s->name);
+       if (type == IVTV_MAX_STREAMS)
+               return 0;
+
+       /* One or more streams could not be initialized. Clean 'em all up. */
+       ivtv_streams_cleanup(itv);
+       return -ENOMEM;
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+       struct ivtv_stream *s = &itv->streams[type];
+       int vfl_type = ivtv_stream_info[type].vfl_type;
+       int minor;
+
+       if (s->v4l2dev == NULL)
                return 0;
+
+       minor = s->v4l2dev->minor;
+       /* 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)) {
+               IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
+                               s->name, minor);
+               video_device_release(s->v4l2dev);
+               s->v4l2dev = NULL;
+               return -ENOMEM;
        }
 
        switch (vfl_type) {
@@ -254,27 +290,18 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
        return 0;
 }
 
-/* Initialize v4l2 variables and register v4l2 devices */
-int ivtv_streams_setup(struct ivtv *itv)
+/* Register v4l2 devices */
+int ivtv_streams_register(struct ivtv *itv)
 {
        int type;
+       int err = 0;
 
-       /* Setup V4L2 Devices */
-       for (type = 0; type < IVTV_MAX_STREAMS; type++) {
-               /* Register Device */
-               if (ivtv_reg_dev(itv, type))
-                       break;
-
-               if (itv->streams[type].v4l2dev == NULL)
-                       continue;
+       /* Register V4L2 devices */
+       for (type = 0; type < IVTV_MAX_STREAMS; type++)
+               err |= ivtv_reg_dev(itv, type);
 
-               /* Allocate Stream */
-               if (ivtv_stream_alloc(&itv->streams[type]))
-                       break;
-       }
-       if (type == IVTV_MAX_STREAMS) {
+       if (err == 0)
                return 0;
-       }
 
        /* One or more streams could not be initialized. Clean 'em all up. */
        ivtv_streams_cleanup(itv);
@@ -295,11 +322,8 @@ void ivtv_streams_cleanup(struct ivtv *itv)
                        continue;
 
                ivtv_stream_free(&itv->streams[type]);
-               /* Free Device */
-               if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */
-                       video_device_release(vdev);
-               else    /* All others, just unregister. */
-                       video_unregister_device(vdev);
+               /* Unregister device */
+               video_unregister_device(vdev);
        }
 }
 
@@ -313,16 +337,6 @@ static void ivtv_vbi_setup(struct ivtv *itv)
        /* Reset VBI */
        ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
 
-       if (itv->is_60hz) {
-               itv->vbi.count = 12;
-               itv->vbi.start[0] = 10;
-               itv->vbi.start[1] = 273;
-       } else {        /* PAL/SECAM */
-               itv->vbi.count = 18;
-               itv->vbi.start[0] = 6;
-               itv->vbi.start[1] = 318;
-       }
-
        /* setup VBI registers */
        itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
 
@@ -396,8 +410,8 @@ static void ivtv_vbi_setup(struct ivtv *itv)
        if (!itv->vbi.fpi)
                itv->vbi.fpi = 1;
 
-       IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n",
-               itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer);
+       IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n",
+               itv->vbi.enc_start, data[1], itv->vbi.fpi);
 
        /* select VBI lines.
           Note that the sliced argument seems to have no effect. */
@@ -480,12 +494,15 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
        s->buffers_stolen = 0;
 
        /* mute/unmute video */
-       ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0);
+       ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+               test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 0x00808001 : 0);
 
        /* Clear Streamoff flags in case left from last capture */
        clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
 
        if (atomic_read(&itv->capturing) == 0) {
+               int digitizer;
+
                /* Always use frame based mode. Experiments have demonstrated that byte
                   stream based mode results in dropped frames and corruption. Not often,
                   but occasionally. Many thanks go to Leonard Orb who spent a lot of
@@ -511,7 +528,14 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
                ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 
-               ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer);
+               if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+                   digitizer = 0xF1;
+               else if (itv->card->hw_all & IVTV_HW_SAA7114)
+                   digitizer = 0xEF;
+               else /* cx25840 */
+                   digitizer = 0x140;
+
+               ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer);
 
                /* Setup VBI */
                if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
@@ -658,10 +682,10 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
        clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
 
        /* Zero out decoder counters */
-       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
-       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
-       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
-       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]);
        writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
        writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
        writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
@@ -702,7 +726,6 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
        struct ivtv *itv = s->itv;
        DECLARE_WAITQUEUE(wait, current);
        int cap_type;
-       unsigned long then;
        int stopmode;
 
        if (s->v4l2dev == NULL)
@@ -745,15 +768,13 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
        /* when: 0 =  end of GOP  1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
        ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
 
-       then = jiffies;
-
        if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
                if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
                        /* only run these if we're shutting down the last cap */
                        unsigned long duration;
+                       unsigned long then = jiffies;
 
-                       then = jiffies;
-                       add_wait_queue(&itv->cap_w, &wait);
+                       add_wait_queue(&itv->eos_waitq, &wait);
 
                        set_current_state(TASK_INTERRUPTIBLE);
 
@@ -779,11 +800,10 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
                                IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
                        }
                        set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&itv->cap_w, &wait);
+                       remove_wait_queue(&itv->eos_waitq, &wait);
+                       set_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
                }
 
-               then = jiffies;
-
                /* Handle any pending interrupts */
                ivtv_msleep_timeout(100, 1);
        }