camera and uvc: support uvc sensor
authorddl <ddl@rockchip.com>
Mon, 20 Jun 2011 07:17:23 +0000 (15:17 +0800)
committerddl <ddl@rockchip.com>
Wed, 27 Jul 2011 03:24:43 +0000 (11:24 +0800)
drivers/media/video/rk29_camera_oneframe.c
drivers/media/video/soc_camera.c
drivers/media/video/uvc/uvc_queue.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c [changed mode: 0644->0755]
drivers/media/video/uvc/uvcvideo.h [changed mode: 0644->0755]

index e76d72f7d33f0953f001e6db6ad1cead3b756d19..95aa0758a5fba5ed40680867762a40c575ab0288 100755 (executable)
@@ -130,7 +130,7 @@ module_param(debug, int, S_IRUGO|S_IWUSR);
 #define CAM_IPPWORK_IS_EN()     ((pcdev->host_width != pcdev->icd->user_width) || (pcdev->host_height != pcdev->icd->user_height))                                  
 
 //Configure Macro
-#define RK29_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define RK29_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 2)
 
 /* limit to rk29 hardware capabilities */
 #define RK29_CAM_BUS_PARAM   (SOCAM_MASTER |\
@@ -240,7 +240,6 @@ struct rk29_camera_dev
     spinlock_t         lock;
 
     struct videobuf_buffer     *active;
-       struct videobuf_queue *vb_vidq_ptr;
        struct rk29_camera_reg reginfo_suspend;
        struct workqueue_struct *camera_wq;
        struct rk29_camera_work *camera_work;
@@ -266,12 +265,12 @@ static int rk29_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
     struct soc_camera_device *icd = vq->priv_data;
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
     struct rk29_camera_dev *pcdev = ici->priv;
-    int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3;
 
     dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
 
        /* planar capture requires Y, U and V buffers to be page aligned */
        #if 0
+    int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3;
     *size = PAGE_ALIGN(icd->user_width* icd->user_height * bytes_per_pixel);                               /* Y pages UV pages, yuv422*/
        pcdev->vipmem_bsize = PAGE_ALIGN(pcdev->host_width * pcdev->host_height * bytes_per_pixel);
        #else
