?? mx27_v4l2_capture.c
字號:
g_user_buf = buf; if (down_interruptible(&cam->busy_lock)) return -EINTR; /* Video capture and still image capture are exclusive */ if (cam->capture_on == true) { err = -EBUSY; goto exit0; } /* The CSI-DMA can not do CSC */ if (cam->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) { pr_info("mxc_v4l_read support YUYV pixel format only\n"); err = -EINVAL; goto exit0; } /* The CSI-DMA can not do resize or crop */ if ((cam->v2f.fmt.pix.width != cam->crop_bounds.width) || (cam->v2f.fmt.pix.height != cam->crop_bounds.height)) { pr_info("mxc_v4l_read resize is not supported\n"); pr_info("supported image size width = %d height = %d\n", cam->crop_bounds.width, cam->crop_bounds.height); err = -EINVAL; goto exit0; } if ((cam->crop_current.left != cam->crop_bounds.left) || (cam->crop_current.width != cam->crop_bounds.width) || (cam->crop_current.top != cam->crop_bounds.top) || (cam->crop_current.height != cam->crop_bounds.height)) { pr_info("mxc_v4l_read cropping is not supported\n"); err = -EINVAL; goto exit0; } cam->still_buf_vaddr = dma_alloc_coherent(0, PAGE_ALIGN(CSI_MEM_SIZE), &cam->still_buf, GFP_DMA | GFP_KERNEL); if (!cam->still_buf_vaddr) { pr_info("mxc_v4l_read failed at allocate still_buf\n"); err = -ENOBUFS; goto exit0; } /* Initialize DMA */ g_dma_channel = mxc_dma_request(MXC_DMA_CSI_RX, "CSI RX DMA"); if (g_dma_channel < 0) { pr_debug("mxc_v4l_read failed to request DMA channel\n"); err = -EIO; goto exit1; } err = mxc_dma_callback_set(g_dma_channel, (mxc_dma_callback_t) mxc_csi_dma_callback, (void *)cam); if (err != 0) { pr_debug("mxc_v4l_read failed to set DMA callback\n"); err = -EIO; goto exit2; } g_user_buf = buf; if (cam->v2f.fmt.pix.sizeimage < count) g_user_count = cam->v2f.fmt.pix.sizeimage; else g_user_count = count & ~0x3; tasklet_init(&g_dma_tasklet, mxc_csi_dma_task, (unsigned long)cam); g_dma_status = CSI_DMA_STATUS_DONE; csi_set_callback(mxc_csi_irq_callback, cam); csi_enable_prpif(0); /* clear current SOF first */ csi_clear_status(BIT_SOF_INT); csi_enable_mclk(CSI_MCLK_RAW, true, true); do { g_dma_completed = g_dma_copied = 0; mxc_csi_dma_chaining(cam); cam->still_counter = 0; g_dma_status = CSI_DMA_STATUS_IDLE; if (!wait_event_interruptible_timeout(cam->still_queue, cam->still_counter != 0, 10 * HZ)) { pr_info("mxc_v4l_read timeout counter %x\n", cam->still_counter); err = -ETIME; goto exit3; } if (g_dma_status == CSI_DMA_STATUS_DONE) break; if (retry-- == 0) break; pr_debug("Now retry image capture\n"); } while (1); if (g_dma_status != CSI_DMA_STATUS_DONE) err = -EIO; exit3: csi_enable_prpif(1); g_dma_status = CSI_DMA_STATUS_DONE; csi_set_callback(0, 0); csi_enable_mclk(CSI_MCLK_RAW, false, false); tasklet_kill(&g_dma_tasklet); exit2: mxc_dma_free(g_dma_channel); exit1: dma_free_coherent(0, PAGE_ALIGN(CSI_MEM_SIZE), cam->still_buf_vaddr, cam->still_buf); cam->still_buf = 0; exit0: up(&cam->busy_lock); if (err < 0) return err; else return g_user_count;}#else/*! * V4L interface - read function * * @param file struct file * * @param read buf char * * @param count size_t * @param ppos structure loff_t * * * @return bytes read */static ssize_tmxc_v4l_read(struct file *file, char *buf, size_t count, loff_t * ppos){ int err = 0; u8 *v_address; struct video_device *dev = video_devdata(file); cam_data *cam = dev->priv; if (down_interruptible(&cam->busy_lock)) return -EINTR; /* Video capture and still image capture are exclusive */ if (cam->capture_on == true) { err = -EBUSY; goto exit0; } v_address = dma_alloc_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), &cam->still_buf, GFP_DMA | GFP_KERNEL); if (!v_address) { pr_info("mxc_v4l_read failed at allocate still_buf\n"); err = -ENOBUFS; goto exit0; } if (prp_still_select(cam)) { err = -EIO; goto exit1; } cam->still_counter = 0; if (cam->csi_start(cam)) { err = -EIO; goto exit2; } if (!wait_event_interruptible_timeout(cam->still_queue, cam->still_counter != 0, 10 * HZ)) { pr_info("mxc_v4l_read timeout counter %x\n", cam->still_counter); err = -ETIME; goto exit2; } err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage); exit2: prp_still_deselect(cam); exit1: dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address, cam->still_buf); cam->still_buf = 0; exit0: up(&cam->busy_lock); if (err < 0) return err; else return (cam->v2f.fmt.pix.sizeimage - err);}#endif /* CONFIG_VIDEO_MXC_CSI_DMA *//*! * V4L interface - ioctl function * * @param inode struct inode * * * @param file struct file * * * @param ioctlnr unsigned int * * @param arg void * * * @return 0 success, ENODEV for invalid device instance, * -1 for other errors. */static intmxc_v4l_do_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, void *arg){ struct video_device *dev = video_devdata(file); cam_data *cam = dev->priv; int retval = 0; unsigned long lock_flags; if (!cam) return -EBADF; wait_event_interruptible(cam->power_queue, cam->low_power == false); /* make this _really_ smp-safe */ if (down_interruptible(&cam->busy_lock)) return -EBUSY; switch (ioctlnr) { /*! * V4l2 VIDIOC_QUERYCAP ioctl */ case VIDIOC_QUERYCAP:{ struct v4l2_capability *cap = arg; strcpy(cap->driver, "mxc_v4l2"); cap->version = KERNEL_VERSION(0, 1, 11); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; cap->card[0] = '\0'; cap->bus_info[0] = '\0'; retval = 0; break; } /*! * V4l2 VIDIOC_G_FMT ioctl */ case VIDIOC_G_FMT:{ struct v4l2_format *gf = arg; retval = mxc_v4l2_g_fmt(cam, gf); break; } /*! * V4l2 VIDIOC_S_FMT ioctl */ case VIDIOC_S_FMT:{ struct v4l2_format *sf = arg; retval = mxc_v4l2_s_fmt(cam, sf); break; } /*! * V4l2 VIDIOC_REQBUFS ioctl */ case VIDIOC_REQBUFS:{ struct v4l2_requestbuffers *req = arg; if (req->count > FRAME_NUM) { pr_info("VIDIOC_REQBUFS: not enough buffer\n"); req->count = FRAME_NUM; } if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (req->memory != V4L2_MEMORY_MMAP)) { pr_debug("VIDIOC_REQBUFS: wrong buffer type\n"); retval = -EINVAL; break; } mxc_streamoff(cam); mxc_free_frame_buf(cam); retval = mxc_allocate_frame_buf(cam, req->count); break; } /*! * V4l2 VIDIOC_QUERYBUF ioctl */ case VIDIOC_QUERYBUF:{ struct v4l2_buffer *buf = arg; int index = buf->index; if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { pr_debug ("VIDIOC_QUERYBUFS: wrong buffer type\n"); retval = -EINVAL; break; } memset(buf, 0, sizeof(buf)); buf->index = index; down(&cam->param_lock); retval = mxc_v4l2_buffer_status(cam, buf); up(&cam->param_lock); break; } /*! * V4l2 VIDIOC_QBUF ioctl */ case VIDIOC_QBUF:{ struct v4l2_buffer *buf = arg; int index = buf->index; int overflow = 0; pr_debug("VIDIOC_QBUF: %d\n", buf->index); if (cam->overflow == 1) { cam->enc_enable(cam); cam->overflow = 0; overflow = 1; printk(KERN_INFO "VIDIOC_QBUF - overflow\n"); } spin_lock_irqsave(&cam->int_lock, lock_flags); if ((cam->frame[index].buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED; if ((cam->skip_frame > 0) || (overflow == 1)) { list_add_tail(&cam->frame[index].queue, &cam->working_q); retval = cam->enc_update_eba(cam-> frame[index]. paddress, &cam-> ping_pong_csi); cam->skip_frame = 0; } else { list_add_tail(&cam->frame[index].queue, &cam->ready_q); } } else if (cam->frame[index].buffer.flags & V4L2_BUF_FLAG_QUEUED) { pr_debug ("VIDIOC_QBUF: buffer already queued\n"); } else if (cam->frame[index].buffer. flags & V4L2_BUF_FLAG_DONE) { pr_debug ("VIDIOC_QBUF: overwrite done buffer.\n"); cam->frame[index].buffer.flags &= ~V4L2_BUF_FLAG_DONE; cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED; } buf->flags = cam->frame[index].buffer.flags; spin_unlock_irqrestore(&cam->int_lock, lock_flags); break; } /*! * V4l2 VIDIOC_DQBUF ioctl */ case VIDIOC_DQBUF:{ struct v4l2_buffer *buf = arg; retval = mxc_v4l_dqueue(cam, buf); break; } /*! * V4l2 VIDIOC_STREAMON ioctl */ case VIDIOC_STREAMON:{ cam->capture_on = true; retval = mxc_streamon(cam); break; } /*! * V4l2 VIDIOC_STREAMOFF ioctl */ case VIDIOC_STREAMOFF:{ retval = mxc_streamoff(cam); cam->capture_on = false; break; } /*! * V4l2 VIDIOC_G_CTRL ioctl */ case VIDIOC_G_CTRL:{ retval = mxc_get_v42l_control(cam, arg); break; } /*! * V4l2 VIDIOC_S_CTRL ioctl */ case VIDIOC_S_CTRL:{ retval = mxc_set_v42l_control(cam, arg); break; } /*! * V4l2 VIDIOC_CROPCAP ioctl */ case VIDIOC_CROPCAP:{ struct v4l2_cropcap *cap = arg; if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { retval = -EINVAL; break; } cap->bounds = cam->crop_bounds; cap->defrect = cam->crop_defrect; break; } /*! * V4l2 VIDIOC_G_CROP ioctl */ case VIDIOC_G_CROP:{ struct v4l2_crop *crop = arg; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { retval = -EINVAL; break; } crop->c = cam->crop_current; break; } /*! * V4l2 VIDIOC_S_CROP ioctl */ case VIDIOC_S_CROP:{ struct v4l2_crop *crop = arg; struct v4l2_rect *b = &cam->crop_bounds; int i; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { retval = -EINVAL; break; } crop->c.top = (crop->c.top < b->top) ? b->top : crop->c.top; if (crop->c.top > b->top + b->height) crop->c.top = b->top + b->height - 1; if (crop->c.height > b->top + b->height - crop->c.top) crop->c.height = b->top + b->height - crop->c.top; crop->c.left = (crop->c.left < b->left) ? b->left : crop->c.left; if (crop->c.left > b->left + b->width) crop->c.left = b->left + b->width - 1; if (crop->c.width > b->left - crop->c.left + b->width) crop->c.width = b->left - crop->c.left + b->width; crop->c.width &= ~0x1; /* * MX27 PrP limitation: * The right spare space (CSI_FRAME_X_SIZE * - SOURCE_LINE_STRIDE - PICTURE_X_SIZE)) must be * multiple of 32. * So we tune the crop->c.left value to the closest * desired cropping value and meet the PrP requirement. */ i = ((b->left + b->width) - (crop->c.left + crop->c.width)) % 32; if (i <= 16) { if (crop->c.left + crop->c.width + i <= b->left + b->width) crop->c.left += i; else if (crop->c.left - (32 - i) >= b->left) crop->c.left -= 32 - i; else { retval = -EINVAL; break; } } else { if (crop->c.left - (32 - i) >= b->left) crop->c.left -= 32 - i; else if (crop->c.left + crop->c.width + i <= b->left + b->width) crop->c.left += i; else { retval = -EINVAL; break; } } cam->crop_current = crop->c; break; } /*! * V4l2 VIDIOC_OVERLAY ioctl */ case VIDIOC_OVERLAY:{ int *on = arg; if (*on) { cam->overlay_on = true; retval = start_preview(cam); } if (!*on) { retval = stop_preview(cam); cam->overlay_on = false; } break; } /*! * V4l2 VIDIOC_G_FBUF ioctl */ case VIDIOC_G_FBUF:{ struct v4l2_framebuffer *fb = arg; struct fb_var_screeninfo *var; if (cam->output >= num_registered_fb) { retval = -EINVAL; break; } var = ®istered_fb[cam->output]->var; cam->v4l2_fb.fmt.width = var->xres; cam->v4l2_fb.fmt.height = var->yres; cam->v4l2_fb.fmt.bytesperline = var->xres_virtual * var->bits_per_pixel; cam->v4l2_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; *fb = cam->v4l2_fb; break; } /*! * V4l2 VIDIOC_S_FBUF ioctl
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -