]> err.no Git - linux-2.6/blobdiff - drivers/media/video/ivtv/ivtv-fileops.c
Merge branch 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / drivers / media / video / ivtv / ivtv-fileops.c
index 66ea3cbc369c209f2ae4cc52d03253479f16052b..a200a8a95a2dc2ee125e6b32a94ab69b96727973 100644 (file)
 #include "ivtv-irq.h"
 #include "ivtv-vbi.h"
 #include "ivtv-mailbox.h"
-#include "ivtv-audio.h"
+#include "ivtv-routing.h"
 #include "ivtv-streams.h"
 #include "ivtv-yuv.h"
-#include "ivtv-controls.h"
 #include "ivtv-ioctl.h"
 #include "ivtv-cards.h"
 #include <media/saa7115.h>
@@ -247,8 +246,9 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block,
                /* do we have new data? */
                buf = ivtv_dequeue(s, &s->q_full);
                if (buf) {
-                       if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags))
+                       if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
                                return buf;
+                       buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
                        if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
                                /* byteswap MPG data */
                                ivtv_buf_swap(buf);
@@ -258,19 +258,19 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block,
                        }
                        return buf;
                }
-               /* return if file was opened with O_NONBLOCK */
-               if (non_block) {
-                       *err = -EAGAIN;
-                       return NULL;
-               }
 
                /* return if end of stream */
                if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
-                       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
                        IVTV_DEBUG_INFO("EOS %s\n", s->name);
                        return NULL;
                }
 
+               /* return if file was opened with O_NONBLOCK */
+               if (non_block) {
+                       *err = -EAGAIN;
+                       return NULL;
+               }
+
                /* wait for more data to arrive */
                prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
                /* New buffers might have become available before we were added to the waitqueue */
@@ -378,10 +378,20 @@ static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_co
                int rc;
 
                buf = ivtv_get_buffer(s, non_block, &rc);
-               if (buf == NULL && rc == -EAGAIN && tot_written)
-                       break;
-               if (buf == NULL)
+               /* if there is no data available... */
+               if (buf == NULL) {
+                       /* if we got data, then return that regardless */
+                       if (tot_written)
+                               break;
+                       /* EOS condition */
+                       if (rc == 0) {
+                               clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+                               clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+                               ivtv_release_stream(s);
+                       }
+                       /* set errno */
                        return rc;
+               }
                rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
                if (buf != &itv->vbi.sliced_mpeg_buf) {
                        ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
@@ -408,7 +418,7 @@ static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t co
        ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
        struct ivtv *itv = s->itv;
 
-       IVTV_DEBUG_HI_INFO("read %zd from %s, got %zd\n", count, s->name, rc);
+       IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
        if (rc > 0)
                pos += rc;
        return rc;
@@ -499,9 +509,11 @@ ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_
        struct ivtv_stream *s = &itv->streams[id->type];
        int rc;
 
-       IVTV_DEBUG_HI_IOCTL("read %zd bytes from %s\n", count, s->name);
+       IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
 
+       mutex_lock(&itv->serialize_lock);
        rc = ivtv_start_capture(id);
+       mutex_unlock(&itv->serialize_lock);
        if (rc)
                return rc;
        return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
@@ -537,7 +549,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
        int rc;
        DEFINE_WAIT(wait);
 
-       IVTV_DEBUG_HI_IOCTL("write %zd bytes to %s\n", count, s->name);
+       IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name);
 
        if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
            s->type != IVTV_DEC_STREAM_TYPE_YUV &&
@@ -551,8 +563,11 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
 
        /* This stream does not need to start any decoding */
        if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+               int elems = count / sizeof(struct v4l2_sliced_vbi_data);
+
                set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
-               return ivtv_write_vbi(itv, user_buf, count);
+               ivtv_write_vbi(itv, (const struct v4l2_sliced_vbi_data *)user_buf, elems);
+               return elems * sizeof(struct v4l2_sliced_vbi_data);
        }
 
        mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
@@ -612,7 +627,9 @@ retry:
        }
 
        /* Start decoder (returns 0 if already started) */
+       mutex_lock(&itv->serialize_lock);
        rc = ivtv_start_decoding(id, itv->speed);
+       mutex_unlock(&itv->serialize_lock);
        if (rc) {
                IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
 
@@ -645,7 +662,7 @@ retry:
           to transfer the rest. */
        if (count && !(filp->f_flags & O_NONBLOCK))
                goto retry;
-       IVTV_DEBUG_HI_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+       IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
        return bytes_written;
 }
 
@@ -657,6 +674,7 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
        int res = 0;
 
        /* add stream's waitq to the poll list */
+       IVTV_DEBUG_HI_FILE("Decoder poll\n");
        poll_wait(filp, &s->waitq, wait);
 
        set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
@@ -679,16 +697,21 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
 
        /* Start a capture if there is none */
        if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
-               int rc = ivtv_start_capture(id);
+               int rc;
 
+               mutex_lock(&itv->serialize_lock);
+               rc = ivtv_start_capture(id);
+               mutex_unlock(&itv->serialize_lock);
                if (rc) {
                        IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
                                        s->name, rc);
                        return POLLERR;
                }
+               IVTV_DEBUG_FILE("Encoder poll started capture\n");
        }
 
        /* add stream's waitq to the poll list */
+       IVTV_DEBUG_HI_FILE("Encoder poll\n");
        poll_wait(filp, &s->waitq, wait);
 
        if (eof || s->q_full.length)
@@ -701,7 +724,7 @@ void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
        struct ivtv *itv = id->itv;
        struct ivtv_stream *s = &itv->streams[id->type];
 
-       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+       IVTV_DEBUG_FILE("close() of %s\n", s->name);
 
        /* 'Unclaim' this stream */
 
@@ -728,10 +751,11 @@ void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
                        ivtv_stop_v4l2_encode_stream(s, gop_end);
                }
        }
-       clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
-       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
-
-       ivtv_release_stream(s);
+       if (!gop_end) {
+               clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+               ivtv_release_stream(s);
+       }
 }
 
 static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
@@ -739,13 +763,14 @@ static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
        struct ivtv *itv = id->itv;
        struct ivtv_stream *s = &itv->streams[id->type];
 
-       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+       IVTV_DEBUG_FILE("close() of %s\n", s->name);
 
        /* Stop decoding */
        if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
                IVTV_DEBUG_INFO("close stopping decode\n");
 
                ivtv_stop_v4l2_decode_stream(s, flags, pts);
+               itv->output_mode = OUT_NONE;
        }
        clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
        clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
@@ -753,11 +778,7 @@ static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
                /* Restore registers we've changed & clean up any mess we've made */
                ivtv_yuv_close(itv);
        }
-       if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV)
-               itv->output_mode = OUT_NONE;
-       else if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_UDMA_YUV)
-               itv->output_mode = OUT_NONE;
-       else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG)
+       if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames)
                itv->output_mode = OUT_NONE;
 
        itv->speed = 0;
@@ -771,7 +792,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
        struct ivtv *itv = id->itv;
        struct ivtv_stream *s = &itv->streams[id->type];
 
-       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+       IVTV_DEBUG_FILE("close %s\n", s->name);
 
        v4l2_prio_close(&itv->prio, &id->prio);
 
@@ -784,6 +805,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
        /* 'Unclaim' this stream */
 
        /* Stop radio */
+       mutex_lock(&itv->serialize_lock);
        if (id->type == IVTV_ENC_STREAM_TYPE_RAD) {
                /* Closing radio device, return to TV mode */
                ivtv_mute(itv);
@@ -800,6 +822,11 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
                        crystal_freq.flags = 0;
                        ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
                }
+               if (atomic_read(&itv->capturing) > 0) {
+                       /* Undo video mute */
+                       ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+                               itv->params.video_mute | (itv->params.video_mute_yuv << 8));
+               }
                /* Done! Unmute and continue. */
                ivtv_unmute(itv);
                ivtv_release_stream(s);
@@ -809,56 +836,35 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
                ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
 
                /* If all output streams are closed, and if the user doesn't have
-                  IVTV_DEC_STREAM_TYPE_VOUT open, then disable VBI on TV-out. */
+                  IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
                if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) {
-                       /* disable VBI on TV-out */
-                       ivtv_disable_vbi(itv);
+                       /* disable CC on TV-out */
+                       ivtv_disable_cc(itv);
                }
        } else {
                ivtv_stop_capture(id, 0);
        }
        kfree(id);
+       mutex_unlock(&itv->serialize_lock);
        return 0;
 }
 
-int ivtv_v4l2_open(struct inode *inode, struct file *filp)
+static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
 {
-       int x, y = 0;
+       struct ivtv *itv = s->itv;
        struct ivtv_open_id *item;
-       struct ivtv *itv = NULL;
-       struct ivtv_stream *s = NULL;
-       int minor = iminor(inode);
 
-       /* Find which card this open was on */
-       spin_lock(&ivtv_cards_lock);
-       for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
-               /* find out which stream this open was on */
-               for (y = 0; y < IVTV_MAX_STREAMS; y++) {
-                       s = &ivtv_cards[x]->streams[y];
-                       if (s->v4l2dev && s->v4l2dev->minor == minor) {
-                               itv = ivtv_cards[x];
-                               break;
-                       }
-               }
-       }
-       spin_unlock(&ivtv_cards_lock);
+       IVTV_DEBUG_FILE("open %s\n", s->name);
 