@@ -435,12 +434,14 @@ static int rk29_pixfmt2ippfmt(unsigned int pixfmt, int *ippfmt)
 {
        switch (pixfmt)
        {
-               case V4L2_PIX_FMT_YUV422P:
+               case V4L2_PIX_FMT_NV16:
+        case V4L2_PIX_FMT_YUV422P:
                {
                        *ippfmt = IPP_Y_CBCR_H2V1;
                        break;
                }
-               case V4L2_PIX_FMT_YUV420:
+               case V4L2_PIX_FMT_NV12:
+        case V4L2_PIX_FMT_YUV420:
                {
                        *ippfmt = IPP_Y_CBCR_H2V2;
                        break;
@@ -622,7 +623,6 @@ static void rk29_camera_init_videobuf(struct videobuf_queue *q,
                                    V4L2_FIELD_NONE,
                                    sizeof(struct rk29_buffer),
                                    icd);
-       pcdev->vb_vidq_ptr = q;         /* ddl@rock-chips.com */
 }
 static int rk29_camera_activate(struct rk29_camera_dev *pcdev, struct soc_camera_device *icd)
 {
@@ -771,9 +771,11 @@ static int rk29_camera_add_device(struct soc_camera_device *icd)
     if (control) {
         sd = dev_get_drvdata(control);
                v4l2_subdev_call(sd, core, ioctl, RK29_CAM_SUBDEV_IOREQUEST,(void*)pcdev->pdata);
+        #if 0
         ret = v4l2_subdev_call(sd,core, init, 0);
         if (ret)
             goto ebusy;
+        #endif
         v4l2_subdev_call(sd, core, ioctl, RK29_CAM_SUBDEV_CB_REGISTER,(void*)(&pcdev->icd_cb));
     }
 
@@ -805,16 +807,6 @@ static void rk29_camera_remove_device(struct soc_camera_device *icd)
     v4l2_subdev_call(sd, core, ioctl, RK29_CAM_SUBDEV_DEACTIVATE,NULL);
        rk29_camera_deactivate(pcdev);
 
-       /* ddl@rock-chips.com: Call videobuf_mmap_free here for free the struct video_buffer which malloc in videobuf_alloc */
-       #if 0
-       if (pcdev->vb_vidq_ptr) {
-               videobuf_mmap_free(pcdev->vb_vidq_ptr);
-               pcdev->vb_vidq_ptr = NULL;
-       }
-       #else
-       pcdev->vb_vidq_ptr = NULL;
-       #endif
-
        if (pcdev->camera_work) {
                kfree(pcdev->camera_work);
                pcdev->camera_work = NULL;
@@ -917,16 +909,16 @@ static int rk29_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt
 }
 static const struct soc_camera_data_format rk29_camera_formats[] = {
        {
-               .name           = "Planar YUV420 12 bit",
+               .name           = "YUV420 NV12",
                .depth          = 12,
-               .fourcc         = V4L2_PIX_FMT_YUV420,
+               .fourcc         = V4L2_PIX_FMT_NV12,
                .colorspace     = V4L2_COLORSPACE_JPEG,
        },{
-               .name           = "Planar YUV422 16 bit",
+               .name           = "YUV422 NV16",
                .depth          = 16,
-               .fourcc         = V4L2_PIX_FMT_YUV422P,
+               .fourcc         = V4L2_PIX_FMT_NV16,
                .colorspace     = V4L2_COLORSPACE_JPEG,
-       },{
+       },{ 
                .name           = "Raw Bayer RGB 10 bit",
                .depth          = 16,
                .fourcc         = V4L2_PIX_FMT_SGRBG10,
@@ -943,12 +935,14 @@ static void rk29_camera_setup_format(struct soc_camera_device *icd, __u32 host_p
 
     switch (host_pixfmt)
     {
-        case V4L2_PIX_FMT_YUV422P:
+        case V4L2_PIX_FMT_NV16:
+        case V4L2_PIX_FMT_YUV422P:  /* ddl@rock-chips.com: V4L2_PIX_FMT_YUV422P is V4L2_PIX_FMT_NV16 actually in 0.0.1 driver */
             vip_ctrl_val |= VIPREGYUV422;
                        pcdev->frame_inval = RK29_CAM_FRAME_INVAL_DC;
                        pcdev->pixfmt = host_pixfmt;
             break;
-        case V4L2_PIX_FMT_YUV420:
+        case V4L2_PIX_FMT_NV12:
+        case V4L2_PIX_FMT_YUV420:   /* ddl@rock-chips.com: V4L2_PIX_FMT_YUV420 is V4L2_PIX_FMT_NV12 actually in 0.0.1 driver */
             vip_ctrl_val |= VIPREGYUV420;
                        if (pcdev->frame_inval != RK29_CAM_FRAME_INVAL_INIT)
                                pcdev->frame_inval = RK29_CAM_FRAME_INVAL_INIT;
@@ -1082,7 +1076,7 @@ static int rk29_camera_set_crop(struct soc_camera_device *icd,
 
         v4l_bound_align_image(&pix->width, RK29_CAM_W_MIN, RK29_CAM_W_MAX, 1,
             &pix->height, RK29_CAM_H_MIN, RK29_CAM_H_MAX, 0,
-            icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ?4 : 0);
+            icd->current_fmt->fourcc == V4L2_PIX_FMT_NV16 ?4 : 0);
 
         ret = v4l2_subdev_call(sd, video, s_fmt, &f);
         if (ret < 0)
@@ -1110,6 +1104,7 @@ static int rk29_camera_set_fmt(struct soc_camera_device *icd,
     struct v4l2_format cam_f = *f;
     struct v4l2_rect rect;
     int ret,usr_w,usr_h;
+    int stream_on = 0;
 
        usr_w = pix->width;
        usr_h = pix->height;
@@ -1123,13 +1118,31 @@ static int rk29_camera_set_fmt(struct soc_camera_device *icd,
     }
 
     cam_fmt = xlate->cam_fmt;
+    /* ddl@rock-chips.com: sensor init code transmit in here after open */
+    if ((pcdev->frame_inval == RK29_CAM_FRAME_INVAL_INIT) && (pcdev->active == NULL)
+        && list_empty(&pcdev->capture)) {
+        v4l2_subdev_call(sd,core, init, 0);        
+    }
 
+    stream_on = read_vip_reg(RK29_VIP_CTRL);
+    if (stream_on & ENABLE_CAPTURE)
+        write_vip_reg(RK29_VIP_CTRL, (stream_on & (~ENABLE_CAPTURE)));
     cam_f.fmt.pix.pixelformat = cam_fmt->fourcc;
     ret = v4l2_subdev_call(sd, video, s_fmt, &cam_f);
     cam_f.fmt.pix.pixelformat = pix->pixelformat;
     *pix = cam_f.fmt.pix;
        #ifdef CONFIG_VIDEO_RK29_WORK_IPP
        if ((pix->width != usr_w) || (pix->height != usr_h)) {
+        if (unlikely((pix->width <16) || (pix->width > 8190) || (pix->height < 16) || (pix->height > 8190))) {
+               RK29CAMERA_TR("Senor and IPP both invalid source resolution(%dx%d)\n",pix->width,pix->height);
+               ret = -EINVAL;
+               goto RK29_CAMERA_SET_FMT_END;
+       }       
+       if (unlikely((usr_w <16) || (usr_w > 2047) || (usr_h < 16) || (usr_h > 2047))) {
+               RK29CAMERA_TR("Senor and IPP both invalid destination resolution(%dx%d)\n",usr_w,usr_h);
+               ret = -EINVAL;
+            goto RK29_CAMERA_SET_FMT_END;
+       }
                pix->width = usr_w;
                pix->height = usr_h;
        }
@@ -1154,6 +1167,8 @@ static int rk29_camera_set_fmt(struct soc_camera_device *icd,
     }
 
 RK29_CAMERA_SET_FMT_END:
+    if (stream_on & ENABLE_CAPTURE)
+        write_vip_reg(RK29_VIP_CTRL, (read_vip_reg(RK29_VIP_CTRL) | ENABLE_CAPTURE));
        if (ret)
        RK29CAMERA_TR("\n%s..%d.. ret = %d  \n",__FUNCTION__,__LINE__, ret);
     return ret;
@@ -1205,7 +1220,7 @@ static int rk29_camera_try_fmt(struct soc_camera_device *icd,
    /* limit to rk29 hardware capabilities */
     v4l_bound_align_image(&pix->width, RK29_CAM_W_MIN, RK29_CAM_W_MAX, 1,
              &pix->height, RK29_CAM_H_MIN, RK29_CAM_H_MAX, 0,
-             pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+             pixfmt == V4L2_PIX_FMT_NV16 ? 4 : 0);
 
     pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8);
     pix->sizeimage = pix->height * pix->bytesperline;
@@ -1215,7 +1230,7 @@ static int rk29_camera_try_fmt(struct soc_camera_device *icd,
     /* limit to sensor capabilities */
     ret = v4l2_subdev_call(sd, video, try_fmt, f);
     pix->pixelformat = pixfmt;
-       #ifdef CONFIG_VIDEO_RK29_WORK_IPP
+       #ifdef CONFIG_VIDEO_RK29_WORK_IPP       
        if ((pix->width > usr_w) && (pix->height > usr_h)) {
                if (is_capture) {
                        vipmem_is_overflow = (PAGE_ALIGN((pix->width*pix->height*icd->current_fmt->depth+7)>>3) > pcdev->vipmem_size);
index 572b2e93b8d98cce5e84251b6a8985681471d06a..9c174400285aa53599ffe8f904df45e4415a2902 100644 (file)
@@ -515,7 +515,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
 
        mutex_lock(&icf->vb_vidq.vb_lock);
 
-       #if 0
+       #if 1
        if (icf->vb_vidq.bufs[0]) {
                dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
                ret = -EBUSY;
@@ -529,7 +529,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
        i = 0;
        while (icf->vb_vidq.bufs[i] && (i<VIDEO_MAX_FRAME)) {
                if (icf->vb_vidq.bufs[i]->state != VIDEOBUF_NEEDS_INIT) {
-                       dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
+                       dev_err(&icd->dev, "S_FMT denied: queue initialised, icf->vb_vidq.bufs[%d]->state:0x%x\n",i,icf->vb_vidq.bufs[i]->state);
                        ret = -EBUSY;
                        goto unlock;
                }
@@ -640,9 +640,8 @@ static int soc_camera_streamoff(struct file *file, void *priv,
 
        if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-
+    
        mutex_lock(&icd->video_lock);
-
        /* This calls buf_release from host driver's videobuf_queue_ops for all
         * remaining buffers. When the last buffer is freed, stop capture */
        videobuf_streamoff(&icf->vb_vidq);
@@ -651,6 +650,7 @@ static int soc_camera_streamoff(struct file *file, void *priv,
     if (ici->ops->s_stream)
                ici->ops->s_stream(icd, 0);                             /* ddl@rock-chips.com : Add stream control for host */
 
+    videobuf_mmap_free(&icf->vb_vidq);          /* ddl@rock-chips.com : free video buf */
        mutex_unlock(&icd->video_lock);
 
        return 0;
index f854698c40618a4a5225209f1060933f1aafb430..da4853b1538f1442da70834911c7ca7d411600b9 100644 (file)
@@ -84,6 +84,7 @@ void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
        spin_lock_init(&queue->irqlock);
        INIT_LIST_HEAD(&queue->mainqueue);
        INIT_LIST_HEAD(&queue->irqqueue);
+    init_waitqueue_head(&queue->wait);  /* ddl@rock-chips.com : This design copied from video-buf */
        queue->type = type;
 }
 
@@ -286,6 +287,8 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
        list_add_tail(&buf->queue, &queue->irqqueue);
        spin_unlock_irqrestore(&queue->irqlock, flags);
 
+    wake_up_interruptible_sync(&queue->wait);     /* ddl@rock-chips.com : This design copied from video-buf */
+
 done:
        mutex_unlock(&queue->mutex);
        return ret;
@@ -323,10 +326,31 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue,
        }
 
        mutex_lock(&queue->mutex);
+    /* ddl@rock-chips.com : This design copied from video-buf */
+checks:    
        if (list_empty(&queue->mainqueue)) {
-               uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
-               ret = -EINVAL;
-               goto done;
+        if (nonblocking) {
+                       uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
+               ret = -EINVAL;
+               goto done;
+               } else {
+                   //uvc_trace(UVC_TRACE_CAPTURE, "dequeue_buffer: waiting on buffer\n");
+            printk("dequeue_buffer: waiting on buffer\n");
+                       /* Drop lock to avoid deadlock with qbuf */
+                       mutex_unlock(&queue->mutex);
+
+                       /* Checking list_empty and streaming is safe without
+                        * locks because we goto checks to validate while
+                        * holding locks before proceeding */
+                       ret = wait_event_interruptible(queue->wait,
+                               ((!list_empty(&queue->mainqueue)) || (!(queue->flags & UVC_QUEUE_STREAMING))));
+                       mutex_lock(&queue->mutex);
+
+                       if (ret)
+                               goto done;
+
+                       goto checks;
+               }       
        }
 
        buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
@@ -455,6 +479,8 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
        struct uvc_buffer *buf;
        unsigned long flags;
 
+    wake_up_interruptible_sync(&queue->wait);           /* ddl@rock-chips.com : This design copied from video-buf */
+
        spin_lock_irqsave(&queue->irqlock, flags);
        while (!list_empty(&queue->irqqueue)) {
                buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
index a2bdd806efab1e8244dc551c3bf2d0fcf4d0ab7d..4527d6f7c0fe2cd9c27e873784cbe580cf5e07cc 100644 (file)
@@ -249,12 +249,17 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream,
        struct uvc_frame *frame;
        int ret;
 
-       if (fmt->type != stream->type)
+       if (fmt->type != stream->type) {
+        printk("uvc_v4l2_set_format, fmt->type(%d) != stream->type(%d)\n",fmt->type,stream->type);
                return -EINVAL;
+       }
 
-       if (uvc_queue_allocated(&stream->queue))
+       if (uvc_queue_allocated(&stream->queue)) {
+        printk("uvc_queue_allocated failed\n");
                return -EBUSY;
 
+       }
+
        ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
        if (ret < 0)
                return ret;
@@ -738,8 +743,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        }
 
        case VIDIOC_S_FMT:
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
+               if ((ret = uvc_acquire_privileges(handle)) < 0) {
+            printk("uvc_acquire_privileges error.");
                        return ret;
+               }
 
                return uvc_v4l2_set_format(stream, arg);
 
@@ -907,14 +914,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        }
 
        case VIDIOC_QBUF:
-               if (!uvc_has_privileges(handle))
+               if (!uvc_has_privileges(handle)) {
+            printk("uvcvideo: VIDIOC_QBUF uvc_has_privileges failed\n");
                        return -EBUSY;
+               }
 
                return uvc_queue_buffer(&stream->queue, arg);
 
        case VIDIOC_DQBUF:
-               if (!uvc_has_privileges(handle))
+               if (!uvc_has_privileges(handle)) {
+            printk("uvcvideo: VIDIOC_DQBUF uvc_has_privileges failed\n");
                        return -EBUSY;
+               }
 
                return uvc_dequeue_buffer(&stream->queue, arg,
                        file->f_flags & O_NONBLOCK);
old mode 100644 (file)
new mode 100755 (executable)
index 688598a..2dd8dc9
@@ -684,9 +684,11 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
 
        urb->transfer_buffer_length = stream->urb_size - len;
 }
-
-static void uvc_video_complete(struct urb *urb)
-{
+/* ddl@rock-chips.com : uvc_video_complete is run in_interrupt(), so uvc decode operation delay run in tasklet for
+*    usb host reenable interrupt soon
+*/
+static void uvc_video_complete_fun (struct urb *urb)
+{    
        struct uvc_streaming *stream = urb->context;
        struct uvc_video_queue *queue = &stream->queue;
        struct uvc_buffer *buf = NULL;
@@ -724,6 +726,42 @@ static void uvc_video_complete(struct urb *urb)
                        ret);
        }
 }
+static void uvc_video_complete_tasklet(unsigned long data)
+{
+    struct urb *urb = (struct urb*)data;
+    struct uvc_streaming *stream = urb->context;
+    struct tasklet_struct *tasklet = NULL;
+    int i;
+    
+    uvc_video_complete_fun(urb);
+    for (i = 0; i < UVC_URBS; ++i) {    
+        if (stream->urb[i] == urb) {
+            tasklet = stream->tasklet[i];
+            break;
+        }
+    }
+    
+    return;
+}
+static void uvc_video_complete(struct urb *urb)
+{
+    int i;
+    struct uvc_streaming *stream = urb->context;
+    struct tasklet_struct *tasklet = NULL;
+    
+    for (i = 0; i < UVC_URBS; ++i) {    
+        if (stream->urb[i] == urb) {
+            tasklet = stream->tasklet[i];
+            break;
+        }
+    }
+
+    if (tasklet != NULL) {
+        tasklet_schedule(tasklet);
+    } else {
+        uvc_video_complete_fun(urb);
+    }
+}
 
 /*
  * Free transfer buffers.
@@ -808,6 +846,12 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
                usb_kill_urb(urb);
                usb_free_urb(urb);
                stream->urb[i] = NULL;
+        /* ddl@rock-chips.com */
+        if (stream->tasklet[i]) {
+            tasklet_kill(stream->tasklet[i]);
+            kfree(stream->tasklet[i]);
+            stream->tasklet[i] = NULL;
+        }
        }
 
        if (free_buffers)
@@ -861,6 +905,14 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
                }
 
                stream->urb[i] = urb;
+        /* ddl@rock-chips.com  */
+        stream->tasklet[i] = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+        if (stream->tasklet[i] == NULL) {
+            uvc_printk(KERN_ERR, "device %s requested tasklet memory fail!\n",
+                               stream->dev->name);
+        } else {
+            tasklet_init(stream->tasklet[i], uvc_video_complete_tasklet, (unsigned long)urb);
+        }
        }
 
        return 0;
@@ -912,6 +964,15 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
                urb->transfer_dma = stream->urb_dma[i];
 
                stream->urb[i] = urb;
+
+        /* ddl@rock-chips.com  */
+        stream->tasklet[i] = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+        if (stream->tasklet[i] == NULL) {
+            uvc_printk(KERN_ERR, "device %s requested tasklet memory fail!\n",
+                               stream->dev->name);
+        } else {
+            tasklet_init(stream->tasklet[i], uvc_video_complete_tasklet, (unsigned long)urb);
+        }
        }
 
        return 0;
old mode 100644 (file)
new mode 100755 (executable)
index 64007b9..030dfec
@@ -405,8 +405,11 @@ struct uvc_video_queue {
        struct mutex mutex;     /* protects buffers and mainqueue */
        spinlock_t irqlock;     /* protects irqqueue */
 
+    wait_queue_head_t wait; /* wait if mainqueue is empty */
+
        struct list_head mainqueue;
        struct list_head irqqueue;
+    
 };
 
 struct uvc_video_chain {
@@ -465,6 +468,8 @@ struct uvc_streaming {
        unsigned int urb_size;
 
        __u8 last_fid;
+
+    struct tasklet_struct *tasklet[UVC_URBS];     /* ddl@rock-chips.com */
 };
 
 enum uvc_device_state {