From: ddl <ddl@rockchip.com>
Date: Mon, 20 Jun 2011 07:17:23 +0000 (+0800)
Subject: camera and uvc: support uvc sensor
X-Git-Tag: firefly_0821_release~10084
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=c7fcad114b526e752868e42a53c4fef359fa9c5a;p=firefly-linux-kernel-4.4.55.git

camera and uvc: support uvc sensor
---

diff --git a/drivers/media/video/rk29_camera_oneframe.c b/drivers/media/video/rk29_camera_oneframe.c
index e76d72f7d33f..95aa0758a5fb 100755
--- a/drivers/media/video/rk29_camera_oneframe.c
+++ b/drivers/media/video/rk29_camera_oneframe.c
@@ -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);
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 572b2e93b8d9..9c174400285a 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -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;
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index f854698c4061..da4853b1538f 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -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,
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index a2bdd806efab..4527d6f7c0fe 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -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);
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
old mode 100644
new mode 100755
index 688598a6cf5c..2dd8dc967157
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -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;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
old mode 100644
new mode 100755
index 64007b90bd73..030dfec314ca
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -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 {