?? mxc_v4l2_output.c
字號:
V4L2_BUF_TYPE_VIDEO_OUTPUT; vout->v4l2_bufs[i].length = PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); vout->v4l2_bufs[i].m.offset = (unsigned long)vout->queue_buf_paddr[i]; vout->v4l2_bufs[i].timestamp.tv_sec = 0; vout->v4l2_bufs[i].timestamp.tv_usec = 0; } break; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = arg; u32 type = buf->type; int index = buf->index; if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (index >= vout->buffer_cnt)) { dev_dbg(vdev->dev, "VIDIOC_QUERYBUFS: incorrect buffer type\n"); retval = -EINVAL; break; } down(&vout->param_lock); memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf)); up(&vout->param_lock); break; } case VIDIOC_QBUF: { struct v4l2_buffer *buf = arg; int index = buf->index; u32 lock_flags; if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (index >= vout->buffer_cnt) || (buf->flags != 0)) { retval = -EINVAL; break; } dev_dbg(vdev->dev, "VIDIOC_QBUF: %d\n", buf->index); spin_lock_irqsave(&g_lock, lock_flags); memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf)); vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED; g_buf_q_cnt++; queue_buf(&vout->ready_q, index); if (vout->state == STATE_STREAM_PAUSED) { unsigned long timeout; index = peek_next_buf(&vout->ready_q); /* if timestamp is 0, then default to 30fps */ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) && (vout->v4l2_bufs[index].timestamp. tv_usec == 0)) timeout = vout->start_jiffies + vout->frame_count * HZ / 30; else timeout = get_jiffies(&vout->v4l2_bufs[index]. timestamp); if (jiffies >= timeout) { dev_dbg(vout->video_dev->dev, "warning: timer timeout already expired.\n"); } vout->output_timer.expires = timeout; dev_dbg(vdev->dev, "QBUF: frame #%u timeout @ %u jiffies, current = %u\n", vout->frame_count, timeout, jiffies); add_timer(&vout->output_timer); vout->state = STATE_STREAM_ON; } spin_unlock_irqrestore(&g_lock, lock_flags); break; } case VIDIOC_DQBUF: { struct v4l2_buffer *buf = arg; int idx; if ((queue_size(&vout->done_q) == 0) && (file->f_flags & O_NONBLOCK)) { retval = -EAGAIN; break; } if (!wait_event_interruptible_timeout(vout->v4l_bufq, queue_size(&vout-> done_q) != 0, 10 * HZ)) { if(dq_timeout_cnt == 0){ dev_dbg(vdev->dev, "VIDIOC_DQBUF: timeout\n"); } dq_timeout++; retval = -ETIME; break; } else if (signal_pending(current)) { if (dq_intr_cnt == 0) { dev_dbg(vdev->dev, "VIDIOC_DQBUF: interrupt received\n"); } dq_intr_cnt++ retval = -ERESTARTSYS; break; } idx = dequeue_buf(&vout->done_q); if (idx == -1) { /* No frame free */ dev_dbg(vdev->dev, "VIDIOC_DQBUF: no free buffers, returning\n"); retval = -EAGAIN; break; } if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) == 0) dev_dbg(vdev->dev, "VIDIOC_DQBUF: buffer in done q, but not " "flagged as done\n"); vout->v4l2_bufs[idx].flags = 0; memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf)); dev_dbg(vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index); break; } case VIDIOC_STREAMON: { retval = mxc_v4l2out_streamon(vout); break; } case VIDIOC_STREAMOFF: { retval = mxc_v4l2out_streamoff(vout); break; } case VIDIOC_G_CTRL: { retval = mxc_get_v42lout_control(vout, arg); break; } case VIDIOC_S_CTRL: { retval = mxc_set_v42lout_control(vout, arg); break; } case VIDIOC_CROPCAP: { struct v4l2_cropcap *cap = arg; if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { retval = -EINVAL; break; } cap->bounds = vout->crop_bounds[vout->cur_disp_output]; cap->defrect = vout->crop_bounds[vout->cur_disp_output]; retval = 0; break; } case VIDIOC_G_CROP: { struct v4l2_crop *crop = arg; if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { retval = -EINVAL; break; } crop->c = vout->crop_current; break; } case VIDIOC_S_CROP: { struct v4l2_crop *crop = arg; struct v4l2_rect *b = &(vout->crop_bounds[vout->cur_disp_output]); if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { retval = -EINVAL; break; } if (crop->c.height < 0) { retval = -EINVAL; break; } if (crop->c.width < 0) { retval = -EINVAL; break; } if (crop->c.top < b->top) crop->c.top = b->top; if (crop->c.top > b->top + b->height) crop->c.top = b->top + b->height; if (crop->c.height > b->top - crop->c.top + b->height) crop->c.height = b->top - crop->c.top + b->height; if (crop->c.left < b->left) crop->c.top = b->left; if (crop->c.left > b->left + b->width) crop->c.top = b->left + b->width; if (crop->c.width > b->left - crop->c.left + b->width) crop->c.width = b->left - crop->c.left + b->width; /* stride line limitation */ crop->c.height -= crop->c.height % 8; crop->c.width -= crop->c.width % 8; vout->crop_current = crop->c; /* Free previously allocated buffer */ if (vout->display_bufs[0]) { mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr, 2, vout->sdc_fg_buf_size); } vout->sdc_fg_buf_size = vout->crop_current.width * vout->crop_current.height; vout->sdc_fg_buf_size *= fmt_to_bpp(SDC_FG_FB_FORMAT) / 8; if ((retval = mxc_allocate_buffers(vout->display_bufs, vout-> display_bufs_vaddr, 2, vout-> sdc_fg_buf_size)) < 0) { dev_err(vdev->dev, "unable to allocate SDC FG buffers\n"); retval = -ENOMEM; break; } break; } case VIDIOC_ENUMOUTPUT: { struct v4l2_output *output = arg; if ((output->index >= 4) || (vout->output_enabled[output->index] == false)) { retval = -EINVAL; break; } if (output->index < 3) { *output = mxc_outputs[MXC_V4L2_OUT_2_ADC]; output->name[4] = '0' + output->index; } else { *output = mxc_outputs[MXC_V4L2_OUT_2_SDC]; } break; } case VIDIOC_G_OUTPUT: { int *p_output_num = arg; *p_output_num = vout->cur_disp_output; break; } case VIDIOC_S_OUTPUT: { int *p_output_num = arg; if ((*p_output_num >= 4) || (vout->output_enabled[*p_output_num] == false)) { retval = -EINVAL; break; } if (vout->state != STATE_STREAM_OFF) { retval = -EBUSY; break; } vout->cur_disp_output = *p_output_num; break; } case VIDIOC_ENUM_FMT: case VIDIOC_TRY_FMT: case VIDIOC_QUERYCTRL: case VIDIOC_G_PARM: case VIDIOC_ENUMSTD: case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_G_TUNER: case VIDIOC_S_TUNER: case VIDIOC_G_FREQUENCY: case VIDIOC_S_FREQUENCY: default: retval = -EINVAL; break; } up(&vout->busy_lock); return retval;}/* * V4L2 interface - ioctl function * * @return None */static intmxc_v4l2out_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);}/*! * V4L2 interface - mmap function * * @param file structure file * * * @param vma structure vm_area_struct * * * @return status 0 Success, EINTR busy lock error, * ENOBUFS remap_page error */static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma){ struct video_device *vdev = file->private_data; unsigned long size = vma->vm_end - vma->vm_start; int res = 0; vout_data *vout = video_get_drvdata(vdev); dev_dbg(vdev->dev, "pgoff=0x%x, start=0x%x, end=0x%x\n", vma->vm_pgoff, vma->vm_start, vma->vm_end); /* make this _really_ smp-safe */ if (down_interruptible(&vout->busy_lock)) return -EINTR; /* make buffers write-thru cacheable */ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) & ~L_PTE_BUFFERABLE); if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot)) { dev_dbg(vdev->dev, "mmap remap_pfn_range failed\n"); res = -ENOBUFS; goto mxc_mmap_exit; } vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ mxc_mmap_exit: up(&vout->busy_lock); return res;}/*! * V4L2 interface - poll function * * @param file structure file * * * @param wait structure poll_table * * * @return status POLLIN | POLLRDNORM */static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait){ struct video_device *dev = file->private_data; vout_data *vout = video_get_drvdata(dev); wait_queue_head_t *queue = NULL; int res = POLLIN | POLLRDNORM; if (down_interruptible(&vout->busy_lock)) return -EINTR; queue = &vout->v4l_bufq; poll_wait(file, queue, wait); up(&vout->busy_lock); return res;}static structfile_operations mxc_v4l2out_fops = { .owner = THIS_MODULE, .open = mxc_v4l2out_open, .release = mxc_v4l2out_close, .ioctl = mxc_v4l2out_ioctl, .mmap = mxc_v4l2out_mmap, .poll = mxc_v4l2out_poll,};static struct video_device mxc_v4l2out_template = { .owner = THIS_MODULE, .name = "MXC Video Output", .type = 0, .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, .hardware = 0, .fops = &mxc_v4l2out_fops, .release = video_device_release,};/*! * Probe routine for the framebuffer driver. It is called during the * driver binding process. The following functions are performed in * this routine: Framebuffer initialization, Memory allocation and * mapping, Framebuffer registration, IPU initialization. * * @return Appropriate error code to the kernel common code */static int mxc_v4l2out_probe(struct platform_device *pdev){ int i; vout_data *vout; /* * Allocate sufficient memory for the fb structure */ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL); if (!vout) return 0; memset(vout, 0, sizeof(vout_data)); vout->video_dev = video_device_alloc(); if (vout->video_dev == NULL) return -1; vout->video_dev->dev = &pdev->dev; vout->video_dev->minor = -1; *(vout->video_dev) = mxc_v4l2out_template; /* register v4l device */ if (video_register_device(vout->video_dev, VFL_TYPE_GRABBER, video_nr) == -1) { dev_dbg(&pdev->dev, "video_register_device failed\n"); return 0; } dev_info(&pdev->dev, "Registered device video%d\n", vout->video_dev->minor & 0x1f); video_set_drvdata(vout->video_dev, vout); init_MUTEX(&vout->param_lock); init_MUTEX(&vout->busy_lock); /* setup outputs and cropping */ vout->cur_disp_output = -1; for (i = 0; i < num_registered_fb; i++) { char *idstr = registered_fb[i]->fix.id; if (strncmp(idstr, "DISP", 4) == 0) { int disp_num = idstr[4] - '0'; if ((disp_num == 3) && (strncmp(idstr, "DISP3 BG", 8) != 0)) { continue; } vout->crop_bounds[disp_num].left = 0; vout->crop_bounds[disp_num].top = 0; vout->crop_bounds[disp_num].width = registered_fb[i]->var.xres; vout->crop_bounds[disp_num].height = registered_fb[i]->var.yres; vout->output_enabled[disp_num] = true; vout->output_fb_num[disp_num] = i; if (vout->cur_disp_output == -1) vout->cur_disp_output = disp_num; } } vout->crop_current = vout->crop_bounds[vout->cur_disp_output]; platform_set_drvdata(pdev, vout); return 0;}static int mxc_v4l2out_remove(struct platform_device *pdev){ vout_data *vout = platform_get_drvdata(pdev); if (vout->video_dev) { if (-1 != vout->video_dev->minor) video_unregister_device(vout->video_dev); else video_device_release(vout->video_dev); vout->video_dev = NULL; } platform_set_drvdata(pdev, NULL); kfree(vout); return 0;}/*! * This structure contains pointers to the power management callback functions. */static struct platform_driver mxc_v4l2out_driver = { .driver = { .name = "MXC Video Output", }, .probe = mxc_v4l2out_probe, .remove = mxc_v4l2out_remove,};static struct platform_device mxc_v4l2out_device = { .name = "MXC Video Output", .id = 0,};/*! * mxc v4l2 init function * */static int mxc_v4l2out_init(void){ u8 err = 0; err = platform_driver_register(&mxc_v4l2out_driver); if (err == 0) { platform_device_register(&mxc_v4l2out_device); } return err;}/*! * mxc v4l2 cleanup function * */static void mxc_v4l2out_clean(void){ video_unregister_device(g_vout->video_dev); platform_driver_unregister(&mxc_v4l2out_driver); platform_device_unregister(&mxc_v4l2out_device); kfree(g_vout); g_vout = NULL;}module_init(mxc_v4l2out_init);module_exit(mxc_v4l2out_clean);module_param(video_nr, int, -1);MODULE_AUTHOR("Freescale Semiconductor, Inc.");MODULE_DESCRIPTION("V4L2-driver for MXC video output");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("video");
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -