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 {