-       if (itv == NULL) {
-               /* Couldn't find a device registered
-                  on that minor, shouldn't happen! */
-               printk(KERN_WARNING "ivtv:  No ivtv device found on minor %d\n", minor);
-               return -ENXIO;
-       }
-
-       if (y == IVTV_DEC_STREAM_TYPE_MPG &&
+       if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
                test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
                return -EBUSY;
 
-       if (y == IVTV_DEC_STREAM_TYPE_YUV &&
+       if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
                test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
                return -EBUSY;
 
-       if (y == IVTV_DEC_STREAM_TYPE_YUV) {
+       if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
                if (read_reg(0x82c) == 0) {
                        IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
                        /* return -ENODEV; */
@@ -873,7 +879,7 @@ int ivtv_v4l2_open(struct inode *inode, struct file *filp)
                return -ENOMEM;
        }
        item->itv = itv;
-       item->type = y;
+       item->type = s->type;
        v4l2_prio_open(&itv->prio, &item->prio);
 
        item->open_id = itv->open_id++;
@@ -887,12 +893,21 @@ int ivtv_v4l2_open(struct inode *inode, struct file *filp)
                        return -EBUSY;
                }
 
+               if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+                       if (atomic_read(&itv->capturing) > 0) {
+                               /* switching to radio while capture is
+                                  in progress is not polite */
+                               ivtv_release_stream(s);
+                               kfree(item);
+                               return -EBUSY;
+                       }
+               }
+               /* Mark that the radio is being used. */
+               set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
                /* We have the radio */
                ivtv_mute(itv);
                /* Switch tuner to radio */
                ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
-               /* Mark that the radio is being used. */
-               set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
                /* Select the correct audio input (i.e. radio tuner) */
                ivtv_audio_set_io(itv);
                if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -907,45 +922,65 @@ int ivtv_v4l2_open(struct inode *inode, struct file *filp)
        }
 
        /* YUV or MPG Decoding Mode? */
-       if (y == IVTV_DEC_STREAM_TYPE_MPG)
+       if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
                clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
-       else if (y == IVTV_DEC_STREAM_TYPE_YUV)
-       {
+       else if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
                set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
-       }
-
        return 0;
 }
 
-void ivtv_mute(struct ivtv *itv)
+int ivtv_v4l2_open(struct inode *inode, struct file *filp)
 {
-       struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 };
+       int res, x, y = 0;
+       struct ivtv *itv = NULL;
+       struct ivtv_stream *s = NULL;
+       int minor = iminor(inode);
+
+       /* Find which card this open was on */
+       spin_lock(&ivtv_cards_lock);
+       for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
+               /* find out which stream this open was on */
+               for (y = 0; y < IVTV_MAX_STREAMS; y++) {
+                       s = &ivtv_cards[x]->streams[y];
+                       if (s->v4l2dev && s->v4l2dev->minor == minor) {
+                               itv = ivtv_cards[x];
+                               break;
+                       }
+               }
+       }
+       spin_unlock(&ivtv_cards_lock);
+
+       if (itv == NULL) {
+               /* Couldn't find a device registered
+                  on that minor, shouldn't happen! */
+               printk(KERN_WARNING "No ivtv device found on minor %d\n", minor);
+               return -ENXIO;
+       }
 
-       /* Mute sound to avoid pop */
-       ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+       mutex_lock(&itv->serialize_lock);
+       if (ivtv_init_on_first_open(itv)) {
+               IVTV_ERR("Failed to initialize on minor %d\n", minor);
+               mutex_unlock(&itv->serialize_lock);
+               return -ENXIO;
+       }
+       res = ivtv_serialized_open(s, filp);
+       mutex_unlock(&itv->serialize_lock);
+       return res;
+}
 
+void ivtv_mute(struct ivtv *itv)
+{
        if (atomic_read(&itv->capturing))
                ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
-
        IVTV_DEBUG_INFO("Mute\n");
 }
 
 void ivtv_unmute(struct ivtv *itv)
 {
-       struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
-
-       /* initialize or refresh input */
-       if (atomic_read(&itv->capturing) == 0)
-               ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
-       ivtv_msleep_timeout(100, 0);
-
        if (atomic_read(&itv->capturing)) {
+               ivtv_msleep_timeout(100, 0);
                ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
                ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
        }
-
-       /* Unmute */
-       ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
        IVTV_DEBUG_INFO("Unmute\n");
 }