diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-14 03:22:22 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-14 03:22:22 (GMT) |
commit | d8c532c40721f7507896d202b8cae3b3642d2b0d (patch) | |
tree | 42b1ce76671eb85324281ed93491432f4523f983 /drivers/media/platform | |
parent | e777d192ffb9f2929d547a2f8a5f65b7db7a9552 (diff) | |
parent | 77c53d0b56264a8fc5844e087ad15fffe20c299d (diff) | |
download | linux-fsl-qoriq-d8c532c40721f7507896d202b8cae3b3642d2b0d.tar.xz |
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Missing MAINTAINERS entries were added for several drivers
- Adds V4L2 support for DMABUF handling, allowing zero-copy buffer
sharing between V4L2 devices and GPU
- Got rid of all warnings when compiling with W=1 on x86
- Add a new driver for Exynos hardware (s3c-camif)
- Several bug fixes, cleanups and driver improvements
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (243 commits)
[media] omap3isp: Replace cpu_is_omap3630() with ISP revision check
[media] omap3isp: Prepare/unprepare clocks before/after enable/disable
[media] omap3isp: preview: Add support for 8-bit formats at the sink pad
[media] omap3isp: Replace printk with dev_*
[media] omap3isp: Find source pad from external entity
[media] omap3isp: Configure CSI-2 phy based on platform data
[media] omap3isp: Add PHY routing configuration
[media] omap3isp: Add CSI configuration registers from control block to ISP resources
[media] omap3isp: Remove unneeded module memory address definitions
[media] omap3isp: Use monotonic timestamps for statistics buffers
[media] uvcvideo: Fix control value clamping for unsigned integer controls
[media] uvcvideo: Mark first output terminal as default video node
[media] uvcvideo: Add VIDIOC_[GS]_PRIORITY support
[media] uvcvideo: Return -ENOTTY for unsupported ioctls
[media] uvcvideo: Set device_caps in VIDIOC_QUERYCAP
[media] uvcvideo: Don't fail when an unsupported format is requested
[media] uvcvideo: Return -EACCES when trying to access a read/write-only control
[media] uvcvideo: Set error_idx properly for extended controls API failures
[media] rtl28xxu: add NOXON DAB/DAB+ USB dongle rev 2
[media] fc2580: write some registers conditionally
...
Diffstat (limited to 'drivers/media/platform')
48 files changed, 4266 insertions, 471 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 181c768..3dcfea6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -109,6 +109,18 @@ config VIDEO_OMAP3_DEBUG ---help--- Enable debug messages on OMAP 3 camera controller driver. +config VIDEO_S3C_CAMIF + tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME + select VIDEOBUF2_DMA_CONTIG + ---help--- + This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera + host interface (CAMIF). + + To compile this driver as a module, choose M here: the module + will be called s3c-camif. + source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/s5p-fimc/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index baaa550..4817d28 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_CODA) += coda.o obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o +obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index cb2eb26..ec476ef 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -1050,19 +1050,7 @@ static struct platform_driver bcap_driver = { .probe = bcap_probe, .remove = __devexit_p(bcap_remove), }; - -static __init int bcap_init(void) -{ - return platform_driver_register(&bcap_driver); -} - -static __exit void bcap_exit(void) -{ - platform_driver_unregister(&bcap_driver); -} - -module_init(bcap_init); -module_exit(bcap_exit); +module_platform_driver(bcap_driver); MODULE_DESCRIPTION("Analog Devices blackfin video capture driver"); MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index cd04ae2..7b8b547 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1540,7 +1540,7 @@ static irqreturn_t coda_irq_handler(int irq, void *data) u32 wr_ptr, start_ptr; struct coda_ctx *ctx; - __cancel_delayed_work(&dev->timeout); + cancel_delayed_work(&dev->timeout); /* read status register to attend the IRQ */ coda_read(dev, CODA_REG_BIT_INT_STATUS); @@ -1877,7 +1877,7 @@ static const struct coda_devtype coda_devdata[] = { static struct platform_device_id coda_platform_ids[] = { { .name = "coda-imx27", .driver_data = CODA_IMX27 }, - { .name = "coda-imx53", .driver_data = CODA_7541 }, + { .name = "coda-imx53", .driver_data = CODA_IMX53 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, coda_platform_ids); diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 78e26d2..3c56037 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -101,7 +101,7 @@ config VIDEO_DM644X_VPBE tristate "DM644X VPBE HW module" depends on ARCH_DAVINCI_DM644x select VIDEO_VPSS_SYSTEM - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG help Enables VPBE modules used for display on a DM644x SoC. diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index ce0e413..030950d 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -1003,7 +1003,7 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev) status = PTR_ERR(ccdc_cfg.mclk); goto fail_nomap; } - if (clk_enable(ccdc_cfg.mclk)) { + if (clk_prepare_enable(ccdc_cfg.mclk)) { status = -ENODEV; goto fail_mclk; } @@ -1014,7 +1014,7 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev) status = PTR_ERR(ccdc_cfg.sclk); goto fail_mclk; } - if (clk_enable(ccdc_cfg.sclk)) { + if (clk_prepare_enable(ccdc_cfg.sclk)) { status = -ENODEV; goto fail_sclk; } @@ -1034,8 +1034,10 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev) printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; fail_sclk: + clk_disable_unprepare(ccdc_cfg.sclk); clk_put(ccdc_cfg.sclk); fail_mclk: + clk_disable_unprepare(ccdc_cfg.mclk); clk_put(ccdc_cfg.mclk); fail_nomap: iounmap(ccdc_cfg.base_addr); @@ -1050,6 +1052,8 @@ static int dm355_ccdc_remove(struct platform_device *pdev) { struct resource *res; + clk_disable_unprepare(ccdc_cfg.sclk); + clk_disable_unprepare(ccdc_cfg.mclk); clk_put(ccdc_cfg.mclk); clk_put(ccdc_cfg.sclk); iounmap(ccdc_cfg.base_addr); diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index ee7942b..0215ab6 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -994,7 +994,7 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev) status = PTR_ERR(ccdc_cfg.mclk); goto fail_nomap; } - if (clk_enable(ccdc_cfg.mclk)) { + if (clk_prepare_enable(ccdc_cfg.mclk)) { status = -ENODEV; goto fail_mclk; } @@ -1005,7 +1005,7 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev) status = PTR_ERR(ccdc_cfg.sclk); goto fail_mclk; } - if (clk_enable(ccdc_cfg.sclk)) { + if (clk_prepare_enable(ccdc_cfg.sclk)) { status = -ENODEV; goto fail_sclk; } @@ -1013,8 +1013,10 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev) printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; fail_sclk: + clk_disable_unprepare(ccdc_cfg.sclk); clk_put(ccdc_cfg.sclk); fail_mclk: + clk_disable_unprepare(ccdc_cfg.mclk); clk_put(ccdc_cfg.mclk); fail_nomap: iounmap(ccdc_cfg.base_addr); @@ -1029,6 +1031,8 @@ static int dm644x_ccdc_remove(struct platform_device *pdev) { struct resource *res; + clk_disable_unprepare(ccdc_cfg.mclk); + clk_disable_unprepare(ccdc_cfg.sclk); clk_put(ccdc_cfg.mclk); clk_put(ccdc_cfg.sclk); iounmap(ccdc_cfg.base_addr); @@ -1046,8 +1050,8 @@ static int dm644x_ccdc_suspend(struct device *dev) /* Disable CCDC */ ccdc_enable(0); /* Disable both master and slave clock */ - clk_disable(ccdc_cfg.mclk); - clk_disable(ccdc_cfg.sclk); + clk_disable_unprepare(ccdc_cfg.mclk); + clk_disable_unprepare(ccdc_cfg.sclk); return 0; } @@ -1055,8 +1059,8 @@ static int dm644x_ccdc_suspend(struct device *dev) static int dm644x_ccdc_resume(struct device *dev) { /* Enable both master and slave clock */ - clk_enable(ccdc_cfg.mclk); - clk_enable(ccdc_cfg.sclk); + clk_prepare_enable(ccdc_cfg.mclk); + clk_prepare_enable(ccdc_cfg.sclk); /* Restore CCDC context */ ccdc_restore_context(); diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index b99d542..2c26c3e 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -1053,7 +1053,7 @@ static int __devinit isif_probe(struct platform_device *pdev) status = PTR_ERR(isif_cfg.mclk); goto fail_mclk; } - if (clk_enable(isif_cfg.mclk)) { + if (clk_prepare_enable(isif_cfg.mclk)) { status = -ENODEV; goto fail_mclk; } @@ -1125,6 +1125,7 @@ fail_nobase_res: i--; } fail_mclk: + clk_disable_unprepare(isif_cfg.mclk); clk_put(isif_cfg.mclk); vpfe_unregister_ccdc_device(&isif_hw_dev); return status; @@ -1145,6 +1146,8 @@ static int isif_remove(struct platform_device *pdev) i++; } vpfe_unregister_ccdc_device(&isif_hw_dev); + clk_disable_unprepare(isif_cfg.mclk); + clk_put(isif_cfg.mclk); return 0; } diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 69d7a58..7f5cf9b 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -612,7 +612,7 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) ret = PTR_ERR(vpbe_dev->dac_clk); goto fail_mutex_unlock; } - if (clk_enable(vpbe_dev->dac_clk)) { + if (clk_prepare_enable(vpbe_dev->dac_clk)) { ret = -ENODEV; goto fail_mutex_unlock; } @@ -759,8 +759,10 @@ fail_kfree_encoders: fail_dev_unregister: v4l2_device_unregister(&vpbe_dev->v4l2_dev); fail_clk_put: - if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { + clk_disable_unprepare(vpbe_dev->dac_clk); clk_put(vpbe_dev->dac_clk); + } fail_mutex_unlock: mutex_unlock(&vpbe_dev->lock); return ret; @@ -777,8 +779,10 @@ fail_mutex_unlock: static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev) { v4l2_device_unregister(&vpbe_dev->v4l2_dev); - if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { + clk_disable_unprepare(vpbe_dev->dac_clk); clk_put(vpbe_dev->dac_clk); + } kfree(vpbe_dev->amp); kfree(vpbe_dev->encoders); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 161c776..2bfde79 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -47,6 +47,9 @@ static int debug; module_param(debug, int, 0644); +static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, + struct vpbe_layer *layer); + static int venc_is_second_field(struct vpbe_display *disp_dev) { struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; @@ -73,10 +76,11 @@ static void vpbe_isr_even_field(struct vpbe_display *disp_obj, if (layer->cur_frm == layer->next_frm) return; ktime_get_ts(&timevalue); - layer->cur_frm->ts.tv_sec = timevalue.tv_sec; - layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC; - layer->cur_frm->state = VIDEOBUF_DONE; - wake_up_interruptible(&layer->cur_frm->done); + layer->cur_frm->vb.v4l2_buf.timestamp.tv_sec = + timevalue.tv_sec; + layer->cur_frm->vb.v4l2_buf.timestamp.tv_usec = + timevalue.tv_nsec / NSEC_PER_USEC; + vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ layer->cur_frm = layer->next_frm; } @@ -99,16 +103,14 @@ static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, * otherwise hold on current frame * Get next from the buffer queue */ - layer->next_frm = list_entry( - layer->dma_queue.next, - struct videobuf_buffer, - queue); + layer->next_frm = list_entry(layer->dma_queue.next, + struct vpbe_disp_buffer, list); /* Remove that from the buffer queue */ - list_del(&layer->next_frm->queue); + list_del(&layer->next_frm->list); spin_unlock(&disp_obj->dma_queue_lock); /* Mark state of the frame to active */ - layer->next_frm->state = VIDEOBUF_ACTIVE; - addr = videobuf_to_dma_contig(layer->next_frm); + layer->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb, 0); osd_device->ops.start_layer(osd_device, layer->layer_info.id, addr, @@ -199,39 +201,29 @@ static irqreturn_t venc_isr(int irq, void *arg) /* * vpbe_buffer_prepare() - * This is the callback function called from videobuf_qbuf() function + * This is the callback function called from vb2_qbuf() function * the buffer is prepared and user space virtual address is converted into * physical address */ -static int vpbe_buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int vpbe_buffer_prepare(struct vb2_buffer *vb) { - struct vpbe_fh *fh = q->priv_data; + struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *q = vb->vb2_queue; struct vpbe_layer *layer = fh->layer; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; unsigned long addr; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_prepare\n"); - /* If buffer is not initialized, initialize it */ - if (VIDEOBUF_NEEDS_INIT == vb->state) { - vb->width = layer->pix_fmt.width; - vb->height = layer->pix_fmt.height; - vb->size = layer->pix_fmt.sizeimage; - vb->field = field; - - ret = videobuf_iolock(q, vb, NULL); - if (ret < 0) { - v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \ - user address\n"); + if (vb->state != VB2_BUF_STATE_ACTIVE && + vb->state != VB2_BUF_STATE_PREPARED) { + vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) return -EINVAL; - } - - addr = videobuf_to_dma_contig(vb); + addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (q->streaming) { if (!IS_ALIGNED(addr, 8)) { v4l2_err(&vpbe_dev->v4l2_dev, @@ -240,7 +232,6 @@ static int vpbe_buffer_prepare(struct videobuf_queue *q, return -EINVAL; } } - vb->state = VIDEOBUF_PREPARED; } return 0; } @@ -249,22 +240,26 @@ static int vpbe_buffer_prepare(struct videobuf_queue *q, * vpbe_buffer_setup() * This function allocates memory for the buffers */ -static int vpbe_buffer_setup(struct videobuf_queue *q, - unsigned int *count, - unsigned int *size) +static int +vpbe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) + { /* Get the file handle object and layer object */ - struct vpbe_fh *fh = q->priv_data; + struct vpbe_fh *fh = vb2_get_drv_priv(vq); struct vpbe_layer *layer = fh->layer; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); - *size = layer->pix_fmt.sizeimage; - /* Store number of buffers allocated in numbuffer member */ - if (*count < VPBE_DEFAULT_NUM_BUFS) - *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + if (*nbuffers < VPBE_DEFAULT_NUM_BUFS) + *nbuffers = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + + *nplanes = 1; + sizes[0] = layer->pix_fmt.sizeimage; + alloc_ctxs[0] = layer->alloc_ctx; return 0; } @@ -273,11 +268,12 @@ static int vpbe_buffer_setup(struct videobuf_queue *q, * vpbe_buffer_queue() * This function adds the buffer to DMA queue */ -static void vpbe_buffer_queue(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void vpbe_buffer_queue(struct vb2_buffer *vb) { /* Get the file handle object and layer object */ - struct vpbe_fh *fh = q->priv_data; + struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpbe_disp_buffer *buf = container_of(vb, + struct vpbe_disp_buffer, vb); struct vpbe_layer *layer = fh->layer; struct vpbe_display *disp = fh->disp_dev; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; @@ -288,39 +284,125 @@ static void vpbe_buffer_queue(struct videobuf_queue *q, /* add the buffer to the DMA queue */ spin_lock_irqsave(&disp->dma_queue_lock, flags); - list_add_tail(&vb->queue, &layer->dma_queue); + list_add_tail(&buf->list, &layer->dma_queue); spin_unlock_irqrestore(&disp->dma_queue_lock, flags); - /* Change state of the buffer */ - vb->state = VIDEOBUF_QUEUED; } /* - * vpbe_buffer_release() - * This function is called from the videobuf layer to free memory allocated to + * vpbe_buf_cleanup() + * This function is called from the vb2 layer to free memory allocated to * the buffers */ -static void vpbe_buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void vpbe_buf_cleanup(struct vb2_buffer *vb) { /* Get the file handle object and layer object */ - struct vpbe_fh *fh = q->priv_data; + struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); struct vpbe_layer *layer = fh->layer; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct vpbe_disp_buffer *buf = container_of(vb, + struct vpbe_disp_buffer, vb); + unsigned long flags; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, - "vpbe_buffer_release\n"); + "vpbe_buf_cleanup\n"); + + spin_lock_irqsave(&layer->irqlock, flags); + if (vb->state == VB2_BUF_STATE_ACTIVE) + list_del_init(&buf->list); + spin_unlock_irqrestore(&layer->irqlock, flags); +} + +static void vpbe_wait_prepare(struct vb2_queue *vq) +{ + struct vpbe_fh *fh = vb2_get_drv_priv(vq); + struct vpbe_layer *layer = fh->layer; + + mutex_unlock(&layer->opslock); +} + +static void vpbe_wait_finish(struct vb2_queue *vq) +{ + struct vpbe_fh *fh = vb2_get_drv_priv(vq); + struct vpbe_layer *layer = fh->layer; + + mutex_lock(&layer->opslock); +} + +static int vpbe_buffer_init(struct vb2_buffer *vb) +{ + struct vpbe_disp_buffer *buf = container_of(vb, + struct vpbe_disp_buffer, vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpbe_fh *fh = vb2_get_drv_priv(vq); + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + /* If buffer queue is empty, return error */ + if (list_empty(&layer->dma_queue)) { + v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EINVAL; + } + /* Get the next frame from the buffer queue */ + layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, + struct vpbe_disp_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&layer->cur_frm->list); + /* Mark state of the current frame to active */ + layer->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + /* Initialize field_id and started member */ + layer->field_id = 0; + + /* Set parameters in OSD and VENC */ + ret = vpbe_set_osd_display_params(fh->disp_dev, layer); + if (ret < 0) + return ret; - if (V4L2_MEMORY_USERPTR != layer->memory) - videobuf_dma_contig_free(q, vb); + /* + * if request format is yuv420 semiplanar, need to + * enable both video windows + */ + layer->started = 1; + layer->layer_first_int = 1; + + return ret; +} + +static int vpbe_stop_streaming(struct vb2_queue *vq) +{ + struct vpbe_fh *fh = vb2_get_drv_priv(vq); + struct vpbe_layer *layer = fh->layer; + + if (!vb2_is_streaming(vq)) + return 0; + + /* release all active buffers */ + while (!list_empty(&layer->dma_queue)) { + layer->next_frm = list_entry(layer->dma_queue.next, + struct vpbe_disp_buffer, list); + list_del(&layer->next_frm->list); + vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); + } - vb->state = VIDEOBUF_NEEDS_INIT; + return 0; } -static struct videobuf_queue_ops video_qops = { - .buf_setup = vpbe_buffer_setup, +static struct vb2_ops video_qops = { + .queue_setup = vpbe_buffer_queue_setup, + .wait_prepare = vpbe_wait_prepare, + .wait_finish = vpbe_wait_finish, + .buf_init = vpbe_buffer_init, .buf_prepare = vpbe_buffer_prepare, + .start_streaming = vpbe_start_streaming, + .stop_streaming = vpbe_stop_streaming, + .buf_cleanup = vpbe_buf_cleanup, .buf_queue = vpbe_buffer_queue, - .buf_release = vpbe_buffer_release, }; static @@ -345,7 +427,7 @@ static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, unsigned long addr; int ret; - addr = videobuf_to_dma_contig(layer->cur_frm); + addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb, 0); /* Set address in the display registers */ osd_device->ops.start_layer(osd_device, layer->layer_info.id, @@ -620,9 +702,12 @@ static int vpbe_display_querycap(struct file *file, void *priv, struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; cap->version = VPBE_DISPLAY_VERSION_CODE; - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); - strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + snprintf(cap->driver, sizeof(cap->driver), "%s", + dev_name(vpbe_dev->pdev)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(vpbe_dev->pdev)); strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); return 0; @@ -1161,7 +1246,7 @@ static int vpbe_display_streamoff(struct file *file, void *priv, osd_device->ops.disable_layer(osd_device, layer->layer_info.id); layer->started = 0; - ret = videobuf_streamoff(&layer->buffer_queue); + ret = vb2_streamoff(&layer->buffer_queue, buf_type); return ret; } @@ -1199,46 +1284,15 @@ static int vpbe_display_streamon(struct file *file, void *priv, } /* - * Call videobuf_streamon to start streaming + * Call vb2_streamon to start streaming * in videobuf */ - ret = videobuf_streamon(&layer->buffer_queue); + ret = vb2_streamon(&layer->buffer_queue, buf_type); if (ret) { v4l2_err(&vpbe_dev->v4l2_dev, - "error in videobuf_streamon\n"); + "error in vb2_streamon\n"); return ret; } - /* If buffer queue is empty, return error */ - if (list_empty(&layer->dma_queue)) { - v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); - goto streamoff; - } - /* Get the next frame from the buffer queue */ - layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, - struct videobuf_buffer, queue); - /* Remove buffer from the buffer queue */ - list_del(&layer->cur_frm->queue); - /* Mark state of the current frame to active */ - layer->cur_frm->state = VIDEOBUF_ACTIVE; - /* Initialize field_id and started member */ - layer->field_id = 0; - - /* Set parameters in OSD and VENC */ - ret = vpbe_set_osd_display_params(disp_dev, layer); - if (ret < 0) - goto streamoff; - - /* - * if request format is yuv420 semiplanar, need to - * enable both video windows - */ - layer->started = 1; - - layer->layer_first_int = 1; - - return ret; -streamoff: - ret = videobuf_streamoff(&layer->buffer_queue); return ret; } @@ -1265,10 +1319,10 @@ static int vpbe_display_dqbuf(struct file *file, void *priv, } if (file->f_flags & O_NONBLOCK) /* Call videobuf_dqbuf for non blocking mode */ - ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1); + ret = vb2_dqbuf(&layer->buffer_queue, buf, 1); else /* Call videobuf_dqbuf for blocking mode */ - ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0); + ret = vb2_dqbuf(&layer->buffer_queue, buf, 0); return ret; } @@ -1295,7 +1349,7 @@ static int vpbe_display_qbuf(struct file *file, void *priv, return -EACCES; } - return videobuf_qbuf(&layer->buffer_queue, p); + return vb2_qbuf(&layer->buffer_queue, p); } static int vpbe_display_querybuf(struct file *file, void *priv, @@ -1304,7 +1358,6 @@ static int vpbe_display_querybuf(struct file *file, void *priv, struct vpbe_fh *fh = file->private_data; struct vpbe_layer *layer = fh->layer; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_QUERYBUF, layer id = %d\n", @@ -1314,11 +1367,8 @@ static int vpbe_display_querybuf(struct file *file, void *priv, v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); return -EINVAL; } - - /* Call videobuf_querybuf to get information */ - ret = videobuf_querybuf(&layer->buffer_queue, buf); - - return ret; + /* Call vb2_querybuf to get information */ + return vb2_querybuf(&layer->buffer_queue, buf); } static int vpbe_display_reqbufs(struct file *file, void *priv, @@ -1327,8 +1377,8 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, struct vpbe_fh *fh = file->private_data; struct vpbe_layer *layer = fh->layer; struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct vb2_queue *q; int ret; - v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n"); if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { @@ -1342,15 +1392,26 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, return -EBUSY; } /* Initialize videobuf queue as per the buffer type */ - videobuf_queue_dma_contig_init(&layer->buffer_queue, - &video_qops, - vpbe_dev->pdev, - &layer->irqlock, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - layer->pix_fmt.field, - sizeof(struct videobuf_buffer), - fh, NULL); + layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); + if (!layer->alloc_ctx) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); + return -EINVAL; + } + q = &layer->buffer_queue; + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fh; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpbe_disp_buffer); + ret = vb2_queue_init(q); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(layer->alloc_ctx); + return ret; + } /* Set io allowed member of file handle to TRUE */ fh->io_allowed = 1; /* Increment io usrs member of layer object to 1 */ @@ -1360,9 +1421,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, /* Initialize buffer queue */ INIT_LIST_HEAD(&layer->dma_queue); /* Allocate buffers */ - ret = videobuf_reqbufs(&layer->buffer_queue, req_buf); - - return ret; + return vb2_reqbufs(q, req_buf); } /* @@ -1381,7 +1440,7 @@ static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma) if (mutex_lock_interruptible(&layer->opslock)) return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&layer->buffer_queue, vma); + ret = vb2_mmap(&layer->buffer_queue, vma); mutex_unlock(&layer->opslock); return ret; } @@ -1398,7 +1457,7 @@ static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n"); if (layer->started) { mutex_lock(&layer->opslock); - err = videobuf_poll_stream(filep, &layer->buffer_queue, wait); + err = vb2_poll(&layer->buffer_queue, filep, wait); mutex_unlock(&layer->opslock); } return err; @@ -1488,8 +1547,8 @@ static int vpbe_display_release(struct file *file) layer->layer_info.id); layer->started = 0; /* Free buffers allocated */ - videobuf_queue_cancel(&layer->buffer_queue); - videobuf_mmap_free(&layer->buffer_queue); + vb2_queue_release(&layer->buffer_queue); + vb2_dma_contig_cleanup_ctx(&layer->buffer_queue); } /* Decrement layer usrs counter */ diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index bba299d..707f243 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -62,7 +62,7 @@ static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset) { struct osd_state *osd = sd; - u32 addr = osd->osd_base + offset; + void __iomem *addr = osd->osd_base + offset; u32 val = readl(addr) | mask; writel(val, addr); @@ -74,7 +74,7 @@ static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset) { struct osd_state *osd = sd; - u32 addr = osd->osd_base + offset; + void __iomem *addr = osd->osd_base + offset; u32 val = readl(addr) & ~mask; writel(val, addr); @@ -87,7 +87,7 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, { struct osd_state *osd = sd; - u32 addr = osd->osd_base + offset; + void __iomem *addr = osd->osd_base + offset; u32 new_val = (readl(addr) & ~mask) | (val & mask); writel(new_val, addr); @@ -1559,8 +1559,7 @@ static int osd_probe(struct platform_device *pdev) ret = -ENODEV; goto free_mem; } - osd->osd_base = (unsigned long)ioremap_nocache(res->start, - osd->osd_size); + osd->osd_base = ioremap_nocache(res->start, osd->osd_size); if (!osd->osd_base) { dev_err(osd->dev, "Unable to map the OSD region\n"); ret = -ENODEV; diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index cff3c0a..0d6cc8e 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -444,7 +444,7 @@ static int __devinit vpif_probe(struct platform_device *pdev) status = PTR_ERR(vpif_clk); goto clk_fail; } - clk_enable(vpif_clk); + clk_prepare_enable(vpif_clk); spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); @@ -460,7 +460,7 @@ fail: static int __devexit vpif_remove(struct platform_device *pdev) { if (vpif_clk) { - clk_disable(vpif_clk); + clk_disable_unprepare(vpif_clk); clk_put(vpif_clk); } @@ -472,13 +472,13 @@ static int __devexit vpif_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int vpif_suspend(struct device *dev) { - clk_disable(vpif_clk); + clk_disable_unprepare(vpif_clk); return 0; } static int vpif_resume(struct device *dev) { - clk_enable(vpif_clk); + clk_prepare_enable(vpif_clk); return 0; } diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index fcabc02..a409cce 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -201,13 +201,16 @@ static void vpif_buffer_queue(struct vb2_buffer *vb) struct vpif_cap_buffer *buf = container_of(vb, struct vpif_cap_buffer, vb); struct common_obj *common; + unsigned long flags; common = &ch->common[VPIF_VIDEO_INDEX]; vpif_dbg(2, debug, "vpif_buffer_queue\n"); + spin_lock_irqsave(&common->irqlock, flags); /* add the buffer to the DMA queue */ list_add_tail(&buf->list, &common->dma_queue); + spin_unlock_irqrestore(&common->irqlock, flags); } /** @@ -278,10 +281,13 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpif = &ch->vpifparams; unsigned long addr = 0; + unsigned long flags; int ret; - /* If buffer queue is empty, return error */ + /* If buffer queue is empty, return error */ + spin_lock_irqsave(&common->irqlock, flags); if (list_empty(&common->dma_queue)) { + spin_unlock_irqrestore(&common->irqlock, flags); vpif_dbg(1, debug, "buffer queue is empty\n"); return -EIO; } @@ -291,6 +297,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) struct vpif_cap_buffer, list); /* Remove buffer from the buffer queue */ list_del(&common->cur_frm->list); + spin_unlock_irqrestore(&common->irqlock, flags); /* Mark state of the current frame to active */ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; /* Initialize field_id and started member */ @@ -362,6 +369,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq) struct vpif_fh *fh = vb2_get_drv_priv(vq); struct channel_obj *ch = fh->channel; struct common_obj *common; + unsigned long flags; if (!vb2_is_streaming(vq)) return 0; @@ -369,12 +377,14 @@ static int vpif_stop_streaming(struct vb2_queue *vq) common = &ch->common[VPIF_VIDEO_INDEX]; /* release all active buffers */ + spin_lock_irqsave(&common->irqlock, flags); while (!list_empty(&common->dma_queue)) { common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); list_del(&common->next_frm->list); vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } + spin_unlock_irqrestore(&common->irqlock, flags); return 0; } @@ -420,10 +430,12 @@ static void vpif_schedule_next_buffer(struct common_obj *common) { unsigned long addr = 0; + spin_lock(&common->irqlock); common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); /* Remove that buffer from the buffer queue */ list_del(&common->next_frm->list); + spin_unlock(&common->irqlock); common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); @@ -468,8 +480,12 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) /* Check the field format */ if (1 == ch->vpifparams.std_info.frm_fmt) { /* Progressive mode */ - if (list_empty(&common->dma_queue)) + spin_lock(&common->irqlock); + if (list_empty(&common->dma_queue)) { + spin_unlock(&common->irqlock); continue; + } + spin_unlock(&common->irqlock); if (!channel_first_int[i][channel_id]) vpif_process_buffer_complete(common); @@ -513,9 +529,13 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) vpif_process_buffer_complete(common); } else if (1 == fid) { /* odd field */ + spin_lock(&common->irqlock); if (list_empty(&common->dma_queue) || - (common->cur_frm != common->next_frm)) + (common->cur_frm != common->next_frm)) { + spin_unlock(&common->irqlock); continue; + } + spin_unlock(&common->irqlock); vpif_schedule_next_buffer(common); } @@ -1004,9 +1024,9 @@ static int vpif_reqbufs(struct file *file, void *priv, /* Initialize videobuf2 queue as per the buffer type */ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (!common->alloc_ctx) { + if (IS_ERR(common->alloc_ctx)) { vpif_err("Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(common->alloc_ctx); } q = &common->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1715,7 +1735,7 @@ vpif_enum_dv_timings(struct file *file, void *priv, int ret; ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); - if (ret == -ENOIOCTLCMD && ret == -ENODEV) + if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -EINVAL; return ret; } @@ -1735,7 +1755,7 @@ vpif_query_dv_timings(struct file *file, void *priv, int ret; ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings); - if (ret == -ENOIOCTLCMD && ret == -ENODEV) + if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -ENODATA; return ret; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index b716fbd..9f2b603 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -177,11 +177,14 @@ static void vpif_buffer_queue(struct vb2_buffer *vb) struct vpif_disp_buffer, vb); struct channel_obj *ch = fh->channel; struct common_obj *common; + unsigned long flags; common = &ch->common[VPIF_VIDEO_INDEX]; /* add the buffer to the DMA queue */ + spin_lock_irqsave(&common->irqlock, flags); list_add_tail(&buf->list, &common->dma_queue); + spin_unlock_irqrestore(&common->irqlock, flags); } /* @@ -246,10 +249,13 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpif = &ch->vpifparams; unsigned long addr = 0; + unsigned long flags; int ret; /* If buffer queue is empty, return error */ + spin_lock_irqsave(&common->irqlock, flags); if (list_empty(&common->dma_queue)) { + spin_unlock_irqrestore(&common->irqlock, flags); vpif_err("buffer queue is empty\n"); return -EIO; } @@ -260,6 +266,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) struct vpif_disp_buffer, list); list_del(&common->cur_frm->list); + spin_unlock_irqrestore(&common->irqlock, flags); /* Mark state of the current frame to active */ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; @@ -330,6 +337,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq) struct vpif_fh *fh = vb2_get_drv_priv(vq); struct channel_obj *ch = fh->channel; struct common_obj *common; + unsigned long flags; if (!vb2_is_streaming(vq)) return 0; @@ -337,12 +345,14 @@ static int vpif_stop_streaming(struct vb2_queue *vq) common = &ch->common[VPIF_VIDEO_INDEX]; /* release all active buffers */ + spin_lock_irqsave(&common->irqlock, flags); while (!list_empty(&common->dma_queue)) { common->next_frm = list_entry(common->dma_queue.next, struct vpif_disp_buffer, list); list_del(&common->next_frm->list); vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } + spin_unlock_irqrestore(&common->irqlock, flags); return 0; } @@ -363,11 +373,13 @@ static void process_progressive_mode(struct common_obj *common) { unsigned long addr = 0; + spin_lock(&common->irqlock); /* Get the next buffer from buffer queue */ common->next_frm = list_entry(common->dma_queue.next, struct vpif_disp_buffer, list); /* Remove that buffer from the buffer queue */ list_del(&common->next_frm->list); + spin_unlock(&common->irqlock); /* Mark status of the buffer as active */ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; @@ -398,16 +410,18 @@ static void process_interlaced_mode(int fid, struct common_obj *common) common->cur_frm = common->next_frm; } else if (1 == fid) { /* odd field */ + spin_lock(&common->irqlock); if (list_empty(&common->dma_queue) || (common->cur_frm != common->next_frm)) { + spin_unlock(&common->irqlock); return; } + spin_unlock(&common->irqlock); /* one field is displayed configure the next * frame if it is available else hold on current * frame */ /* Get next from the buffer queue */ process_progressive_mode(common); - } } @@ -437,8 +451,12 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) continue; if (1 == ch->vpifparams.std_info.frm_fmt) { - if (list_empty(&common->dma_queue)) + spin_lock(&common->irqlock); + if (list_empty(&common->dma_queue)) { + spin_unlock(&common->irqlock); continue; + } + spin_unlock(&common->irqlock); /* Progressive mode */ if (!channel_first_int[i][channel_id]) { @@ -972,9 +990,9 @@ static int vpif_reqbufs(struct file *file, void *priv, } /* Initialize videobuf2 queue as per the buffer type */ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (!common->alloc_ctx) { + if (IS_ERR(common->alloc_ctx)) { vpif_err("Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(common->alloc_ctx); } q = &common->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; @@ -1380,7 +1398,7 @@ vpif_enum_dv_timings(struct file *file, void *priv, int ret; ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); - if (ret == -ENOIOCTLCMD && ret == -ENODEV) + if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -EINVAL; return ret; } diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 19cbb12..cc7b218 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -982,7 +982,7 @@ static void *gsc_get_drv_data(struct platform_device *pdev) match = of_match_node(of_match_ptr(exynos_gsc_match), pdev->dev.of_node); if (match) - driver_data = match->data; + driver_data = (struct gsc_driverdata *)match->data; } else { driver_data = (struct gsc_driverdata *) platform_get_device_id(pdev)->driver_data; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index c065d04..c267c57 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -122,7 +122,7 @@ static void gsc_m2m_device_run(void *priv) struct gsc_ctx *ctx = priv; struct gsc_dev *gsc; unsigned long flags; - u32 ret; + int ret; bool is_set = false; if (WARN(!ctx, "null hardware context\n")) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 31ac4dc..a8ddb0c 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -352,8 +352,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq) return 0; buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); if (prev == NULL) { - list_del(&buf->vb.queue); - list_add_tail(&buf->vb.queue, &vidq->active); + list_move_tail(&buf->vb.queue, &vidq->active); dprintk(1, "Restarting video dma\n"); viu_stop_dma(vidq->dev); @@ -367,8 +366,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq) } else if (prev->vb.width == buf->vb.width && prev->vb.height == buf->vb.height && prev->fmt == buf->fmt) { - list_del(&buf->vb.queue); - list_add_tail(&buf->vb.queue, &vidq->active); + list_move_tail(&buf->vb.queue, &vidq->active); buf->vb.state = VIDEOBUF_ACTIVE; dprintk(2, "[%p/%d] restart_queue - move to active\n", buf, buf->vb.i); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 45164c4..05c560f 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -218,15 +218,14 @@ static void dma_callback(void *data) static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op, int do_callback) { - struct deinterlace_q_data *s_q_data, *d_q_data; + struct deinterlace_q_data *s_q_data; struct vb2_buffer *src_buf, *dst_buf; struct deinterlace_dev *pcdev = ctx->dev; struct dma_chan *chan = pcdev->dma_chan; struct dma_device *dmadev = chan->device; struct dma_async_tx_descriptor *tx; unsigned int s_width, s_height; - unsigned int d_width, d_height; - unsigned int d_size, s_size; + unsigned int s_size; dma_addr_t p_in, p_out; enum dma_ctrl_flags flags; @@ -238,11 +237,6 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op, s_height = s_q_data->height; s_size = s_width * s_height; - d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE); - d_width = d_q_data->width; - d_height = d_q_data->height; - d_size = d_width * d_height; - p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(src_buf, 0); p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(dst_buf, 0); if (!p_in || !p_out) { @@ -1108,17 +1102,5 @@ static struct platform_driver deinterlace_pdrv = { .owner = THIS_MODULE, }, }; - -static void __exit deinterlace_exit(void) -{ - platform_driver_unregister(&deinterlace_pdrv); -} - -static int __init deinterlace_init(void) -{ - return platform_driver_register(&deinterlace_pdrv); -} - -module_init(deinterlace_init); -module_exit(deinterlace_exit); +module_platform_driver(deinterlace_pdrv); diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 2e2121e..7487d72 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -839,7 +839,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds int ret; src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &m2mtest_qops; @@ -850,7 +850,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds return ret; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &m2mtest_qops; diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index bfa6507..6b155d7 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -1013,16 +1013,4 @@ static struct platform_driver emmaprp_pdrv = { .owner = THIS_MODULE, }, }; - -static void __exit emmaprp_exit(void) -{ - platform_driver_unregister(&emmaprp_pdrv); -} - -static int __init emmaprp_init(void) -{ - return platform_driver_register(&emmaprp_pdrv); -} - -module_init(emmaprp_init); -module_exit(emmaprp_exit); +module_platform_driver(emmaprp_pdrv); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 9935040..202d1b4 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1174,13 +1174,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh, /* set default crop and win */ omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win); - /* Save the changes in the overlay strcuture */ - ret = omapvid_init(vout, 0); - if (ret) { - v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n"); - goto s_fmt_vid_out_exit; - } - ret = 0; s_fmt_vid_out_exit: @@ -1684,20 +1677,6 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) omap_dispc_register_isr(omap_vout_isr, vout, mask); - for (j = 0; j < ovid->num_overlays; j++) { - struct omap_overlay *ovl = ovid->overlays[j]; - - if (ovl->get_device(ovl)) { - struct omap_overlay_info info; - ovl->get_overlay_info(ovl, &info); - info.paddr = addr; - if (ovl->set_overlay_info(ovl, &info)) { - ret = -EINVAL; - goto streamon_err1; - } - } - } - /* First save the configuration in ovelray structure */ ret = omapvid_init(vout, addr); if (ret) @@ -2094,11 +2073,12 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev) } video_set_drvdata(vfd, vout); - /* Configure the overlay structure */ - ret = omapvid_init(vid_dev->vouts[k], 0); - if (!ret) - goto success; + dev_info(&pdev->dev, ": registered and initialized" + " video device %d\n", vfd->minor); + if (k == (pdev->num_resources - 1)) + return 0; + continue; error2: if (vout->vid_info.rotation_type == VOUT_ROT_VRFB) omap_vout_release_vrfb(vout); @@ -2108,12 +2088,6 @@ error1: error: kfree(vout); return ret; - -success: - dev_info(&pdev->dev, ": registered and initialized" - " video device %d\n", vfd->minor); - if (k == (pdev->num_resources - 1)) - return 0; } return -ENODEV; diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 7f182f0..a9f6de5 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -103,7 +103,8 @@ static const struct isp_res_mapping isp_res_maps[] = { 1 << OMAP3_ISP_IOMEM_RESZ | 1 << OMAP3_ISP_IOMEM_SBL | 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | - 1 << OMAP3_ISP_IOMEM_CSIPHY2, + 1 << OMAP3_ISP_IOMEM_CSIPHY2 | + 1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, }, { .isp_rev = ISP_REVISION_15_0, @@ -120,7 +121,8 @@ static const struct isp_res_mapping isp_res_maps[] = { 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | 1 << OMAP3_ISP_IOMEM_CSIPHY1 | - 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2, + 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 | + 1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, }, }; @@ -1331,7 +1333,8 @@ void omap3isp_subclk_disable(struct isp_device *isp, * isp_enable_clocks - Enable ISP clocks * @isp: OMAP3 ISP device * - * Return 0 if successful, or clk_enable return value if any of tthem fails. + * Return 0 if successful, or clk_prepare_enable return value if any of them + * fails. */ static int isp_enable_clocks(struct isp_device *isp) { @@ -1348,14 +1351,11 @@ static int isp_enable_clocks(struct isp_device *isp) * has to be twice of what is set on OMAP3430 to get * the required value for cam_mclk */ - if (cpu_is_omap3630()) - divisor = 1; - else - divisor = 2; + divisor = isp->revision == ISP_REVISION_15_0 ? 1 : 2; - r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); + r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); if (r) { - dev_err(isp->dev, "clk_enable cam_ick failed\n"); + dev_err(isp->dev, "failed to enable cam_ick clock\n"); goto out_clk_enable_ick; } r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], @@ -1364,9 +1364,9 @@ static int isp_enable_clocks(struct isp_device *isp) dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); goto out_clk_enable_mclk; } - r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]); + r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]); if (r) { - dev_err(isp->dev, "clk_enable cam_mclk failed\n"); + dev_err(isp->dev, "failed to enable cam_mclk clock\n"); goto out_clk_enable_mclk; } rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); @@ -1374,17 +1374,17 @@ static int isp_enable_clocks(struct isp_device *isp) dev_warn(isp->dev, "unexpected cam_mclk rate:\n" " expected : %d\n" " actual : %ld\n", CM_CAM_MCLK_HZ, rate); - r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]); + r = clk_prepare_enable(isp->clock[ISP_CLK_CSI2_FCK]); if (r) { - dev_err(isp->dev, "clk_enable csi2_fck failed\n"); + dev_err(isp->dev, "failed to enable csi2_fck clock\n"); goto out_clk_enable_csi2_fclk; } return 0; out_clk_enable_csi2_fclk: - clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); + clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); out_clk_enable_mclk: - clk_disable(isp->clock[ISP_CLK_CAM_ICK]); + clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); out_clk_enable_ick: return r; } @@ -1395,9 +1395,9 @@ out_clk_enable_ick: */ static void isp_disable_clocks(struct isp_device *isp) { - clk_disable(isp->clock[ISP_CLK_CAM_ICK]); - clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); - clk_disable(isp->clock[ISP_CLK_CSI2_FCK]); + clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); + clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); + clk_disable_unprepare(isp->clock[ISP_CLK_CSI2_FCK]); } static const char *isp_clocks[] = { @@ -1678,7 +1678,7 @@ isp_register_subdev_group(struct isp_device *isp, adapter = i2c_get_adapter(board_info->i2c_adapter_id); if (adapter == NULL) { - printk(KERN_ERR "%s: Unable to get I2C adapter %d for " + dev_err(isp->dev, "%s: Unable to get I2C adapter %d for " "device %s\n", __func__, board_info->i2c_adapter_id, board_info->board_info->type); @@ -1688,7 +1688,7 @@ isp_register_subdev_group(struct isp_device *isp, subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, board_info->board_info, NULL); if (subdev == NULL) { - printk(KERN_ERR "%s: Unable to register subdev %s\n", + dev_err(isp->dev, "%s: Unable to register subdev %s\n", __func__, board_info->board_info->type); continue; } @@ -1713,7 +1713,7 @@ static int isp_register_entities(struct isp_device *isp) isp->media_dev.link_notify = isp_pipeline_link_notify; ret = media_device_register(&isp->media_dev); if (ret < 0) { - printk(KERN_ERR "%s: Media device registration failed (%d)\n", + dev_err(isp->dev, "%s: Media device registration failed (%d)\n", __func__, ret); return ret; } @@ -1721,7 +1721,7 @@ static int isp_register_entities(struct isp_device *isp) isp->v4l2_dev.mdev = &isp->media_dev; ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); if (ret < 0) { - printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", + dev_err(isp->dev, "%s: V4L2 device registration failed (%d)\n", __func__, ret); goto done; } @@ -1766,6 +1766,7 @@ static int isp_register_entities(struct isp_device *isp) struct media_entity *input; unsigned int flags; unsigned int pad; + unsigned int i; sensor = isp_register_subdev_group(isp, subdevs->subdevs); if (sensor == NULL) @@ -1807,13 +1808,25 @@ static int isp_register_entities(struct isp_device *isp) break; default: - printk(KERN_ERR "%s: invalid interface type %u\n", - __func__, subdevs->interface); + dev_err(isp->dev, "%s: invalid interface type %u\n", + __func__, subdevs->interface); + ret = -EINVAL; + goto done; + } + + for (i = 0; i < sensor->entity.num_pads; i++) { + if (sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + if (i == sensor->entity.num_pads) { + dev_err(isp->dev, + "%s: no source pad in external entity\n", + __func__); ret = -EINVAL; goto done; } - ret = media_entity_create_link(&sensor->entity, 0, input, pad, + ret = media_entity_create_link(&sensor->entity, i, input, pad, flags); if (ret < 0) goto done; @@ -2096,7 +2109,11 @@ static int __devinit isp_probe(struct platform_device *pdev) isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1"); isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2"); - /* Clocks */ + /* Clocks + * + * The ISP clock tree is revision-dependent. We thus need to enable ICLK + * manually to read the revision before calling __omap3isp_get(). + */ ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); if (ret < 0) goto error; @@ -2105,6 +2122,16 @@ static int __devinit isp_probe(struct platform_device *pdev) if (ret < 0) goto error; + ret = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); + if (ret < 0) + goto error; + + isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); + dev_info(isp->dev, "Revision %d.%d found\n", + (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); + + clk_disable(isp->clock[ISP_CLK_CAM_ICK]); + if (__omap3isp_get(isp, false) == NULL) { ret = -ENODEV; goto error; @@ -2115,10 +2142,6 @@ static int __devinit isp_probe(struct platform_device *pdev) goto error_isp; /* Memory resources */ - isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); - dev_info(isp->dev, "Revision %d.%d found\n", - (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); - for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) if (isp->revision == isp_res_maps[m].isp_rev) break; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 8d68669..517d348 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -70,6 +70,8 @@ enum isp_mem_resources { OMAP3_ISP_IOMEM_CSI2C_REGS1, OMAP3_ISP_IOMEM_CSIPHY1, OMAP3_ISP_IOMEM_CSI2C_REGS2, + OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, + OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, OMAP3_ISP_IOMEM_LAST }; @@ -125,9 +127,6 @@ struct isp_reg { struct isp_platform_callback { u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); - int (*csiphy_config)(struct isp_csiphy *phy, - struct isp_csiphy_dphy_cfg *dphy, - struct isp_csiphy_lanes_cfg *lanes); }; /* diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 6a3ff79..783f4b0 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -517,7 +517,7 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2) } while (soft_reset_retries < 5); if (soft_reset_retries == 5) { - printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); + dev_err(isp->dev, "CSI2: Soft reset try count exceeded!\n"); return -EBUSY; } @@ -535,8 +535,8 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2) } while (--i > 0); if (i == 0) { - printk(KERN_ERR - "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); + dev_err(isp->dev, + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); return -EBUSY; } diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 348f67e..3d56b33 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -32,34 +32,92 @@ #include "ispreg.h" #include "ispcsiphy.h" -/* - * csiphy_lanes_config - Configuration of CSIPHY lanes. - * - * Updates HW configuration. - * Called with phy->mutex taken. - */ -static void csiphy_lanes_config(struct isp_csiphy *phy) +static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, + bool ccp2_strobe) { - unsigned int i; - u32 reg; + u32 reg = isp_reg_readl( + phy->isp, OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0); + u32 shift, mode; + + switch (iface) { + case ISP_INTERFACE_CCP2B_PHY1: + reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; + shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; + break; + case ISP_INTERFACE_CSI2C_PHY1: + shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; + mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; + break; + case ISP_INTERFACE_CCP2B_PHY2: + reg |= OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; + shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; + break; + case ISP_INTERFACE_CSI2A_PHY2: + shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; + mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; + break; + } - reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); + /* Select data/clock or data/strobe mode for CCP2 */ + switch (iface) { + case ISP_INTERFACE_CCP2B_PHY1: + case ISP_INTERFACE_CCP2B_PHY2: + if (ccp2_strobe) + mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; + else + mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK; + } - for (i = 0; i < phy->num_data_lanes; i++) { - reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | - ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); - reg |= (phy->lanes.data[i].pol << - ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); - reg |= (phy->lanes.data[i].pos << - ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); + reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift); + reg |= mode << shift; + + isp_reg_writel(phy->isp, reg, + OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0); +} + +static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, + bool ccp2_strobe) +{ + u32 csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ + | OMAP343X_CONTROL_CSIRXFE_RESET; + + /* Only the CCP2B on PHY1 is configurable. */ + if (iface != ISP_INTERFACE_CCP2B_PHY1) + return; + + if (!on) { + isp_reg_writel(phy->isp, 0, + OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0); + return; } - reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | - ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); - reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; - reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; + if (ccp2_strobe) + csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM; - isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); + isp_reg_writel(phy->isp, csirxfe, + OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0); +} + +/* + * Configure OMAP 3 CSI PHY routing. + * @phy: relevant phy device + * @iface: ISP_INTERFACE_* + * @on: power on or off + * @ccp2_strobe: false: data/clock, true: data/strobe + * + * Note that the underlying routing configuration registers are part of the + * control (SCM) register space and part of the CORE power domain on both 3430 + * and 3630, so they will not hold their contents in off-mode. This isn't an + * issue since the MPU power domain is forced on whilst the ISP is in use. + */ +static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on, + bool ccp2_strobe) +{ + if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL] + && on) + return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe); + if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE]) + return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe); } /* @@ -99,7 +157,7 @@ static int csiphy_set_power(struct isp_csiphy *phy, u32 power) } while ((reg != power >> 2) && (retry_count < 100)); if (retry_count == 100) { - printk(KERN_ERR "CSI2 CIO set power failed!\n"); + dev_err(phy->isp->dev, "CSI2 CIO set power failed!\n"); return -EBUSY; } @@ -107,43 +165,28 @@ static int csiphy_set_power(struct isp_csiphy *phy, u32 power) } /* - * csiphy_dphy_config - Configure CSI2 D-PHY parameters. - * - * Called with phy->mutex taken. + * TCLK values are OK at their reset values */ -static void csiphy_dphy_config(struct isp_csiphy *phy) -{ - u32 reg; - - /* Set up ISPCSIPHY_REG0 */ - reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); - - reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | - ISPCSIPHY_REG0_THS_SETTLE_MASK); - reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; - reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; - - isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); - - /* Set up ISPCSIPHY_REG1 */ - reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); - - reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | - ISPCSIPHY_REG1_TCLK_MISS_MASK | - ISPCSIPHY_REG1_TCLK_SETTLE_MASK); - reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; - reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; - reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; - - isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); -} +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 -static int csiphy_config(struct isp_csiphy *phy, - struct isp_csiphy_dphy_cfg *dphy, - struct isp_csiphy_lanes_cfg *lanes) +static int omap3isp_csiphy_config(struct isp_csiphy *phy) { + struct isp_csi2_device *csi2 = phy->csi2; + struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); + struct isp_v4l2_subdevs_group *subdevs = pipe->external->host_priv; + struct isp_csiphy_lanes_cfg *lanes; + int csi2_ddrclk_khz; unsigned int used_lanes = 0; unsigned int i; + u32 reg; + + if (subdevs->interface == ISP_INTERFACE_CCP2B_PHY1 + || subdevs->interface == ISP_INTERFACE_CCP2B_PHY2) + lanes = &subdevs->bus.ccp2.lanecfg; + else + lanes = &subdevs->bus.csi2.lanecfg; /* Clock and data lanes verification */ for (i = 0; i < phy->num_data_lanes; i++) { @@ -162,10 +205,61 @@ static int csiphy_config(struct isp_csiphy *phy, if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) return -EINVAL; - mutex_lock(&phy->mutex); - phy->dphy = *dphy; - phy->lanes = *lanes; - mutex_unlock(&phy->mutex); + /* + * The PHY configuration is lost in off mode, that's not an + * issue since the MPU power domain is forced on whilst the + * ISP is in use. + */ + csiphy_routing_cfg(phy, subdevs->interface, true, + subdevs->bus.ccp2.phy_layer); + + /* DPHY timing configuration */ + /* CSI-2 is DDR and we only count used lanes. */ + csi2_ddrclk_khz = pipe->external_rate / 1000 + / (2 * hweight32(used_lanes)) * pipe->external_width; + + reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG0); + + reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | + ISPCSIPHY_REG0_THS_SETTLE_MASK); + /* THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. */ + reg |= (DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1) + << ISPCSIPHY_REG0_THS_TERM_SHIFT; + /* THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. */ + reg |= (DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3) + << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; + + isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); + + reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG1); + + reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | + ISPCSIPHY_REG1_TCLK_MISS_MASK | + ISPCSIPHY_REG1_TCLK_SETTLE_MASK); + reg |= TCLK_TERM << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; + reg |= TCLK_MISS << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; + reg |= TCLK_SETTLE << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; + + isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); + + /* DPHY lane configuration */ + reg = isp_reg_readl(csi2->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); + + for (i = 0; i < phy->num_data_lanes; i++) { + reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | + ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); + reg |= (lanes->data[i].pol << + ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); + reg |= (lanes->data[i].pos << + ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); + } + + reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | + ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); + reg |= lanes->clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; + reg |= lanes->clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; + + isp_reg_writel(csi2->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); return 0; } @@ -190,8 +284,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy) if (rval < 0) goto done; - csiphy_dphy_config(phy); - csiphy_lanes_config(phy); + rval = omap3isp_csiphy_config(phy); + if (rval < 0) + goto done; rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); if (rval) { @@ -211,6 +306,14 @@ void omap3isp_csiphy_release(struct isp_csiphy *phy) { mutex_lock(&phy->mutex); if (phy->phy_in_use) { + struct isp_csi2_device *csi2 = phy->csi2; + struct isp_pipeline *pipe = + to_isp_pipeline(&csi2->subdev.entity); + struct isp_v4l2_subdevs_group *subdevs = + pipe->external->host_priv; + + csiphy_routing_cfg(phy, subdevs->interface, false, + subdevs->bus.ccp2.phy_layer); csiphy_power_autoswitch_enable(phy, false); csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); regulator_disable(phy->vdd); @@ -227,8 +330,6 @@ int omap3isp_csiphy_init(struct isp_device *isp) struct isp_csiphy *phy1 = &isp->isp_csiphy1; struct isp_csiphy *phy2 = &isp->isp_csiphy2; - isp->platform_cb.csiphy_config = csiphy_config; - phy2->isp = isp; phy2->csi2 = &isp->isp_csi2a; phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h index e93a661..14551fd 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/drivers/media/platform/omap3isp/ispcsiphy.h @@ -32,14 +32,6 @@ struct isp_csi2_device; struct regulator; -struct isp_csiphy_dphy_cfg { - u8 ths_term; - u8 ths_settle; - u8 tclk_term; - unsigned tclk_miss:1; - u8 tclk_settle; -}; - struct isp_csiphy { struct isp_device *isp; struct mutex mutex; /* serialize csiphy configuration */ @@ -52,8 +44,6 @@ struct isp_csiphy { unsigned int phy_regs; u8 num_data_lanes; /* number of CSI2 Data Lanes supported */ - struct isp_csiphy_lanes_cfg lanes; - struct isp_csiphy_dphy_cfg dphy; }; int omap3isp_csiphy_acquire(struct isp_csiphy *phy); diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index e7f9c42..2d759c5 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -74,11 +74,14 @@ static void hist_reset_mem(struct ispstat *hist) static void hist_dma_config(struct ispstat *hist) { + struct isp_device *isp = hist->isp; + hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; hist->dma_config.frame_count = 1; hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; - hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; + hist->dma_config.src_start = isp->mmio_base_phys[OMAP3_ISP_IOMEM_HIST] + + ISPHIST_DATA; hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; } @@ -479,6 +482,8 @@ int omap3isp_hist_init(struct isp_device *isp) return -ENOMEM; memset(hist, 0, sizeof(*hist)); + hist->isp = isp; + if (HIST_CONFIG_DMA) ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", hist_dma_cb, hist, &hist->dma_ch); @@ -496,7 +501,6 @@ int omap3isp_hist_init(struct isp_device *isp) hist->ops = &hist_ops; hist->priv = hist_cfg; hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; - hist->isp = isp; ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); if (ret) { diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 1ae1c09..691b92a 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -200,10 +200,10 @@ static void preview_enable_invalaw(struct isp_prev_device *prev, bool enable) if (enable) isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); + ISPPRV_PCR_INVALAW); else isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); + ISPPRV_PCR_INVALAW); } /* @@ -1014,7 +1014,7 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) /* * preview_config_input_format - Configure the input format * @prev: The preview engine - * @format: Format on the preview engine sink pad + * @info: Sink pad format information * * Enable and configure CFA interpolation for Bayer formats and disable it for * greyscale formats. @@ -1025,22 +1025,29 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) * reordered to support non-GRBG Bayer patterns. */ static void preview_config_input_format(struct isp_prev_device *prev, - const struct v4l2_mbus_framefmt *format) + const struct isp_format_info *info) { struct isp_device *isp = to_isp_device(prev); struct prev_params *params; - switch (format->code) { - case V4L2_MBUS_FMT_SGRBG10_1X10: + if (info->width == 8) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_WIDTH); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_WIDTH); + + switch (info->flavor) { + case V4L2_MBUS_FMT_SGRBG8_1X8: prev->params.cfa_order = 0; break; - case V4L2_MBUS_FMT_SRGGB10_1X10: + case V4L2_MBUS_FMT_SRGGB8_1X8: prev->params.cfa_order = 1; break; - case V4L2_MBUS_FMT_SBGGR10_1X10: + case V4L2_MBUS_FMT_SBGGR8_1X8: prev->params.cfa_order = 2; break; - case V4L2_MBUS_FMT_SGBRG10_1X10: + case V4L2_MBUS_FMT_SGBRG8_1X8: prev->params.cfa_order = 3; break; default: @@ -1081,7 +1088,8 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active) unsigned int elv = prev->crop.top + prev->crop.height - 1; u32 features; - if (format->code != V4L2_MBUS_FMT_Y10_1X10) { + if (format->code != V4L2_MBUS_FMT_Y8_1X8 && + format->code != V4L2_MBUS_FMT_Y10_1X10) { sph -= 2; eph += 2; slv -= 2; @@ -1389,6 +1397,7 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev) static void preview_configure(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); + const struct isp_format_info *info; struct v4l2_mbus_framefmt *format; unsigned long flags; u32 update; @@ -1402,17 +1411,18 @@ static void preview_configure(struct isp_prev_device *prev) /* PREV_PAD_SINK */ format = &prev->formats[PREV_PAD_SINK]; + info = omap3isp_video_format_info(format->code); preview_adjust_bandwidth(prev); - preview_config_input_format(prev, format); + preview_config_input_format(prev, info); preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_CCDC) preview_config_inlineoffset(prev, 0); else - preview_config_inlineoffset(prev, - ALIGN(format->width, 0x20) * 2); + preview_config_inlineoffset(prev, ALIGN(format->width, 0x20) * + info->bpp); preview_setup_hw(prev, update, active); @@ -1709,6 +1719,11 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, /* previewer format descriptions */ static const unsigned int preview_input_fmts[] = { + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_MBUS_FMT_SRGGB8_1X8, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h index e2c57f3..b7d90e6 100644 --- a/drivers/media/platform/omap3isp/ispreg.h +++ b/drivers/media/platform/omap3isp/ispreg.h @@ -29,83 +29,6 @@ #define CM_CAM_MCLK_HZ 172800000 /* Hz */ -/* ISP Submodules offset */ - -#define L4_34XX_BASE 0x48000000 -#define OMAP3430_ISP_BASE (L4_34XX_BASE + 0xBC000) - -#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE -#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) - -#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 -#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CCP2_REG_OFFSET) -#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) - -#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 -#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CCDC_REG_OFFSET) -#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) - -#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 -#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_HIST_REG_OFFSET) -#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) - -#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 -#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_H3A_REG_OFFSET) -#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) - -#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 -#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_PREV_REG_OFFSET) -#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) - -#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 -#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_RESZ_REG_OFFSET) -#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) - -#define OMAP3ISP_SBL_REG_OFFSET 0x1200 -#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_SBL_REG_OFFSET) -#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) - -#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800 -#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSI2A_REGS1_REG_OFFSET) -#define OMAP3ISP_CSI2A_REGS1_REG(offset) \ - (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset)) - -#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970 -#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSIPHY2_REG_OFFSET) -#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset)) - -#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0 -#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSI2A_REGS2_REG_OFFSET) -#define OMAP3ISP_CSI2A_REGS2_REG(offset) \ - (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset)) - -#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00 -#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSI2C_REGS1_REG_OFFSET) -#define OMAP3ISP_CSI2C_REGS1_REG(offset) \ - (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset)) - -#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70 -#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSIPHY1_REG_OFFSET) -#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset)) - -#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0 -#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ - OMAP3ISP_CSI2C_REGS2_REG_OFFSET) -#define OMAP3ISP_CSI2C_REGS2_REG(offset) \ - (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset)) - /* ISP module register offset */ #define ISP_REVISION (0x000) @@ -1583,4 +1506,26 @@ #define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \ (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT) +/* ----------------------------------------------------------------------------- + * CONTROL registers for CSI-2 phy routing + */ + +/* OMAP343X_CONTROL_CSIRXFE */ +#define OMAP343X_CONTROL_CSIRXFE_CSIB_INV (1 << 7) +#define OMAP343X_CONTROL_CSIRXFE_RESENABLE (1 << 8) +#define OMAP343X_CONTROL_CSIRXFE_SELFORM (1 << 10) +#define OMAP343X_CONTROL_CSIRXFE_PWRDNZ (1 << 12) +#define OMAP343X_CONTROL_CSIRXFE_RESET (1 << 13) + +/* OMAP3630_CONTROL_CAMERA_PHY_CTRL */ +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT 2 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT 0 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY 0x0 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE 0x1 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK 0x2 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_GPI 0x3 +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK 0x3 +/* CCP2B: set to receive data from PHY2 instead of PHY1 */ +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2 (1 << 4) + #endif /* OMAP3_ISP_REG_H */ diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index e793986..61e17f9 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -257,7 +257,7 @@ static int isp_stat_buf_queue(struct ispstat *stat) if (!stat->active_buf) return STAT_NO_BUF; - do_gettimeofday(&stat->active_buf->ts); + ktime_get_ts(&stat->active_buf->ts); stat->active_buf->buf_size = stat->buf_size; if (isp_stat_buf_check_magic(stat, stat->active_buf)) { @@ -537,7 +537,8 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, return PTR_ERR(buf); } - data->ts = buf->ts; + data->ts.tv_sec = buf->ts.tv_sec; + data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC; data->config_counter = buf->config_counter; data->frame_number = buf->frame_number; data->buf_size = buf->buf_size; diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index fd15094..9a047c9 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -50,7 +50,7 @@ struct ispstat_buffer { struct iovm_struct *iovm; void *virt_addr; dma_addr_t dma_addr; - struct timeval ts; + struct timespec ts; u32 buf_size; u32 frame_number; u16 config_counter; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 3311d6b..e0d73a6 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1392,7 +1392,8 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); if (ret < 0) - printk(KERN_ERR "%s: could not register video device (%d)\n", + dev_err(video->isp->dev, + "%s: could not register video device (%d)\n", __func__, ret); return ret; diff --git a/drivers/media/platform/s3c-camif/Makefile b/drivers/media/platform/s3c-camif/Makefile new file mode 100644 index 0000000..50bf8c5 --- /dev/null +++ b/drivers/media/platform/s3c-camif/Makefile @@ -0,0 +1,5 @@ +# Makefile for s3c244x/s3c64xx CAMIF driver + +s3c-camif-objs := camif-core.o camif-capture.o camif-regs.o + +obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif.o diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c new file mode 100644 index 0000000..a55793c --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -0,0 +1,1672 @@ +/* + * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver + * + * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> + * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> + * + * Based on drivers/media/platform/s5p-fimc, + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/ratelimit.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "camif-core.h" +#include "camif-regs.h" + +static int debug; +module_param(debug, int, 0644); + +/* Locking: called with vp->camif->slock spinlock held */ +static void camif_cfg_video_path(struct camif_vp *vp) +{ + WARN_ON(s3c_camif_get_scaler_config(vp, &vp->scaler)); + camif_hw_set_scaler(vp); + camif_hw_set_flip(vp); + camif_hw_set_target_format(vp); + camif_hw_set_output_dma(vp); +} + +static void camif_prepare_dma_offset(struct camif_vp *vp) +{ + struct camif_frame *f = &vp->out_frame; + + f->dma_offset.initial = f->rect.top * f->f_width + f->rect.left; + f->dma_offset.line = f->f_width - (f->rect.left + f->rect.width); + + pr_debug("dma_offset: initial: %d, line: %d\n", + f->dma_offset.initial, f->dma_offset.line); +} + +/* Locking: called with camif->slock spinlock held */ +static int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp) +{ + const struct s3c_camif_variant *variant = camif->variant; + + if (camif->sensor.sd == NULL || vp->out_fmt == NULL) + return -EINVAL; + + if (variant->ip_revision == S3C244X_CAMIF_IP_REV) + camif_hw_clear_fifo_overflow(vp); + camif_hw_set_camera_bus(camif); + camif_hw_set_source_format(camif); + camif_hw_set_camera_crop(camif); + camif_hw_set_test_pattern(camif, camif->test_pattern); + if (variant->has_img_effect) + camif_hw_set_effect(camif, camif->colorfx, + camif->colorfx_cb, camif->colorfx_cr); + if (variant->ip_revision == S3C6410_CAMIF_IP_REV) + camif_hw_set_input_path(vp); + camif_cfg_video_path(vp); + vp->state &= ~ST_VP_CONFIG; + + return 0; +} + +/* + * Initialize the video path, only up from the scaler stage. The camera + * input interface set up is skipped. This is useful to enable one of the + * video paths when the other is already running. + * Locking: called with camif->slock spinlock held. + */ +static int s3c_camif_hw_vp_init(struct camif_dev *camif, struct camif_vp *vp) +{ + unsigned int ip_rev = camif->variant->ip_revision; + + if (vp->out_fmt == NULL) + return -EINVAL; + + camif_prepare_dma_offset(vp); + if (ip_rev == S3C244X_CAMIF_IP_REV) + camif_hw_clear_fifo_overflow(vp); + camif_cfg_video_path(vp); + vp->state &= ~ST_VP_CONFIG; + return 0; +} + +static int sensor_set_power(struct camif_dev *camif, int on) +{ + struct cam_sensor *sensor = &camif->sensor; + int err = 0; + + if (!on == camif->sensor.power_count) + err = v4l2_subdev_call(sensor->sd, core, s_power, on); + if (!err) + sensor->power_count += on ? 1 : -1; + + pr_debug("on: %d, power_count: %d, err: %d\n", + on, sensor->power_count, err); + + return err; +} + +static int sensor_set_streaming(struct camif_dev *camif, int on) +{ + struct cam_sensor *sensor = &camif->sensor; + int err = 0; + + if (!on == camif->sensor.stream_count) + err = v4l2_subdev_call(sensor->sd, video, s_stream, on); + if (!err) + sensor->stream_count += on ? 1 : -1; + + pr_debug("on: %d, stream_count: %d, err: %d\n", + on, sensor->stream_count, err); + + return err; +} + +/* + * Reinitialize the driver so it is ready to start streaming again. + * Return any buffers to vb2, perform CAMIF software reset and + * turn off streaming at the data pipeline (sensor) if required. + */ +static int camif_reinitialize(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_buffer *buf; + unsigned long flags; + bool streaming; + + spin_lock_irqsave(&camif->slock, flags); + streaming = vp->state & ST_VP_SENSOR_STREAMING; + + vp->state &= ~(ST_VP_PENDING | ST_VP_RUNNING | ST_VP_OFF | + ST_VP_ABORTING | ST_VP_STREAMING | + ST_VP_SENSOR_STREAMING | ST_VP_LASTIRQ); + + /* Release unused buffers */ + while (!list_empty(&vp->pending_buf_q)) { + buf = camif_pending_queue_pop(vp); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + while (!list_empty(&vp->active_buf_q)) { + buf = camif_active_queue_pop(vp); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + spin_unlock_irqrestore(&camif->slock, flags); + + if (!streaming) + return 0; + + return sensor_set_streaming(camif, 0); +} + +static bool s3c_vp_active(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + unsigned long flags; + bool ret; + + spin_lock_irqsave(&camif->slock, flags); + ret = (vp->state & ST_VP_RUNNING) || (vp->state & ST_VP_PENDING); + spin_unlock_irqrestore(&camif->slock, flags); + + return ret; +} + +static bool camif_is_streaming(struct camif_dev *camif) +{ + unsigned long flags; + bool status; + + spin_lock_irqsave(&camif->slock, flags); + status = camif->stream_count > 0; + spin_unlock_irqrestore(&camif->slock, flags); + + return status; +} + +static int camif_stop_capture(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + unsigned long flags; + int ret; + + if (!s3c_vp_active(vp)) + return 0; + + spin_lock_irqsave(&camif->slock, flags); + vp->state &= ~(ST_VP_OFF | ST_VP_LASTIRQ); + vp->state |= ST_VP_ABORTING; + spin_unlock_irqrestore(&camif->slock, flags); + + ret = wait_event_timeout(vp->irq_queue, + !(vp->state & ST_VP_ABORTING), + msecs_to_jiffies(CAMIF_STOP_TIMEOUT)); + + spin_lock_irqsave(&camif->slock, flags); + + if (ret == 0 && !(vp->state & ST_VP_OFF)) { + /* Timed out, forcibly stop capture */ + vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | + ST_VP_LASTIRQ); + + camif_hw_disable_capture(vp); + camif_hw_enable_scaler(vp, false); + } + + spin_unlock_irqrestore(&camif->slock, flags); + + return camif_reinitialize(vp); +} + +static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb, + struct camif_addr *paddr) +{ + struct camif_frame *frame = &vp->out_frame; + u32 pix_size; + + if (vb == NULL || frame == NULL) + return -EINVAL; + + pix_size = frame->rect.width * frame->rect.height; + + pr_debug("colplanes: %d, pix_size: %u\n", + vp->out_fmt->colplanes, pix_size); + + paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); + + switch (vp->out_fmt->colplanes) { + case 1: + paddr->cb = 0; + paddr->cr = 0; + break; + case 2: + /* decompose Y into Y/Cb */ + paddr->cb = (u32)(paddr->y + pix_size); + paddr->cr = 0; + break; + case 3: + paddr->cb = (u32)(paddr->y + pix_size); + /* decompose Y into Y/Cb/Cr */ + if (vp->out_fmt->color == IMG_FMT_YCBCR422P) + paddr->cr = (u32)(paddr->cb + (pix_size >> 1)); + else /* 420 */ + paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); + + if (vp->out_fmt->color == IMG_FMT_YCRCB420) + swap(paddr->cb, paddr->cr); + break; + default: + return -EINVAL; + } + + pr_debug("DMA address: y: %#x cb: %#x cr: %#x\n", + paddr->y, paddr->cb, paddr->cr); + + return 0; +} + +irqreturn_t s3c_camif_irq_handler(int irq, void *priv) +{ + struct camif_vp *vp = priv; + struct camif_dev *camif = vp->camif; + unsigned int ip_rev = camif->variant->ip_revision; + unsigned int status; + + spin_lock(&camif->slock); + + if (ip_rev == S3C6410_CAMIF_IP_REV) + camif_hw_clear_pending_irq(vp); + + status = camif_hw_get_status(vp); + + if (ip_rev == S3C244X_CAMIF_IP_REV && (status & CISTATUS_OVF_MASK)) { + camif_hw_clear_fifo_overflow(vp); + goto unlock; + } + + if (vp->state & ST_VP_ABORTING) { + if (vp->state & ST_VP_OFF) { + /* Last IRQ */ + vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | + ST_VP_LASTIRQ); + wake_up(&vp->irq_queue); + goto unlock; + } else if (vp->state & ST_VP_LASTIRQ) { + camif_hw_disable_capture(vp); + camif_hw_enable_scaler(vp, false); + camif_hw_set_lastirq(vp, false); + vp->state |= ST_VP_OFF; + } else { + /* Disable capture, enable last IRQ */ + camif_hw_set_lastirq(vp, true); + vp->state |= ST_VP_LASTIRQ; + } + } + + if (!list_empty(&vp->pending_buf_q) && (vp->state & ST_VP_RUNNING) && + !list_empty(&vp->active_buf_q)) { + unsigned int index; + struct camif_buffer *vbuf; + struct timeval *tv; + struct timespec ts; + /* + * Get previous DMA write buffer index: + * 0 => DMA buffer 0, 2; + * 1 => DMA buffer 1, 3. + */ + index = (CISTATUS_FRAMECNT(status) + 2) & 1; + + ktime_get_ts(&ts); + vbuf = camif_active_queue_peek(vp, index); + + if (!WARN_ON(vbuf == NULL)) { + /* Dequeue a filled buffer */ + tv = &vbuf->vb.v4l2_buf.timestamp; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + vbuf->vb.v4l2_buf.sequence = vp->frame_sequence++; + vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); + + /* Set up an empty buffer at the DMA engine */ + vbuf = camif_pending_queue_pop(vp); + vbuf->index = index; + camif_hw_set_output_addr(vp, &vbuf->paddr, index); + camif_hw_set_output_addr(vp, &vbuf->paddr, index + 2); + + /* Scheduled in H/W, add to the queue */ + camif_active_queue_add(vp, vbuf); + } + } else if (!(vp->state & ST_VP_ABORTING) && + (vp->state & ST_VP_PENDING)) { + vp->state |= ST_VP_RUNNING; + } + + if (vp->state & ST_VP_CONFIG) { + camif_prepare_dma_offset(vp); + camif_hw_set_camera_crop(camif); + camif_hw_set_scaler(vp); + camif_hw_set_flip(vp); + camif_hw_set_test_pattern(camif, camif->test_pattern); + if (camif->variant->has_img_effect) + camif_hw_set_effect(camif, camif->colorfx, + camif->colorfx_cb, camif->colorfx_cr); + vp->state &= ~ST_VP_CONFIG; + } +unlock: + spin_unlock(&camif->slock); + return IRQ_HANDLED; +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct camif_vp *vp = vb2_get_drv_priv(vq); + struct camif_dev *camif = vp->camif; + unsigned long flags; + int ret; + + /* + * We assume the codec capture path is always activated + * first, before the preview path starts streaming. + * This is required to avoid internal FIFO overflow and + * a need for CAMIF software reset. + */ + spin_lock_irqsave(&camif->slock, flags); + + if (camif->stream_count == 0) { + camif_hw_reset(camif); + ret = s3c_camif_hw_init(camif, vp); + } else { + ret = s3c_camif_hw_vp_init(camif, vp); + } + spin_unlock_irqrestore(&camif->slock, flags); + + if (ret < 0) { + camif_reinitialize(vp); + return ret; + } + + spin_lock_irqsave(&camif->slock, flags); + vp->frame_sequence = 0; + vp->state |= ST_VP_PENDING; + + if (!list_empty(&vp->pending_buf_q) && + (!(vp->state & ST_VP_STREAMING) || + !(vp->state & ST_VP_SENSOR_STREAMING))) { + + camif_hw_enable_scaler(vp, vp->scaler.enable); + camif_hw_enable_capture(vp); + vp->state |= ST_VP_STREAMING; + + if (!(vp->state & ST_VP_SENSOR_STREAMING)) { + vp->state |= ST_VP_SENSOR_STREAMING; + spin_unlock_irqrestore(&camif->slock, flags); + ret = sensor_set_streaming(camif, 1); + if (ret) + v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); + if (debug) + camif_hw_dump_regs(camif, __func__); + + return ret; + } + } + + spin_unlock_irqrestore(&camif->slock, flags); + return 0; +} + +static int stop_streaming(struct vb2_queue *vq) +{ + struct camif_vp *vp = vb2_get_drv_priv(vq); + return camif_stop_capture(vp); +} + +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) +{ + const struct v4l2_pix_format *pix = NULL; + struct camif_vp *vp = vb2_get_drv_priv(vq); + struct camif_dev *camif = vp->camif; + struct camif_frame *frame = &vp->out_frame; + const struct camif_fmt *fmt = vp->out_fmt; + unsigned int size; + + if (pfmt) { + pix = &pfmt->fmt.pix; + fmt = s3c_camif_find_format(vp, &pix->pixelformat, -1); + size = (pix->width * pix->height * fmt->depth) / 8; + } else { + size = (frame->f_width * frame->f_height * fmt->depth) / 8; + } + + if (fmt == NULL) + return -EINVAL; + *num_planes = 1; + + if (pix) + sizes[0] = max(size, pix->sizeimage); + else + sizes[0] = size; + allocators[0] = camif->alloc_ctx; + + pr_debug("size: %u\n", sizes[0]); + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); + + if (vp->out_fmt == NULL) + return -EINVAL; + + if (vb2_plane_size(vb, 0) < vp->payload) { + v4l2_err(&vp->vdev, "buffer too small: %lu, required: %u\n", + vb2_plane_size(vb, 0), vp->payload); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, vp->payload); + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct camif_buffer *buf = container_of(vb, struct camif_buffer, vb); + struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); + struct camif_dev *camif = vp->camif; + unsigned long flags; + + spin_lock_irqsave(&camif->slock, flags); + WARN_ON(camif_prepare_addr(vp, &buf->vb, &buf->paddr)); + + if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) { + /* Schedule an empty buffer in H/W */ + buf->index = vp->buf_index; + + camif_hw_set_output_addr(vp, &buf->paddr, buf->index); + camif_hw_set_output_addr(vp, &buf->paddr, buf->index + 2); + + camif_active_queue_add(vp, buf); + vp->buf_index = !vp->buf_index; + } else { + camif_pending_queue_add(vp, buf); + } + + if (vb2_is_streaming(&vp->vb_queue) && !list_empty(&vp->pending_buf_q) + && !(vp->state & ST_VP_STREAMING)) { + + vp->state |= ST_VP_STREAMING; + camif_hw_enable_scaler(vp, vp->scaler.enable); + camif_hw_enable_capture(vp); + spin_unlock_irqrestore(&camif->slock, flags); + + if (!(vp->state & ST_VP_SENSOR_STREAMING)) { + if (sensor_set_streaming(camif, 1) == 0) + vp->state |= ST_VP_SENSOR_STREAMING; + else + v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); + + if (debug) + camif_hw_dump_regs(camif, __func__); + } + return; + } + spin_unlock_irqrestore(&camif->slock, flags); +} + +static void camif_lock(struct vb2_queue *vq) +{ + struct camif_vp *vp = vb2_get_drv_priv(vq); + mutex_lock(&vp->camif->lock); +} + +static void camif_unlock(struct vb2_queue *vq) +{ + struct camif_vp *vp = vb2_get_drv_priv(vq); + mutex_unlock(&vp->camif->lock); +} + +static const struct vb2_ops s3c_camif_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .wait_prepare = camif_unlock, + .wait_finish = camif_lock, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; + +static int s3c_camif_open(struct file *file) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + int ret; + + pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, + vp->state, vp->owner, task_pid_nr(current)); + + if (mutex_lock_interruptible(&camif->lock)) + return -ERESTARTSYS; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto unlock; + + ret = pm_runtime_get_sync(camif->dev); + if (ret < 0) + goto err_pm; + + ret = sensor_set_power(camif, 1); + if (!ret) + goto unlock; + + pm_runtime_put(camif->dev); +err_pm: + v4l2_fh_release(file); +unlock: + mutex_unlock(&camif->lock); + return ret; +} + +static int s3c_camif_close(struct file *file) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + int ret; + + pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, + vp->state, vp->owner, task_pid_nr(current)); + + mutex_lock(&camif->lock); + + if (vp->owner == file->private_data) { + camif_stop_capture(vp); + vb2_queue_release(&vp->vb_queue); + vp->owner = NULL; + } + + sensor_set_power(camif, 0); + + pm_runtime_put(camif->dev); + ret = v4l2_fh_release(file); + + mutex_unlock(&camif->lock); + return ret; +} + +static unsigned int s3c_camif_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + int ret; + + mutex_lock(&camif->lock); + if (vp->owner && vp->owner != file->private_data) + ret = -EBUSY; + else + ret = vb2_poll(&vp->vb_queue, file, wait); + + mutex_unlock(&camif->lock); + return ret; +} + +static int s3c_camif_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct camif_vp *vp = video_drvdata(file); + int ret; + + if (vp->owner && vp->owner != file->private_data) + ret = -EBUSY; + else + ret = vb2_mmap(&vp->vb_queue, vma); + + return ret; +} + +static const struct v4l2_file_operations s3c_camif_fops = { + .owner = THIS_MODULE, + .open = s3c_camif_open, + .release = s3c_camif_close, + .poll = s3c_camif_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = s3c_camif_mmap, +}; + +/* + * Video node IOCTLs + */ + +static int s3c_camif_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct camif_vp *vp = video_drvdata(file); + + strlcpy(cap->driver, S3C_CAMIF_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, S3C_CAMIF_DRIVER_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s.%d", + dev_name(vp->camif->dev), vp->id); + + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int s3c_camif_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct camif_vp *vp = video_drvdata(file); + struct v4l2_subdev *sensor = vp->camif->sensor.sd; + + if (input->index || sensor == NULL) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(input->name, sensor->name, sizeof(input->name)); + return 0; +} + +static int s3c_camif_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + return i == 0 ? 0 : -EINVAL; +} + +static int s3c_camif_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int s3c_camif_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct camif_vp *vp = video_drvdata(file); + const struct camif_fmt *fmt; + + fmt = s3c_camif_find_format(vp, NULL, f->index); + if (!fmt) + return -EINVAL; + + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + pr_debug("fmt(%d): %s\n", f->index, f->description); + return 0; +} + +static int s3c_camif_vidioc_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct camif_vp *vp = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct camif_frame *frame = &vp->out_frame; + const struct camif_fmt *fmt = vp->out_fmt; + + pix->bytesperline = frame->f_width * fmt->ybpp; + pix->sizeimage = vp->payload; + + pix->pixelformat = fmt->fourcc; + pix->width = frame->f_width; + pix->height = frame->f_height; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_JPEG; + + return 0; +} + +static int __camif_video_try_format(struct camif_vp *vp, + struct v4l2_pix_format *pix, + const struct camif_fmt **ffmt) +{ + struct camif_dev *camif = vp->camif; + struct v4l2_rect *crop = &camif->camif_crop; + unsigned int wmin, hmin, sc_hrmax, sc_vrmax; + const struct vp_pix_limits *pix_lim; + const struct camif_fmt *fmt; + + fmt = s3c_camif_find_format(vp, &pix->pixelformat, 0); + + if (WARN_ON(fmt == NULL)) + return -EINVAL; + + if (ffmt) + *ffmt = fmt; + + pix_lim = &camif->variant->vp_pix_limits[vp->id]; + + pr_debug("fmt: %ux%u, crop: %ux%u, bytesperline: %u\n", + pix->width, pix->height, crop->width, crop->height, + pix->bytesperline); + /* + * Calculate minimum width and height according to the configured + * camera input interface crop rectangle and the resizer's capabilities. + */ + sc_hrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->width) - 3)); + sc_vrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->height) - 1)); + + wmin = max_t(u32, pix_lim->min_out_width, crop->width / sc_hrmax); + wmin = round_up(wmin, pix_lim->out_width_align); + hmin = max_t(u32, 8, crop->height / sc_vrmax); + hmin = round_up(hmin, 8); + + v4l_bound_align_image(&pix->width, wmin, pix_lim->max_sc_out_width, + ffs(pix_lim->out_width_align) - 1, + &pix->height, hmin, pix_lim->max_height, 0, 0); + + pix->bytesperline = pix->width * fmt->ybpp; + pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8; + pix->pixelformat = fmt->fourcc; + pix->colorspace = V4L2_COLORSPACE_JPEG; + pix->field = V4L2_FIELD_NONE; + + pr_debug("%ux%u, wmin: %d, hmin: %d, sc_hrmax: %d, sc_vrmax: %d\n", + pix->width, pix->height, wmin, hmin, sc_hrmax, sc_vrmax); + + return 0; +} + +static int s3c_camif_vidioc_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct camif_vp *vp = video_drvdata(file); + return __camif_video_try_format(vp, &f->fmt.pix, NULL); +} + +static int s3c_camif_vidioc_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct camif_vp *vp = video_drvdata(file); + struct camif_frame *out_frame = &vp->out_frame; + const struct camif_fmt *fmt = NULL; + int ret; + + pr_debug("[vp%d]\n", vp->id); + + if (vb2_is_busy(&vp->vb_queue)) + return -EBUSY; + + ret = __camif_video_try_format(vp, &f->fmt.pix, &fmt); + if (ret < 0) + return ret; + + vp->out_fmt = fmt; + vp->payload = pix->sizeimage; + out_frame->f_width = pix->width; + out_frame->f_height = pix->height; + + /* Reset composition rectangle */ + out_frame->rect.width = pix->width; + out_frame->rect.height = pix->height; + out_frame->rect.left = 0; + out_frame->rect.top = 0; + + if (vp->owner == NULL) + vp->owner = priv; + + pr_debug("%ux%u. payload: %u. fmt: %s. %d %d. sizeimage: %d. bpl: %d\n", + out_frame->f_width, out_frame->f_height, vp->payload, fmt->name, + pix->width * pix->height * fmt->depth, fmt->depth, + pix->sizeimage, pix->bytesperline); + + return 0; +} + +/* Only check pixel formats at the sensor and the camif subdev pads */ +static int camif_pipeline_validate(struct camif_dev *camif) +{ + struct v4l2_subdev_format src_fmt; + struct media_pad *pad; + int ret; + + /* Retrieve format at the sensor subdev source pad */ + pad = media_entity_remote_source(&camif->pads[0]); + if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return -EPIPE; + + src_fmt.pad = pad->index; + src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + if (src_fmt.format.width != camif->mbus_fmt.width || + src_fmt.format.height != camif->mbus_fmt.height || + src_fmt.format.code != camif->mbus_fmt.code) + return -EPIPE; + + return 0; +} + +static int s3c_camif_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + struct media_entity *sensor = &camif->sensor.sd->entity; + int ret; + + pr_debug("[vp%d]\n", vp->id); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + if (s3c_vp_active(vp)) + return 0; + + ret = media_entity_pipeline_start(sensor, camif->m_pipeline); + if (ret < 0) + return ret; + + ret = camif_pipeline_validate(camif); + if (ret < 0) { + media_entity_pipeline_stop(sensor); + return ret; + } + + return vb2_streamon(&vp->vb_queue, type); +} + +static int s3c_camif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + int ret; + + pr_debug("[vp%d]\n", vp->id); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + ret = vb2_streamoff(&vp->vb_queue, type); + if (ret == 0) + media_entity_pipeline_stop(&camif->sensor.sd->entity); + return ret; +} + +static int s3c_camif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct camif_vp *vp = video_drvdata(file); + int ret; + + pr_debug("[vp%d] rb count: %d, owner: %p, priv: %p\n", + vp->id, rb->count, vp->owner, priv); + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + if (rb->count) + rb->count = max_t(u32, CAMIF_REQ_BUFS_MIN, rb->count); + else + vp->owner = NULL; + + ret = vb2_reqbufs(&vp->vb_queue, rb); + if (!ret) { + vp->reqbufs_count = rb->count; + if (vp->owner == NULL && rb->count > 0) + vp->owner = priv; + } + + return ret; +} + +static int s3c_camif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct camif_vp *vp = video_drvdata(file); + return vb2_querybuf(&vp->vb_queue, buf); +} + +static int s3c_camif_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct camif_vp *vp = video_drvdata(file); + + pr_debug("[vp%d]\n", vp->id); + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + return vb2_qbuf(&vp->vb_queue, buf); +} + +static int s3c_camif_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct camif_vp *vp = video_drvdata(file); + + pr_debug("[vp%d] sequence: %d\n", vp->id, vp->frame_sequence); + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + return vb2_dqbuf(&vp->vb_queue, buf, file->f_flags & O_NONBLOCK); +} + +static int s3c_camif_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct camif_vp *vp = video_drvdata(file); + int ret; + + if (vp->owner && vp->owner != priv) + return -EBUSY; + + create->count = max_t(u32, 1, create->count); + ret = vb2_create_bufs(&vp->vb_queue, create); + + if (!ret && vp->owner == NULL) + vp->owner = priv; + + return ret; +} + +static int s3c_camif_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct camif_vp *vp = video_drvdata(file); + return vb2_prepare_buf(&vp->vb_queue, b); +} + +static int s3c_camif_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct camif_vp *vp = video_drvdata(file); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = vp->out_frame.f_width; + sel->r.height = vp->out_frame.f_height; + return 0; + + case V4L2_SEL_TGT_COMPOSE: + sel->r = vp->out_frame.rect; + return 0; + } + + return -EINVAL; +} + +static void __camif_try_compose(struct camif_dev *camif, struct camif_vp *vp, + struct v4l2_rect *r) +{ + /* s3c244x doesn't support composition */ + if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { + *r = vp->out_frame.rect; + return; + } + + /* TODO: s3c64xx */ +} + +static int s3c_camif_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct camif_vp *vp = video_drvdata(file); + struct camif_dev *camif = vp->camif; + struct v4l2_rect rect = sel->r; + unsigned long flags; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + __camif_try_compose(camif, vp, &rect); + + sel->r = rect; + spin_lock_irqsave(&camif->slock, flags); + vp->out_frame.rect = rect; + vp->state |= ST_VP_CONFIG; + spin_unlock_irqrestore(&camif->slock, flags); + + pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n", + sel->type, sel->target, sel->flags, + sel->r.left, sel->r.top, sel->r.width, sel->r.height); + + return 0; +} + +static const struct v4l2_ioctl_ops s3c_camif_ioctl_ops = { + .vidioc_querycap = s3c_camif_vidioc_querycap, + .vidioc_enum_input = s3c_camif_vidioc_enum_input, + .vidioc_g_input = s3c_camif_vidioc_g_input, + .vidioc_s_input = s3c_camif_vidioc_s_input, + .vidioc_enum_fmt_vid_cap = s3c_camif_vidioc_enum_fmt, + .vidioc_try_fmt_vid_cap = s3c_camif_vidioc_try_fmt, + .vidioc_s_fmt_vid_cap = s3c_camif_vidioc_s_fmt, + .vidioc_g_fmt_vid_cap = s3c_camif_vidioc_g_fmt, + .vidioc_g_selection = s3c_camif_g_selection, + .vidioc_s_selection = s3c_camif_s_selection, + .vidioc_reqbufs = s3c_camif_reqbufs, + .vidioc_querybuf = s3c_camif_querybuf, + .vidioc_prepare_buf = s3c_camif_prepare_buf, + .vidioc_create_bufs = s3c_camif_create_bufs, + .vidioc_qbuf = s3c_camif_qbuf, + .vidioc_dqbuf = s3c_camif_dqbuf, + .vidioc_streamon = s3c_camif_streamon, + .vidioc_streamoff = s3c_camif_streamoff, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +/* + * Video node controls + */ +static int s3c_camif_video_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct camif_vp *vp = ctrl->priv; + struct camif_dev *camif = vp->camif; + unsigned long flags; + + pr_debug("[vp%d] ctrl: %s, value: %d\n", vp->id, + ctrl->name, ctrl->val); + + spin_lock_irqsave(&camif->slock, flags); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + vp->hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + vp->vflip = ctrl->val; + break; + } + + vp->state |= ST_VP_CONFIG; + spin_unlock_irqrestore(&camif->slock, flags); + return 0; +} + +/* Codec and preview video node control ops */ +static const struct v4l2_ctrl_ops s3c_camif_video_ctrl_ops = { + .s_ctrl = s3c_camif_video_s_ctrl, +}; + +int s3c_camif_register_video_node(struct camif_dev *camif, int idx) +{ + struct camif_vp *vp = &camif->vp[idx]; + struct vb2_queue *q = &vp->vb_queue; + struct video_device *vfd = &vp->vdev; + struct v4l2_ctrl *ctrl; + int ret; + + memset(vfd, 0, sizeof(*vfd)); + snprintf(vfd->name, sizeof(vfd->name), "camif-%s", + vp->id == 0 ? "codec" : "preview"); + + vfd->fops = &s3c_camif_fops; + vfd->ioctl_ops = &s3c_camif_ioctl_ops; + vfd->v4l2_dev = &camif->v4l2_dev; + vfd->minor = -1; + vfd->release = video_device_release_empty; + vfd->lock = &camif->lock; + vp->reqbufs_count = 0; + + INIT_LIST_HEAD(&vp->pending_buf_q); + INIT_LIST_HEAD(&vp->active_buf_q); + + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->ops = &s3c_camif_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct camif_buffer); + q->drv_priv = vp; + + ret = vb2_queue_init(q); + if (ret) + goto err_vd_rel; + + vp->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0); + if (ret) + goto err_vd_rel; + + video_set_drvdata(vfd, vp); + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + + v4l2_ctrl_handler_init(&vp->ctrl_handler, 1); + ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (ctrl) + ctrl->priv = vp; + ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctrl) + ctrl->priv = vp; + + ret = vp->ctrl_handler.error; + if (ret < 0) + goto err_me_cleanup; + + vfd->ctrl_handler = &vp->ctrl_handler; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_ctrlh_free; + + v4l2_info(&camif->v4l2_dev, "registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + return 0; + +err_ctrlh_free: + v4l2_ctrl_handler_free(&vp->ctrl_handler); +err_me_cleanup: + media_entity_cleanup(&vfd->entity); +err_vd_rel: + video_device_release(vfd); + return ret; +} + +void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx) +{ + struct video_device *vfd = &camif->vp[idx].vdev; + + if (video_is_registered(vfd)) { + video_unregister_device(vfd); + media_entity_cleanup(&vfd->entity); + v4l2_ctrl_handler_free(vfd->ctrl_handler); + } +} + +/* Media bus pixel formats supported at the camif input */ +static const enum v4l2_mbus_pixelcode camif_mbus_formats[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_YVYU8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_VYUY8_2X8, +}; + +/* + * Camera input interface subdev operations + */ + +static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(camif_mbus_formats)) + return -EINVAL; + + code->code = camif_mbus_formats[code->index]; + return 0; +} + +static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct camif_dev *camif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + + mutex_lock(&camif->lock); + + switch (fmt->pad) { + case CAMIF_SD_PAD_SINK: + /* full camera input pixel size */ + *mf = camif->mbus_fmt; + break; + + case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: + /* crop rectangle at camera interface input */ + mf->width = camif->camif_crop.width; + mf->height = camif->camif_crop.height; + mf->code = camif->mbus_fmt.code; + break; + } + + mutex_unlock(&camif->lock); + mf->colorspace = V4L2_COLORSPACE_JPEG; + return 0; +} + +static void __camif_subdev_try_format(struct camif_dev *camif, + struct v4l2_mbus_framefmt *mf, int pad) +{ + const struct s3c_camif_variant *variant = camif->variant; + const struct vp_pix_limits *pix_lim; + int i = ARRAY_SIZE(camif_mbus_formats); + + /* FIXME: constraints against codec or preview path ? */ + pix_lim = &variant->vp_pix_limits[VP_CODEC]; + + while (i-- >= 0) + if (camif_mbus_formats[i] == mf->code) + break; + + mf->code = camif_mbus_formats[i]; + + if (pad == CAMIF_SD_PAD_SINK) { + v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, + ffs(pix_lim->out_width_align) - 1, + &mf->height, 8, CAMIF_MAX_PIX_HEIGHT, 0, + 0); + } else { + struct v4l2_rect *crop = &camif->camif_crop; + v4l_bound_align_image(&mf->width, 8, crop->width, + ffs(pix_lim->out_width_align) - 1, + &mf->height, 8, crop->height, + 0, 0); + } + + v4l2_dbg(1, debug, &camif->subdev, "%ux%u\n", mf->width, mf->height); +} + +static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct camif_dev *camif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct v4l2_rect *crop = &camif->camif_crop; + int i; + + v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n", + fmt->pad, mf->code, mf->width, mf->height); + + mf->colorspace = V4L2_COLORSPACE_JPEG; + mutex_lock(&camif->lock); + + /* + * No pixel format change at the camera input is allowed + * while streaming. + */ + if (vb2_is_busy(&camif->vp[VP_CODEC].vb_queue) || + vb2_is_busy(&camif->vp[VP_PREVIEW].vb_queue)) { + mutex_unlock(&camif->lock); + return -EBUSY; + } + + __camif_subdev_try_format(camif, mf, fmt->pad); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + mutex_unlock(&camif->lock); + return 0; + } + + switch (fmt->pad) { + case CAMIF_SD_PAD_SINK: + camif->mbus_fmt = *mf; + /* Reset sink crop rectangle. */ + crop->width = mf->width; + crop->height = mf->height; + crop->left = 0; + crop->top = 0; + /* + * Reset source format (the camif's crop rectangle) + * and the video output resolution. + */ + for (i = 0; i < CAMIF_VP_NUM; i++) { + struct camif_frame *frame = &camif->vp[i].out_frame; + frame->rect = *crop; + frame->f_width = mf->width; + frame->f_height = mf->height; + } + break; + + case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: + /* Pixel format can be only changed on the sink pad. */ + mf->code = camif->mbus_fmt.code; + mf->width = crop->width; + mf->height = crop->height; + break; + } + + mutex_unlock(&camif->lock); + return 0; +} + +static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct camif_dev *camif = v4l2_get_subdevdata(sd); + struct v4l2_rect *crop = &camif->camif_crop; + struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; + + if ((sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || + sel->pad != CAMIF_SD_PAD_SINK) + return -EINVAL; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + return 0; + } + + mutex_lock(&camif->lock); + + if (sel->target == V4L2_SEL_TGT_CROP) { + sel->r = *crop; + } else { /* crop bounds */ + sel->r.width = mf->width; + sel->r.height = mf->height; + sel->r.left = 0; + sel->r.top = 0; + } + + mutex_unlock(&camif->lock); + + v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n", + __func__, crop->left, crop->top, crop->width, + crop->height, mf->width, mf->height); + + return 0; +} + +static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) +{ + struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; + const struct camif_pix_limits *pix_lim = &camif->variant->pix_limits; + unsigned int left = 2 * r->left; + unsigned int top = 2 * r->top; + + /* + * Following constraints must be met: + * - r->width + 2 * r->left = mf->width; + * - r->height + 2 * r->top = mf->height; + * - crop rectangle size and position must be aligned + * to 8 or 2 pixels, depending on SoC version. + */ + v4l_bound_align_image(&r->width, 0, mf->width, + ffs(pix_lim->win_hor_offset_align) - 1, + &r->height, 0, mf->height, 1, 0); + + v4l_bound_align_image(&left, 0, mf->width - r->width, + ffs(pix_lim->win_hor_offset_align), + &top, 0, mf->height - r->height, 2, 0); + + r->left = left / 2; + r->top = top / 2; + r->width = mf->width - left; + r->height = mf->height - top; + /* + * Make sure we either downscale or upscale both the pixel + * width and height. Just return current crop rectangle if + * this scaler constraint is not met. + */ + if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV && + camif_is_streaming(camif)) { + unsigned int i; + + for (i = 0; i < CAMIF_VP_NUM; i++) { + struct v4l2_rect *or = &camif->vp[i].out_frame.rect; + if ((or->width > r->width) == (or->height > r->height)) + continue; + *r = camif->camif_crop; + pr_debug("Width/height scaling direction limitation\n"); + break; + } + } + + v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n", + r->left, r->top, r->width, r->height, mf->width, mf->height); +} + +static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct camif_dev *camif = v4l2_get_subdevdata(sd); + struct v4l2_rect *crop = &camif->camif_crop; + struct camif_scaler scaler; + + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CAMIF_SD_PAD_SINK) + return -EINVAL; + + mutex_lock(&camif->lock); + __camif_try_crop(camif, &sel->r); + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + } else { + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&camif->slock, flags); + *crop = sel->r; + + for (i = 0; i < CAMIF_VP_NUM; i++) { + struct camif_vp *vp = &camif->vp[i]; + scaler = vp->scaler; + if (s3c_camif_get_scaler_config(vp, &scaler)) + continue; + vp->scaler = scaler; + vp->state |= ST_VP_CONFIG; + } + + spin_unlock_irqrestore(&camif->slock, flags); + } + mutex_unlock(&camif->lock); + + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n", + __func__, crop->left, crop->top, crop->width, crop->height, + camif->mbus_fmt.width, camif->mbus_fmt.height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops s3c_camif_subdev_pad_ops = { + .enum_mbus_code = s3c_camif_subdev_enum_mbus_code, + .get_selection = s3c_camif_subdev_get_selection, + .set_selection = s3c_camif_subdev_set_selection, + .get_fmt = s3c_camif_subdev_get_fmt, + .set_fmt = s3c_camif_subdev_set_fmt, +}; + +static struct v4l2_subdev_ops s3c_camif_subdev_ops = { + .pad = &s3c_camif_subdev_pad_ops, +}; + +static int s3c_camif_subdev_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct camif_dev *camif = container_of(ctrl->handler, struct camif_dev, + ctrl_handler); + unsigned long flags; + + spin_lock_irqsave(&camif->slock, flags); + + switch (ctrl->id) { + case V4L2_CID_COLORFX: + camif->colorfx = camif->ctrl_colorfx->val; + /* Set Cb, Cr */ + switch (ctrl->val) { + case V4L2_COLORFX_SEPIA: + camif->colorfx_cb = 115; + camif->colorfx_cr = 145; + break; + case V4L2_COLORFX_SET_CBCR: + camif->colorfx_cb = camif->ctrl_colorfx_cbcr->val >> 8; + camif->colorfx_cr = camif->ctrl_colorfx_cbcr->val & 0xff; + break; + default: + /* for V4L2_COLORFX_BW and others */ + camif->colorfx_cb = 128; + camif->colorfx_cr = 128; + } + break; + case V4L2_CID_TEST_PATTERN: + camif->test_pattern = camif->ctrl_test_pattern->val; + break; + default: + WARN_ON(1); + } + + camif->vp[VP_CODEC].state |= ST_VP_CONFIG; + camif->vp[VP_PREVIEW].state |= ST_VP_CONFIG; + spin_unlock_irqrestore(&camif->slock, flags); + + return 0; +} + +static const struct v4l2_ctrl_ops s3c_camif_subdev_ctrl_ops = { + .s_ctrl = s3c_camif_subdev_s_ctrl, +}; + +static const char * const s3c_camif_test_pattern_menu[] = { + "Disabled", + "Color bars", + "Horizontal increment", + "Vertical increment", +}; + +int s3c_camif_create_subdev(struct camif_dev *camif) +{ + struct v4l2_ctrl_handler *handler = &camif->ctrl_handler; + struct v4l2_subdev *sd = &camif->subdev; + int ret; + + v4l2_subdev_init(sd, &s3c_camif_subdev_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + strlcpy(sd->name, "S3C-CAMIF", sizeof(sd->name)); + + camif->pads[CAMIF_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; + camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM, + camif->pads, 0); + if (ret) + return ret; + + v4l2_ctrl_handler_init(handler, 3); + camif->ctrl_test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &s3c_camif_subdev_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0, + s3c_camif_test_pattern_menu); + + camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler, + &s3c_camif_subdev_ctrl_ops, + V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, + ~0x981f, V4L2_COLORFX_NONE); + + camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler, + &s3c_camif_subdev_ctrl_ops, + V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); + if (handler->error) { + v4l2_ctrl_handler_free(handler); + media_entity_cleanup(&sd->entity); + return handler->error; + } + + v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx, + V4L2_COLORFX_SET_CBCR, false); + if (!camif->variant->has_img_effect) { + camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED; + camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED; + } + sd->ctrl_handler = handler; + v4l2_set_subdevdata(sd, camif); + + return 0; +} + +void s3c_camif_unregister_subdev(struct camif_dev *camif) +{ + struct v4l2_subdev *sd = &camif->subdev; + + /* Return if not registered */ + if (v4l2_get_subdevdata(sd) == NULL) + return; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&camif->ctrl_handler); + v4l2_set_subdevdata(sd, NULL); +} + +int s3c_camif_set_defaults(struct camif_dev *camif) +{ + unsigned int ip_rev = camif->variant->ip_revision; + int i; + + for (i = 0; i < CAMIF_VP_NUM; i++) { + struct camif_vp *vp = &camif->vp[i]; + struct camif_frame *f = &vp->out_frame; + + vp->camif = camif; + vp->id = i; + vp->offset = camif->variant->vp_offset; + + if (ip_rev == S3C244X_CAMIF_IP_REV) + vp->fmt_flags = i ? FMT_FL_S3C24XX_PREVIEW : + FMT_FL_S3C24XX_CODEC; + else + vp->fmt_flags = FMT_FL_S3C64XX; + + vp->out_fmt = s3c_camif_find_format(vp, NULL, 0); + BUG_ON(vp->out_fmt == NULL); + + memset(f, 0, sizeof(*f)); + f->f_width = CAMIF_DEF_WIDTH; + f->f_height = CAMIF_DEF_HEIGHT; + f->rect.width = CAMIF_DEF_WIDTH; + f->rect.height = CAMIF_DEF_HEIGHT; + + /* Scaler is always enabled */ + vp->scaler.enable = 1; + + vp->payload = (f->f_width * f->f_height * + vp->out_fmt->depth) / 8; + } + + memset(&camif->mbus_fmt, 0, sizeof(camif->mbus_fmt)); + camif->mbus_fmt.width = CAMIF_DEF_WIDTH; + camif->mbus_fmt.height = CAMIF_DEF_HEIGHT; + camif->mbus_fmt.code = camif_mbus_formats[0]; + + memset(&camif->camif_crop, 0, sizeof(camif->camif_crop)); + camif->camif_crop.width = CAMIF_DEF_WIDTH; + camif->camif_crop.height = CAMIF_DEF_HEIGHT; + + return 0; +} diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c new file mode 100644 index 0000000..0dd6537 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -0,0 +1,662 @@ +/* + * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver + * + * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> + * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <media/media-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "camif-core.h" + +static char *camif_clocks[CLK_MAX_NUM] = { + /* HCLK CAMIF clock */ + [CLK_GATE] = "camif", + /* CAMIF / external camera sensor master clock */ + [CLK_CAM] = "camera", +}; + +static const struct camif_fmt camif_formats[] = { + { + .name = "YUV 4:2:2 planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16, + .ybpp = 1, + .color = IMG_FMT_YCBCR422P, + .colplanes = 3, + .flags = FMT_FL_S3C24XX_CODEC | + FMT_FL_S3C64XX, + }, { + .name = "YUV 4:2:0 planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .ybpp = 1, + .color = IMG_FMT_YCBCR420, + .colplanes = 3, + .flags = FMT_FL_S3C24XX_CODEC | + FMT_FL_S3C64XX, + }, { + .name = "YVU 4:2:0 planar, Y/Cr/Cb", + .fourcc = V4L2_PIX_FMT_YVU420, + .depth = 12, + .ybpp = 1, + .color = IMG_FMT_YCRCB420, + .colplanes = 3, + .flags = FMT_FL_S3C24XX_CODEC | + FMT_FL_S3C64XX, + }, { + .name = "RGB565, 16 bpp", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .ybpp = 2, + .color = IMG_FMT_RGB565, + .colplanes = 1, + .flags = FMT_FL_S3C24XX_PREVIEW | + FMT_FL_S3C64XX, + }, { + .name = "XRGB8888, 32 bpp", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .ybpp = 4, + .color = IMG_FMT_XRGB8888, + .colplanes = 1, + .flags = FMT_FL_S3C24XX_PREVIEW | + FMT_FL_S3C64XX, + }, { + .name = "BGR666", + .fourcc = V4L2_PIX_FMT_BGR666, + .depth = 32, + .ybpp = 4, + .color = IMG_FMT_RGB666, + .colplanes = 1, + .flags = FMT_FL_S3C64XX, + } +}; + +/** + * s3c_camif_find_format() - lookup camif color format by fourcc or an index + * @pixelformat: fourcc to match, ignored if null + * @index: index to the camif_formats array, ignored if negative + */ +const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp, + const u32 *pixelformat, + int index) +{ + const struct camif_fmt *fmt, *def_fmt = NULL; + unsigned int i; + int id = 0; + + if (index >= (int)ARRAY_SIZE(camif_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(camif_formats); ++i) { + fmt = &camif_formats[i]; + if (vp && !(vp->fmt_flags & fmt->flags)) + continue; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (index == id) + def_fmt = fmt; + id++; + } + return def_fmt; +} + +static int camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) +{ + unsigned int sh = 6; + + if (src >= 64 * tar) + return -EINVAL; + + while (sh--) { + unsigned int tmp = 1 << sh; + if (src >= tar * tmp) { + *shift = sh, *ratio = tmp; + return 0; + } + } + *shift = 0, *ratio = 1; + return 0; +} + +int s3c_camif_get_scaler_config(struct camif_vp *vp, + struct camif_scaler *scaler) +{ + struct v4l2_rect *camif_crop = &vp->camif->camif_crop; + int source_x = camif_crop->width; + int source_y = camif_crop->height; + int target_x = vp->out_frame.rect.width; + int target_y = vp->out_frame.rect.height; + int ret; + + if (vp->rotation == 90 || vp->rotation == 270) + swap(target_x, target_y); + + ret = camif_get_scaler_factor(source_x, target_x, &scaler->pre_h_ratio, + &scaler->h_shift); + if (ret < 0) + return ret; + + ret = camif_get_scaler_factor(source_y, target_y, &scaler->pre_v_ratio, + &scaler->v_shift); + if (ret < 0) + return ret; + + scaler->pre_dst_width = source_x / scaler->pre_h_ratio; + scaler->pre_dst_height = source_y / scaler->pre_v_ratio; + + scaler->main_h_ratio = (source_x << 8) / (target_x << scaler->h_shift); + scaler->main_v_ratio = (source_y << 8) / (target_y << scaler->v_shift); + + scaler->scaleup_h = (target_x >= source_x); + scaler->scaleup_v = (target_y >= source_y); + + scaler->copy = 0; + + pr_debug("H: ratio: %u, shift: %u. V: ratio: %u, shift: %u.\n", + scaler->pre_h_ratio, scaler->h_shift, + scaler->pre_v_ratio, scaler->v_shift); + + pr_debug("Source: %dx%d, Target: %dx%d, scaleup_h/v: %d/%d\n", + source_x, source_y, target_x, target_y, + scaler->scaleup_h, scaler->scaleup_v); + + return 0; +} + +static int camif_register_sensor(struct camif_dev *camif) +{ + struct s3c_camif_sensor_info *sensor = &camif->pdata.sensor; + struct v4l2_device *v4l2_dev = &camif->v4l2_dev; + struct i2c_adapter *adapter; + struct v4l2_subdev_format format; + struct v4l2_subdev *sd; + int ret; + + camif->sensor.sd = NULL; + + if (sensor->i2c_board_info.addr == 0) + return -EINVAL; + + adapter = i2c_get_adapter(sensor->i2c_bus_num); + if (adapter == NULL) { + v4l2_warn(v4l2_dev, "failed to get I2C adapter %d\n", + sensor->i2c_bus_num); + return -EPROBE_DEFER; + } + + sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, + &sensor->i2c_board_info, NULL); + if (sd == NULL) { + i2c_put_adapter(adapter); + v4l2_warn(v4l2_dev, "failed to acquire subdev %s\n", + sensor->i2c_board_info.type); + return -EPROBE_DEFER; + } + camif->sensor.sd = sd; + + v4l2_info(v4l2_dev, "registered sensor subdevice %s\n", sd->name); + + /* Get initial pixel format and set it at the camif sink pad */ + format.pad = 0; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format); + + if (ret < 0) + return 0; + + format.pad = CAMIF_SD_PAD_SINK; + v4l2_subdev_call(&camif->subdev, pad, set_fmt, NULL, &format); + + v4l2_info(sd, "Initial format from sensor: %dx%d, %#x\n", + format.format.width, format.format.height, + format.format.code); + return 0; +} + +static void camif_unregister_sensor(struct camif_dev *camif) +{ + struct v4l2_subdev *sd = camif->sensor.sd; + struct i2c_client *client = sd ? v4l2_get_subdevdata(sd) : NULL; + struct i2c_adapter *adapter; + + if (client == NULL) + return; + + adapter = client->adapter; + v4l2_device_unregister_subdev(sd); + camif->sensor.sd = NULL; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); +} + +static int camif_create_media_links(struct camif_dev *camif) +{ + int i, ret; + + ret = media_entity_create_link(&camif->sensor.sd->entity, 0, + &camif->subdev.entity, CAMIF_SD_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) { + ret = media_entity_create_link(&camif->subdev.entity, i, + &camif->vp[i - 1].vdev.entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + } + + return ret; +} + +static int camif_register_video_nodes(struct camif_dev *camif) +{ + int ret = s3c_camif_register_video_node(camif, VP_CODEC); + if (ret < 0) + return ret; + + return s3c_camif_register_video_node(camif, VP_PREVIEW); +} + +static void camif_unregister_video_nodes(struct camif_dev *camif) +{ + s3c_camif_unregister_video_node(camif, VP_CODEC); + s3c_camif_unregister_video_node(camif, VP_PREVIEW); +} + +static void camif_unregister_media_entities(struct camif_dev *camif) +{ + camif_unregister_video_nodes(camif); + camif_unregister_sensor(camif); + s3c_camif_unregister_subdev(camif); +} + +/* + * Media device + */ +static int camif_media_dev_register(struct camif_dev *camif) +{ + struct media_device *md = &camif->media_dev; + struct v4l2_device *v4l2_dev = &camif->v4l2_dev; + unsigned int ip_rev = camif->variant->ip_revision; + int ret; + + memset(md, 0, sizeof(*md)); + snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", + ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); + strlcpy(md->bus_info, "platform", sizeof(md->bus_info)); + md->hw_revision = ip_rev; + md->driver_version = KERNEL_VERSION(1, 0, 0); + + md->dev = camif->dev; + + strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name)); + v4l2_dev->mdev = md; + + ret = v4l2_device_register(camif->dev, v4l2_dev); + if (ret < 0) + return ret; + + ret = media_device_register(md); + if (ret < 0) + v4l2_device_unregister(v4l2_dev); + + return ret; +} + +static void camif_clk_put(struct camif_dev *camif) +{ + int i; + + for (i = 0; i < CLK_MAX_NUM; i++) { + if (IS_ERR_OR_NULL(camif->clock[i])) + continue; + clk_unprepare(camif->clock[i]); + clk_put(camif->clock[i]); + } +} + +static int camif_clk_get(struct camif_dev *camif) +{ + int ret, i; + + for (i = 0; i < CLK_MAX_NUM; i++) { + camif->clock[i] = clk_get(camif->dev, camif_clocks[i]); + if (IS_ERR(camif->clock[i])) { + ret = PTR_ERR(camif->clock[i]); + goto err; + } + ret = clk_prepare(camif->clock[i]); + if (ret < 0) { + clk_put(camif->clock[i]); + camif->clock[i] = NULL; + goto err; + } + } + return 0; +err: + camif_clk_put(camif); + dev_err(camif->dev, "failed to get clock: %s\n", + camif_clocks[i]); + return ret; +} + +/* + * The CAMIF device has two relatively independent data processing paths + * that can source data from memory or the common camera input frontend. + * Register interrupts for each data processing path (camif_vp). + */ +static int camif_request_irqs(struct platform_device *pdev, + struct camif_dev *camif) +{ + int irq, ret, i; + + for (i = 0; i < CAMIF_VP_NUM; i++) { + struct camif_vp *vp = &camif->vp[i]; + + init_waitqueue_head(&vp->irq_queue); + + irq = platform_get_irq(pdev, i); + if (irq <= 0) { + dev_err(&pdev->dev, "failed to get IRQ %d\n", i); + return -ENXIO; + } + + ret = devm_request_irq(&pdev->dev, irq, s3c_camif_irq_handler, + 0, dev_name(&pdev->dev), vp); + if (ret < 0) { + dev_err(&pdev->dev, "failed to install IRQ: %d\n", ret); + break; + } + } + + return ret; +} + +static int s3c_camif_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct s3c_camif_plat_data *pdata = dev->platform_data; + struct s3c_camif_drvdata *drvdata; + struct camif_dev *camif; + struct resource *mres; + int ret = 0; + + camif = devm_kzalloc(dev, sizeof(*camif), GFP_KERNEL); + if (!camif) + return -ENOMEM; + + spin_lock_init(&camif->slock); + mutex_init(&camif->lock); + + camif->dev = dev; + + if (!pdata || !pdata->gpio_get || !pdata->gpio_put) { + dev_err(dev, "wrong platform data\n"); + return -EINVAL; + } + + camif->pdata = *pdata; + drvdata = (void *)platform_get_device_id(pdev)->driver_data; + camif->variant = drvdata->variant; + + mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + camif->io_base = devm_request_and_ioremap(dev, mres); + if (!camif->io_base) { + dev_err(dev, "failed to obtain I/O memory\n"); + return -ENOENT; + } + + ret = camif_request_irqs(pdev, camif); + if (ret < 0) + return ret; + + ret = pdata->gpio_get(); + if (ret < 0) + return ret; + + ret = s3c_camif_create_subdev(camif); + if (ret < 0) + goto err_sd; + + ret = camif_clk_get(camif); + if (ret < 0) + goto err_clk; + + platform_set_drvdata(pdev, camif); + clk_set_rate(camif->clock[CLK_CAM], + camif->pdata.sensor.clock_frequency); + + dev_info(dev, "sensor clock frequency: %lu\n", + clk_get_rate(camif->clock[CLK_CAM])); + /* + * Set initial pixel format, resolution and crop rectangle. + * Must be done before a sensor subdev is registered as some + * settings are overrode with values from sensor subdev. + */ + s3c_camif_set_defaults(camif); + + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_pm; + + /* Initialize contiguous memory allocator */ + camif->alloc_ctx = vb2_dma_contig_init_ctx(dev); + if (IS_ERR(camif->alloc_ctx)) { + ret = PTR_ERR(camif->alloc_ctx); + goto err_alloc; + } + + ret = camif_media_dev_register(camif); + if (ret < 0) + goto err_mdev; + + ret = camif_register_sensor(camif); + if (ret < 0) + goto err_sens; + + ret = v4l2_device_register_subdev(&camif->v4l2_dev, &camif->subdev); + if (ret < 0) + goto err_sens; + + mutex_lock(&camif->media_dev.graph_mutex); + + ret = v4l2_device_register_subdev_nodes(&camif->v4l2_dev); + if (ret < 0) + goto err_unlock; + + ret = camif_register_video_nodes(camif); + if (ret < 0) + goto err_unlock; + + ret = camif_create_media_links(camif); + if (ret < 0) + goto err_unlock; + + mutex_unlock(&camif->media_dev.graph_mutex); + pm_runtime_put(dev); + return 0; + +err_unlock: + mutex_unlock(&camif->media_dev.graph_mutex); +err_sens: + v4l2_device_unregister(&camif->v4l2_dev); + media_device_unregister(&camif->media_dev); + camif_unregister_media_entities(camif); +err_mdev: + vb2_dma_contig_cleanup_ctx(camif->alloc_ctx); +err_alloc: + pm_runtime_put(dev); + pm_runtime_disable(dev); +err_pm: + camif_clk_put(camif); +err_clk: + s3c_camif_unregister_subdev(camif); +err_sd: + pdata->gpio_put(); + return ret; +} + +static int __devexit s3c_camif_remove(struct platform_device *pdev) +{ + struct camif_dev *camif = platform_get_drvdata(pdev); + struct s3c_camif_plat_data *pdata = &camif->pdata; + + media_device_unregister(&camif->media_dev); + camif_unregister_media_entities(camif); + v4l2_device_unregister(&camif->v4l2_dev); + + pm_runtime_disable(&pdev->dev); + camif_clk_put(camif); + pdata->gpio_put(); + + return 0; +} + +static int s3c_camif_runtime_resume(struct device *dev) +{ + struct camif_dev *camif = dev_get_drvdata(dev); + + clk_enable(camif->clock[CLK_GATE]); + /* null op on s3c244x */ + clk_enable(camif->clock[CLK_CAM]); + return 0; +} + +static int s3c_camif_runtime_suspend(struct device *dev) +{ + struct camif_dev *camif = dev_get_drvdata(dev); + + /* null op on s3c244x */ + clk_disable(camif->clock[CLK_CAM]); + + clk_disable(camif->clock[CLK_GATE]); + return 0; +} + +static const struct s3c_camif_variant s3c244x_camif_variant = { + .vp_pix_limits = { + [VP_CODEC] = { + .max_out_width = 4096, + .max_sc_out_width = 2048, + .out_width_align = 16, + .min_out_width = 16, + .max_height = 4096, + }, + [VP_PREVIEW] = { + .max_out_width = 640, + .max_sc_out_width = 640, + .out_width_align = 16, + .min_out_width = 16, + .max_height = 480, + } + }, + .pix_limits = { + .win_hor_offset_align = 8, + }, + .ip_revision = S3C244X_CAMIF_IP_REV, +}; + +static struct s3c_camif_drvdata s3c244x_camif_drvdata = { + .variant = &s3c244x_camif_variant, + .bus_clk_freq = 24000000UL, +}; + +static const struct s3c_camif_variant s3c6410_camif_variant = { + .vp_pix_limits = { + [VP_CODEC] = { + .max_out_width = 4096, + .max_sc_out_width = 2048, + .out_width_align = 16, + .min_out_width = 16, + .max_height = 4096, + }, + [VP_PREVIEW] = { + .max_out_width = 4096, + .max_sc_out_width = 720, + .out_width_align = 16, + .min_out_width = 16, + .max_height = 4096, + } + }, + .pix_limits = { + .win_hor_offset_align = 8, + }, + .ip_revision = S3C6410_CAMIF_IP_REV, + .has_img_effect = 1, + .vp_offset = 0x20, +}; + +static struct s3c_camif_drvdata s3c6410_camif_drvdata = { + .variant = &s3c6410_camif_variant, + .bus_clk_freq = 133000000UL, +}; + +static struct platform_device_id s3c_camif_driver_ids[] = { + { + .name = "s3c2440-camif", + .driver_data = (unsigned long)&s3c244x_camif_drvdata, + }, { + .name = "s3c6410-camif", + .driver_data = (unsigned long)&s3c6410_camif_drvdata, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, s3c_camif_driver_ids); + +static const struct dev_pm_ops s3c_camif_pm_ops = { + .runtime_suspend = s3c_camif_runtime_suspend, + .runtime_resume = s3c_camif_runtime_resume, +}; + +static struct platform_driver s3c_camif_driver = { + .probe = s3c_camif_probe, + .remove = __devexit_p(s3c_camif_remove), + .id_table = s3c_camif_driver_ids, + .driver = { + .name = S3C_CAMIF_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &s3c_camif_pm_ops, + } +}; + +module_platform_driver(s3c_camif_driver); + +MODULE_AUTHOR("Sylwester Nawrocki <sylvester.nawrocki@gmail.com>"); +MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>"); +MODULE_DESCRIPTION("S3C24XX/S3C64XX SoC camera interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h new file mode 100644 index 0000000..261134b --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-core.h @@ -0,0 +1,393 @@ +/* + * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver + * + * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> + * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef CAMIF_CORE_H_ +#define CAMIF_CORE_H_ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/videobuf2-core.h> +#include <media/s3c_camif.h> + +#define S3C_CAMIF_DRIVER_NAME "s3c-camif" +#define CAMIF_REQ_BUFS_MIN 3 +#define CAMIF_MAX_OUT_BUFS 4 +#define CAMIF_MAX_PIX_WIDTH 4096 +#define CAMIF_MAX_PIX_HEIGHT 4096 +#define SCALER_MAX_RATIO 64 +#define CAMIF_DEF_WIDTH 640 +#define CAMIF_DEF_HEIGHT 480 +#define CAMIF_STOP_TIMEOUT 1500 /* ms */ + +#define S3C244X_CAMIF_IP_REV 0x20 /* 2.0 */ +#define S3C2450_CAMIF_IP_REV 0x30 /* 3.0 - not implemented, not tested */ +#define S3C6400_CAMIF_IP_REV 0x31 /* 3.1 - not implemented, not tested */ +#define S3C6410_CAMIF_IP_REV 0x32 /* 3.2 */ + +/* struct camif_vp::state */ + +#define ST_VP_PENDING (1 << 0) +#define ST_VP_RUNNING (1 << 1) +#define ST_VP_STREAMING (1 << 2) +#define ST_VP_SENSOR_STREAMING (1 << 3) + +#define ST_VP_ABORTING (1 << 4) +#define ST_VP_OFF (1 << 5) +#define ST_VP_LASTIRQ (1 << 6) + +#define ST_VP_CONFIG (1 << 8) + +#define CAMIF_SD_PAD_SINK 0 +#define CAMIF_SD_PAD_SOURCE_C 1 +#define CAMIF_SD_PAD_SOURCE_P 2 +#define CAMIF_SD_PADS_NUM 3 + +enum img_fmt { + IMG_FMT_RGB565 = 0x0010, + IMG_FMT_RGB666, + IMG_FMT_XRGB8888, + IMG_FMT_YCBCR420 = 0x0020, + IMG_FMT_YCRCB420, + IMG_FMT_YCBCR422P, + IMG_FMT_YCBYCR422 = 0x0040, + IMG_FMT_YCRYCB422, + IMG_FMT_CBYCRY422, + IMG_FMT_CRYCBY422, +}; + +#define img_fmt_is_rgb(x) ((x) & 0x10) +#define img_fmt_is_ycbcr(x) ((x) & 0x60) + +/* Possible values for struct camif_fmt::flags */ +#define FMT_FL_S3C24XX_CODEC (1 << 0) +#define FMT_FL_S3C24XX_PREVIEW (1 << 1) +#define FMT_FL_S3C64XX (1 << 2) + +/** + * struct camif_fmt - pixel format description + * @fourcc: fourcc code for this format, 0 if not applicable + * @color: a corresponding enum img_fmt + * @colplanes: number of physically contiguous data planes + * @flags: indicate for which SoCs revisions this format is valid + * @depth: bits per pixel (total) + * @ybpp: number of luminance bytes per pixel + */ +struct camif_fmt { + char *name; + u32 fourcc; + u32 color; + u16 colplanes; + u16 flags; + u8 depth; + u8 ybpp; +}; + +/** + * struct camif_dma_offset - pixel offset information for DMA + * @initial: offset (in pixels) to first pixel + * @line: offset (in pixels) from end of line to start of next line + */ +struct camif_dma_offset { + int initial; + int line; +}; + +/** + * struct camif_frame - source/target frame properties + * @f_width: full pixel width + * @f_height: full pixel height + * @rect: crop/composition rectangle + * @dma_offset: DMA offset configuration + */ +struct camif_frame { + u16 f_width; + u16 f_height; + struct v4l2_rect rect; + struct camif_dma_offset dma_offset; +}; + +/* CAMIF clocks enumeration */ +enum { + CLK_GATE, + CLK_CAM, + CLK_MAX_NUM, +}; + +struct vp_pix_limits { + u16 max_out_width; + u16 max_sc_out_width; + u16 out_width_align; + u16 max_height; + u8 min_out_width; + u16 out_hor_offset_align; +}; + +struct camif_pix_limits { + u16 win_hor_offset_align; +}; + +/** + * struct s3c_camif_variant - CAMIF variant structure + * @vp_pix_limits: pixel limits for the codec and preview paths + * @camif_pix_limits: pixel limits for the camera input interface + * @ip_revision: the CAMIF IP revision: 0x20 for s3c244x, 0x32 for s3c6410 + */ +struct s3c_camif_variant { + struct vp_pix_limits vp_pix_limits[2]; + struct camif_pix_limits pix_limits; + u8 ip_revision; + u8 has_img_effect; + unsigned int vp_offset; +}; + +struct s3c_camif_drvdata { + const struct s3c_camif_variant *variant; + unsigned long bus_clk_freq; +}; + +struct camif_scaler { + u8 scaleup_h; + u8 scaleup_v; + u8 copy; + u8 enable; + u32 h_shift; + u32 v_shift; + u32 pre_h_ratio; + u32 pre_v_ratio; + u32 pre_dst_width; + u32 pre_dst_height; + u32 main_h_ratio; + u32 main_v_ratio; +}; + +struct camif_dev; + +/** + * struct camif_vp - CAMIF data processing path structure (codec/preview) + * @irq_queue: interrupt handling waitqueue + * @irq: interrupt number for this data path + * @camif: pointer to the camif structure + * @pad: media pad for the video node + * @vdev video device + * @ctrl_handler: video node controls handler + * @owner: file handle that own the streaming + * @pending_buf_q: pending (empty) buffers queue head + * @active_buf_q: active (being written) buffers queue head + * @active_buffers: counter of buffer set up at the DMA engine + * @buf_index: identifier of a last empty buffer set up in H/W + * @frame_sequence: image frame sequence counter + * @reqbufs_count: the number of buffers requested + * @scaler: the scaler structure + * @out_fmt: pixel format at this video path output + * @payload: the output data frame payload size + * @out_frame: the output pixel resolution + * @state: the video path's state + * @fmt_flags: flags determining supported pixel formats + * @id: CAMIF id, 0 - codec, 1 - preview + * @rotation: current image rotation value + * @hflip: apply horizontal flip if set + * @vflip: apply vertical flip if set + */ +struct camif_vp { + wait_queue_head_t irq_queue; + int irq; + struct camif_dev *camif; + struct media_pad pad; + struct video_device vdev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_fh *owner; + struct vb2_queue vb_queue; + struct list_head pending_buf_q; + struct list_head active_buf_q; + unsigned int active_buffers; + unsigned int buf_index; + unsigned int frame_sequence; + unsigned int reqbufs_count; + struct camif_scaler scaler; + const struct camif_fmt *out_fmt; + unsigned int payload; + struct camif_frame out_frame; + unsigned int state; + u16 fmt_flags; + u8 id; + u8 rotation; + u8 hflip; + u8 vflip; + unsigned int offset; +}; + +/* Video processing path enumeration */ +#define VP_CODEC 0 +#define VP_PREVIEW 1 +#define CAMIF_VP_NUM 2 + +/** + * struct camif_dev - the CAMIF driver private data structure + * @media_dev: top-level media device structure + * @v4l2_dev: root v4l2_device + * @subdev: camera interface ("catchcam") subdev + * @mbus_fmt: camera input media bus format + * @camif_crop: camera input interface crop rectangle + * @pads: the camif subdev's media pads + * @stream_count: the camera interface streaming reference counter + * @sensor: image sensor data structure + * @m_pipeline: video entity pipeline description + * @ctrl_handler: v4l2 control handler (owned by @subdev) + * @test_pattern: test pattern controls + * @vp: video path (DMA) description (codec/preview) + * @alloc_ctx: memory buffer allocator context + * @variant: variant information for this device + * @dev: pointer to the CAMIF device struct + * @pdata: a copy of the driver's platform data + * @clock: clocks required for the CAMIF operation + * @lock: mutex protecting this data structure + * @slock: spinlock protecting CAMIF registers + * @io_base: start address of the mmaped CAMIF registers + */ +struct camif_dev { + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_rect camif_crop; + struct media_pad pads[CAMIF_SD_PADS_NUM]; + int stream_count; + + struct cam_sensor { + struct v4l2_subdev *sd; + short power_count; + short stream_count; + } sensor; + struct media_pipeline *m_pipeline; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrl_test_pattern; + struct { + struct v4l2_ctrl *ctrl_colorfx; + struct v4l2_ctrl *ctrl_colorfx_cbcr; + }; + u8 test_pattern; + u8 colorfx; + u8 colorfx_cb; + u8 colorfx_cr; + + struct camif_vp vp[CAMIF_VP_NUM]; + struct vb2_alloc_ctx *alloc_ctx; + + const struct s3c_camif_variant *variant; + struct device *dev; + struct s3c_camif_plat_data pdata; + struct clk *clock[CLK_MAX_NUM]; + struct mutex lock; + spinlock_t slock; + void __iomem *io_base; +}; + +/** + * struct camif_addr - Y/Cb/Cr DMA start address structure + * @y: luminance plane dma address + * @cb: Cb plane dma address + * @cr: Cr plane dma address + */ +struct camif_addr { + dma_addr_t y; + dma_addr_t cb; + dma_addr_t cr; +}; + +/** + * struct camif_buffer - the camif video buffer structure + * @vb: vb2 buffer + * @list: list head for the buffers queue + * @paddr: DMA start addresses + * @index: an identifier of this buffer at the DMA engine + */ +struct camif_buffer { + struct vb2_buffer vb; + struct list_head list; + struct camif_addr paddr; + unsigned int index; +}; + +const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp, + const u32 *pixelformat, int index); +int s3c_camif_register_video_node(struct camif_dev *camif, int idx); +void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx); +irqreturn_t s3c_camif_irq_handler(int irq, void *priv); +int s3c_camif_create_subdev(struct camif_dev *camif); +void s3c_camif_unregister_subdev(struct camif_dev *camif); +int s3c_camif_set_defaults(struct camif_dev *camif); +int s3c_camif_get_scaler_config(struct camif_vp *vp, + struct camif_scaler *scaler); + +static inline void camif_active_queue_add(struct camif_vp *vp, + struct camif_buffer *buf) +{ + list_add_tail(&buf->list, &vp->active_buf_q); + vp->active_buffers++; +} + +static inline struct camif_buffer *camif_active_queue_pop( + struct camif_vp *vp) +{ + struct camif_buffer *buf = list_first_entry(&vp->active_buf_q, + struct camif_buffer, list); + list_del(&buf->list); + vp->active_buffers--; + return buf; +} + +static inline struct camif_buffer *camif_active_queue_peek( + struct camif_vp *vp, int index) +{ + struct camif_buffer *tmp, *buf; + + if (WARN_ON(list_empty(&vp->active_buf_q))) + return NULL; + + list_for_each_entry_safe(buf, tmp, &vp->active_buf_q, list) { + if (buf->index == index) { + list_del(&buf->list); + vp->active_buffers--; + return buf; + } + } + + return NULL; +} + +static inline void camif_pending_queue_add(struct camif_vp *vp, + struct camif_buffer *buf) +{ + list_add_tail(&buf->list, &vp->pending_buf_q); +} + +static inline struct camif_buffer *camif_pending_queue_pop( + struct camif_vp *vp) +{ + struct camif_buffer *buf = list_first_entry(&vp->pending_buf_q, + struct camif_buffer, list); + list_del(&buf->list); + return buf; +} + +#endif /* CAMIF_CORE_H_ */ diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c new file mode 100644 index 0000000..1a3b4fc --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-regs.c @@ -0,0 +1,606 @@ +/* + * Samsung s3c24xx/s3c64xx SoC CAMIF driver + * + * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> + * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/delay.h> +#include "camif-regs.h" + +#define camif_write(_camif, _off, _val) writel(_val, (_camif)->io_base + (_off)) +#define camif_read(_camif, _off) readl((_camif)->io_base + (_off)) + +void camif_hw_reset(struct camif_dev *camif) +{ + u32 cfg; + + cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); + cfg |= CISRCFMT_ITU601_8BIT; + camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); + + /* S/W reset */ + cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); + cfg |= CIGCTRL_SWRST; + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) + cfg |= CIGCTRL_IRQ_LEVEL; + camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); + udelay(10); + + cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); + cfg &= ~CIGCTRL_SWRST; + camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); + udelay(10); +} + +void camif_hw_clear_pending_irq(struct camif_vp *vp) +{ + u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_CIGCTRL); + cfg |= CIGCTRL_IRQ_CLR(vp->id); + camif_write(vp->camif, S3C_CAMIF_REG_CIGCTRL, cfg); +} + +/* + * Sets video test pattern (off, color bar, horizontal or vertical gradient). + * External sensor pixel clock must be active for the test pattern to work. + */ +void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern) +{ + u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); + cfg &= ~CIGCTRL_TESTPATTERN_MASK; + cfg |= (pattern << 27); + camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); +} + +void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect, + unsigned int cr, unsigned int cb) +{ + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, CIIMGEFF_FIN_BYPASS }, + { V4L2_COLORFX_BW, CIIMGEFF_FIN_ARBITRARY }, + { V4L2_COLORFX_SEPIA, CIIMGEFF_FIN_ARBITRARY }, + { V4L2_COLORFX_NEGATIVE, CIIMGEFF_FIN_NEGATIVE }, + { V4L2_COLORFX_ART_FREEZE, CIIMGEFF_FIN_ARTFREEZE }, + { V4L2_COLORFX_EMBOSS, CIIMGEFF_FIN_EMBOSSING }, + { V4L2_COLORFX_SILHOUETTE, CIIMGEFF_FIN_SILHOUETTE }, + { V4L2_COLORFX_SET_CBCR, CIIMGEFF_FIN_ARBITRARY }, + }; + unsigned int i, cfg; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) + if (colorfx[i].id == effect) + break; + + if (i == ARRAY_SIZE(colorfx)) + return; + + cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset)); + /* Set effect */ + cfg &= ~CIIMGEFF_FIN_MASK; + cfg |= colorfx[i].value; + /* Set both paths */ + if (camif->variant->ip_revision >= S3C6400_CAMIF_IP_REV) { + if (effect == V4L2_COLORFX_NONE) + cfg &= ~CIIMGEFF_IE_ENABLE_MASK; + else + cfg |= CIIMGEFF_IE_ENABLE_MASK; + } + cfg &= ~CIIMGEFF_PAT_CBCR_MASK; + cfg |= cr | (cb << 13); + camif_write(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset), cfg); +} + +static const u32 src_pixfmt_map[8][2] = { + { V4L2_MBUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR }, + { V4L2_MBUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB }, + { V4L2_MBUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY }, + { V4L2_MBUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY }, +}; + +/* Set camera input pixel format and resolution */ +void camif_hw_set_source_format(struct camif_dev *camif) +{ + struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; + unsigned int i = ARRAY_SIZE(src_pixfmt_map); + u32 cfg; + + while (i-- >= 0) { + if (src_pixfmt_map[i][0] == mf->code) + break; + } + + if (i == 0 && src_pixfmt_map[i][0] != mf->code) { + dev_err(camif->dev, + "Unsupported pixel code, falling back to %#08x\n", + src_pixfmt_map[i][0]); + } + + cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); + cfg &= ~(CISRCFMT_ORDER422_MASK | CISRCFMT_SIZE_CAM_MASK); + cfg |= (mf->width << 16) | mf->height; + cfg |= src_pixfmt_map[i][1]; + camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); +} + +/* Set the camera host input window offsets (cropping) */ +void camif_hw_set_camera_crop(struct camif_dev *camif) +{ + struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; + struct v4l2_rect *crop = &camif->camif_crop; + u32 hoff2, voff2; + u32 cfg; + + /* Note: s3c244x requirement: left = f_width - rect.width / 2 */ + cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); + cfg &= ~(CIWDOFST_OFST_MASK | CIWDOFST_WINOFSEN); + cfg |= (crop->left << 16) | crop->top; + if (crop->left != 0 || crop->top != 0) + cfg |= CIWDOFST_WINOFSEN; + camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); + + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { + hoff2 = mf->width - crop->width - crop->left; + voff2 = mf->height - crop->height - crop->top; + cfg = (hoff2 << 16) | voff2; + camif_write(camif, S3C_CAMIF_REG_CIWDOFST2, cfg); + } +} + +void camif_hw_clear_fifo_overflow(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + u32 cfg; + + cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); + if (vp->id == 0) + cfg |= (CIWDOFST_CLROVCOFIY | CIWDOFST_CLROVCOFICB | + CIWDOFST_CLROVCOFICR); + else + cfg |= (/* CIWDOFST_CLROVPRFIY | */ CIWDOFST_CLROVPRFICB | + CIWDOFST_CLROVPRFICR); + camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); +} + +/* Set video bus signals polarity */ +void camif_hw_set_camera_bus(struct camif_dev *camif) +{ + unsigned int flags = camif->pdata.sensor.flags; + + u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); + + cfg &= ~(CIGCTRL_INVPOLPCLK | CIGCTRL_INVPOLVSYNC | + CIGCTRL_INVPOLHREF | CIGCTRL_INVPOLFIELD); + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + cfg |= CIGCTRL_INVPOLPCLK; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + cfg |= CIGCTRL_INVPOLVSYNC; + /* + * HREF is normally high during frame active data + * transmission and low during horizontal synchronization + * period. Thus HREF active high means HSYNC active low. + */ + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + cfg |= CIGCTRL_INVPOLHREF; /* HREF active low */ + + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { + if (flags & V4L2_MBUS_FIELD_EVEN_LOW) + cfg |= CIGCTRL_INVPOLFIELD; + cfg |= CIGCTRL_FIELDMODE; + } + + pr_debug("Setting CIGCTRL to: %#x\n", cfg); + + camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); +} + +void camif_hw_set_output_addr(struct camif_vp *vp, + struct camif_addr *paddr, int i) +{ + struct camif_dev *camif = vp->camif; + + camif_write(camif, S3C_CAMIF_REG_CIYSA(vp->id, i), paddr->y); + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV + || vp->id == VP_CODEC) { + camif_write(camif, S3C_CAMIF_REG_CICBSA(vp->id, i), + paddr->cb); + camif_write(camif, S3C_CAMIF_REG_CICRSA(vp->id, i), + paddr->cr); + } + + pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n", + i, paddr->y, paddr->cb, paddr->cr); +} + +static void camif_hw_set_out_dma_size(struct camif_vp *vp) +{ + struct camif_frame *frame = &vp->out_frame; + u32 cfg; + + cfg = camif_read(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); + cfg &= ~CITRGFMT_TARGETSIZE_MASK; + cfg |= (frame->f_width << 16) | frame->f_height; + camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); +} + +static void camif_get_dma_burst(u32 width, u32 ybpp, u32 *mburst, u32 *rburst) +{ + unsigned int nwords = width * ybpp / 4; + unsigned int div, rem; + + if (WARN_ON(width < 8 || (width * ybpp) & 7)) + return; + + for (div = 16; div >= 2; div /= 2) { + if (nwords < div) + continue; + + rem = nwords & (div - 1); + if (rem == 0) { + *mburst = div; + *rburst = div; + break; + } + if (rem == div / 2 || rem == div / 4) { + *mburst = div; + *rburst = rem; + break; + } + } +} + +void camif_hw_set_output_dma(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_frame *frame = &vp->out_frame; + const struct camif_fmt *fmt = vp->out_fmt; + unsigned int ymburst = 0, yrburst = 0; + u32 cfg; + + camif_hw_set_out_dma_size(vp); + + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { + struct camif_dma_offset *offset = &frame->dma_offset; + /* Set the input dma offsets. */ + cfg = S3C_CISS_OFFS_INITIAL(offset->initial); + cfg |= S3C_CISS_OFFS_LINE(offset->line); + camif_write(camif, S3C_CAMIF_REG_CISSY(vp->id), cfg); + camif_write(camif, S3C_CAMIF_REG_CISSCB(vp->id), cfg); + camif_write(camif, S3C_CAMIF_REG_CISSCR(vp->id), cfg); + } + + /* Configure DMA burst values */ + camif_get_dma_burst(frame->rect.width, fmt->ybpp, &ymburst, &yrburst); + + cfg = camif_read(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset)); + cfg &= ~CICTRL_BURST_MASK; + + cfg |= CICTRL_YBURST1(ymburst) | CICTRL_YBURST2(yrburst); + cfg |= CICTRL_CBURST1(ymburst / 2) | CICTRL_CBURST2(yrburst / 2); + + camif_write(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset), cfg); + + pr_debug("ymburst: %u, yrburst: %u\n", ymburst, yrburst); +} + +void camif_hw_set_input_path(struct camif_vp *vp) +{ + u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id)); + cfg &= ~MSCTRL_SEL_DMA_CAM; + camif_write(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id), cfg); +} + +void camif_hw_set_target_format(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_frame *frame = &vp->out_frame; + u32 cfg; + + pr_debug("fw: %d, fh: %d color: %d\n", frame->f_width, + frame->f_height, vp->out_fmt->color); + + cfg = camif_read(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); + cfg &= ~CITRGFMT_TARGETSIZE_MASK; + + if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { + /* We currently support only YCbCr 4:2:2 at the camera input */ + cfg |= CITRGFMT_IN422; + cfg &= ~CITRGFMT_OUT422; + if (vp->out_fmt->color == IMG_FMT_YCBCR422P) + cfg |= CITRGFMT_OUT422; + } else { + cfg &= ~CITRGFMT_OUTFORMAT_MASK; + switch (vp->out_fmt->color) { + case IMG_FMT_RGB565...IMG_FMT_XRGB8888: + cfg |= CITRGFMT_OUTFORMAT_RGB; + break; + case IMG_FMT_YCBCR420...IMG_FMT_YCRCB420: + cfg |= CITRGFMT_OUTFORMAT_YCBCR420; + break; + case IMG_FMT_YCBCR422P: + cfg |= CITRGFMT_OUTFORMAT_YCBCR422; + break; + case IMG_FMT_YCBYCR422...IMG_FMT_CRYCBY422: + cfg |= CITRGFMT_OUTFORMAT_YCBCR422I; + break; + } + } + + /* Rotation is only supported by s3c64xx */ + if (vp->rotation == 90 || vp->rotation == 270) + cfg |= (frame->f_height << 16) | frame->f_width; + else + cfg |= (frame->f_width << 16) | frame->f_height; + camif_write(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); + + /* Target area, output pixel width * height */ + cfg = camif_read(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset)); + cfg &= ~CITAREA_MASK; + cfg |= (frame->f_width * frame->f_height); + camif_write(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset), cfg); +} + +void camif_hw_set_flip(struct camif_vp *vp) +{ + u32 cfg = camif_read(vp->camif, + S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); + + cfg &= ~CITRGFMT_FLIP_MASK; + + if (vp->hflip) + cfg |= CITRGFMT_FLIP_Y_MIRROR; + if (vp->vflip) + cfg |= CITRGFMT_FLIP_X_MIRROR; + + camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); +} + +static void camif_hw_set_prescaler(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_scaler *sc = &vp->scaler; + u32 cfg, shfactor, addr; + + addr = S3C_CAMIF_REG_CISCPRERATIO(vp->id, vp->offset); + + shfactor = 10 - (sc->h_shift + sc->v_shift); + cfg = shfactor << 28; + + cfg |= (sc->pre_h_ratio << 16) | sc->pre_v_ratio; + camif_write(camif, addr, cfg); + + cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; + camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg); +} + +void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_scaler *scaler = &vp->scaler; + unsigned int color = vp->out_fmt->color; + u32 cfg; + + camif_hw_set_prescaler(vp); + + cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); + + cfg &= ~(CISCCTRL_SCALEUP_MASK | CISCCTRL_SCALERBYPASS | + CISCCTRL_MAIN_RATIO_MASK | CIPRSCCTRL_RGB_FORMAT_24BIT); + + if (scaler->enable) { + if (scaler->scaleup_h) { + if (vp->id == VP_CODEC) + cfg |= CISCCTRL_SCALEUP_H; + else + cfg |= CIPRSCCTRL_SCALEUP_H; + } + if (scaler->scaleup_v) { + if (vp->id == VP_CODEC) + cfg |= CISCCTRL_SCALEUP_V; + else + cfg |= CIPRSCCTRL_SCALEUP_V; + } + } else { + if (vp->id == VP_CODEC) + cfg |= CISCCTRL_SCALERBYPASS; + } + + cfg |= ((scaler->main_h_ratio & 0x1ff) << 16); + cfg |= scaler->main_v_ratio & 0x1ff; + + if (vp->id == VP_PREVIEW) { + if (color == IMG_FMT_XRGB8888) + cfg |= CIPRSCCTRL_RGB_FORMAT_24BIT; + cfg |= CIPRSCCTRL_SAMPLE; + } + + camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); + + pr_debug("main: h_ratio: %#x, v_ratio: %#x", + scaler->main_h_ratio, scaler->main_v_ratio); +} + +void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + struct camif_scaler *scaler = &vp->scaler; + unsigned int color = vp->out_fmt->color; + u32 cfg; + + camif_hw_set_prescaler(vp); + + cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); + + cfg &= ~(CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE + | CISCCTRL_SCALEUP_H | CISCCTRL_SCALEUP_V + | CISCCTRL_SCALERBYPASS | CISCCTRL_ONE2ONE + | CISCCTRL_INRGB_FMT_MASK | CISCCTRL_OUTRGB_FMT_MASK + | CISCCTRL_INTERLACE | CISCCTRL_EXTRGB_EXTENSION + | CISCCTRL_MAIN_RATIO_MASK); + + cfg |= (CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE); + + if (!scaler->enable) { + cfg |= CISCCTRL_SCALERBYPASS; + } else { + if (scaler->scaleup_h) + cfg |= CISCCTRL_SCALEUP_H; + if (scaler->scaleup_v) + cfg |= CISCCTRL_SCALEUP_V; + if (scaler->copy) + cfg |= CISCCTRL_ONE2ONE; + } + + switch (color) { + case IMG_FMT_RGB666: + cfg |= CISCCTRL_OUTRGB_FMT_RGB666; + break; + case IMG_FMT_XRGB8888: + cfg |= CISCCTRL_OUTRGB_FMT_RGB888; + break; + } + + cfg |= (scaler->main_h_ratio & 0x1ff) << 16; + cfg |= scaler->main_v_ratio & 0x1ff; + + camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); + + pr_debug("main: h_ratio: %#x, v_ratio: %#x", + scaler->main_h_ratio, scaler->main_v_ratio); +} + +void camif_hw_set_scaler(struct camif_vp *vp) +{ + unsigned int ip_rev = vp->camif->variant->ip_revision; + + if (ip_rev == S3C244X_CAMIF_IP_REV) + camif_s3c244x_hw_set_scaler(vp); + else + camif_s3c64xx_hw_set_scaler(vp); +} + +void camif_hw_enable_scaler(struct camif_vp *vp, bool on) +{ + u32 addr = S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset); + u32 cfg; + + cfg = camif_read(vp->camif, addr); + if (on) + cfg |= CISCCTRL_SCALERSTART; + else + cfg &= ~CISCCTRL_SCALERSTART; + camif_write(vp->camif, addr, cfg); +} + +void camif_hw_set_lastirq(struct camif_vp *vp, int enable) +{ + u32 addr = S3C_CAMIF_REG_CICTRL(vp->id, vp->offset); + u32 cfg; + + cfg = camif_read(vp->camif, addr); + if (enable) + cfg |= CICTRL_LASTIRQ_ENABLE; + else + cfg &= ~CICTRL_LASTIRQ_ENABLE; + camif_write(vp->camif, addr, cfg); +} + +void camif_hw_enable_capture(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + u32 cfg; + + cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); + camif->stream_count++; + + if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) + cfg |= CIIMGCPT_CPT_FREN_ENABLE(vp->id); + + if (vp->scaler.enable) + cfg |= CIIMGCPT_IMGCPTEN_SC(vp->id); + + if (camif->stream_count == 1) + cfg |= CIIMGCPT_IMGCPTEN; + + camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); + + pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", + cfg, camif->stream_count); +} + +void camif_hw_disable_capture(struct camif_vp *vp) +{ + struct camif_dev *camif = vp->camif; + u32 cfg; + + cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); + cfg &= ~CIIMGCPT_IMGCPTEN_SC(vp->id); + + if (WARN_ON(--(camif->stream_count) < 0)) + camif->stream_count = 0; + + if (camif->stream_count == 0) + cfg &= ~CIIMGCPT_IMGCPTEN; + + pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", + cfg, camif->stream_count); + + camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); +} + +void camif_hw_dump_regs(struct camif_dev *camif, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { S3C_CAMIF_REG_CISRCFMT, "CISRCFMT" }, + { S3C_CAMIF_REG_CIWDOFST, "CIWDOFST" }, + { S3C_CAMIF_REG_CIGCTRL, "CIGCTRL" }, + { S3C_CAMIF_REG_CIWDOFST2, "CIWDOFST2" }, + { S3C_CAMIF_REG_CIYSA(0, 0), "CICOYSA0" }, + { S3C_CAMIF_REG_CICBSA(0, 0), "CICOCBSA0" }, + { S3C_CAMIF_REG_CICRSA(0, 0), "CICOCRSA0" }, + { S3C_CAMIF_REG_CIYSA(0, 1), "CICOYSA1" }, + { S3C_CAMIF_REG_CICBSA(0, 1), "CICOCBSA1" }, + { S3C_CAMIF_REG_CICRSA(0, 1), "CICOCRSA1" }, + { S3C_CAMIF_REG_CIYSA(0, 2), "CICOYSA2" }, + { S3C_CAMIF_REG_CICBSA(0, 2), "CICOCBSA2" }, + { S3C_CAMIF_REG_CICRSA(0, 2), "CICOCRSA2" }, + { S3C_CAMIF_REG_CIYSA(0, 3), "CICOYSA3" }, + { S3C_CAMIF_REG_CICBSA(0, 3), "CICOCBSA3" }, + { S3C_CAMIF_REG_CICRSA(0, 3), "CICOCRSA3" }, + { S3C_CAMIF_REG_CIYSA(1, 0), "CIPRYSA0" }, + { S3C_CAMIF_REG_CIYSA(1, 1), "CIPRYSA1" }, + { S3C_CAMIF_REG_CIYSA(1, 2), "CIPRYSA2" }, + { S3C_CAMIF_REG_CIYSA(1, 3), "CIPRYSA3" }, + { S3C_CAMIF_REG_CITRGFMT(0, 0), "CICOTRGFMT" }, + { S3C_CAMIF_REG_CITRGFMT(1, 0), "CIPRTRGFMT" }, + { S3C_CAMIF_REG_CICTRL(0, 0), "CICOCTRL" }, + { S3C_CAMIF_REG_CICTRL(1, 0), "CIPRCTRL" }, + { S3C_CAMIF_REG_CISCPREDST(0, 0), "CICOSCPREDST" }, + { S3C_CAMIF_REG_CISCPREDST(1, 0), "CIPRSCPREDST" }, + { S3C_CAMIF_REG_CISCPRERATIO(0, 0), "CICOSCPRERATIO" }, + { S3C_CAMIF_REG_CISCPRERATIO(1, 0), "CIPRSCPRERATIO" }, + { S3C_CAMIF_REG_CISCCTRL(0, 0), "CICOSCCTRL" }, + { S3C_CAMIF_REG_CISCCTRL(1, 0), "CIPRSCCTRL" }, + { S3C_CAMIF_REG_CITAREA(0, 0), "CICOTAREA" }, + { S3C_CAMIF_REG_CITAREA(1, 0), "CIPRTAREA" }, + { S3C_CAMIF_REG_CISTATUS(0, 0), "CICOSTATUS" }, + { S3C_CAMIF_REG_CISTATUS(1, 0), "CIPRSTATUS" }, + { S3C_CAMIF_REG_CIIMGCPT(0), "CIIMGCPT" }, + }; + u32 i; + + pr_info("--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = readl(camif->io_base + registers[i].offset); + printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg); + } +} diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/s3c-camif/camif-regs.h new file mode 100644 index 0000000..af2d472 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-regs.h @@ -0,0 +1,269 @@ +/* + * Register definition file for s3c24xx/s3c64xx SoC CAMIF driver + * + * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> + * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef CAMIF_REGS_H_ +#define CAMIF_REGS_H_ + +#include "camif-core.h" +#include <media/s3c_camif.h> + +/* + * The id argument indicates the processing path: + * id = 0 - codec (FIMC C), 1 - preview (FIMC P). + */ + +/* Camera input format */ +#define S3C_CAMIF_REG_CISRCFMT 0x00 +#define CISRCFMT_ITU601_8BIT (1 << 31) +#define CISRCFMT_ITU656_8BIT (0 << 31) +#define CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define CISRCFMT_ORDER422_CRYCBY (3 << 14) +#define CISRCFMT_ORDER422_MASK (3 << 14) +#define CISRCFMT_SIZE_CAM_MASK (0x1fff << 16 | 0x1fff) + +/* Window offset */ +#define S3C_CAMIF_REG_CIWDOFST 0x04 +#define CIWDOFST_WINOFSEN (1 << 31) +#define CIWDOFST_CLROVCOFIY (1 << 30) +#define CIWDOFST_CLROVRLB_PR (1 << 28) +/* #define CIWDOFST_CLROVPRFIY (1 << 27) */ +#define CIWDOFST_CLROVCOFICB (1 << 15) +#define CIWDOFST_CLROVCOFICR (1 << 14) +#define CIWDOFST_CLROVPRFICB (1 << 13) +#define CIWDOFST_CLROVPRFICR (1 << 12) +#define CIWDOFST_OFST_MASK (0x7ff << 16 | 0x7ff) + +/* Window offset 2 */ +#define S3C_CAMIF_REG_CIWDOFST2 0x14 +#define CIWDOFST2_OFST2_MASK (0xfff << 16 | 0xfff) + +/* Global control */ +#define S3C_CAMIF_REG_CIGCTRL 0x08 +#define CIGCTRL_SWRST (1 << 31) +#define CIGCTRL_CAMRST (1 << 30) +#define CIGCTRL_TESTPATTERN_NORMAL (0 << 27) +#define CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27) +#define CIGCTRL_TESTPATTERN_HOR_INC (2 << 27) +#define CIGCTRL_TESTPATTERN_VER_INC (3 << 27) +#define CIGCTRL_TESTPATTERN_MASK (3 << 27) +#define CIGCTRL_INVPOLPCLK (1 << 26) +#define CIGCTRL_INVPOLVSYNC (1 << 25) +#define CIGCTRL_INVPOLHREF (1 << 24) +#define CIGCTRL_IRQ_OVFEN (1 << 22) +#define CIGCTRL_HREF_MASK (1 << 21) +#define CIGCTRL_IRQ_LEVEL (1 << 20) +/* IRQ_CLR_C, IRQ_CLR_P */ +#define CIGCTRL_IRQ_CLR(id) (1 << (19 - (id))) +#define CIGCTRL_FIELDMODE (1 << 2) +#define CIGCTRL_INVPOLFIELD (1 << 1) +#define CIGCTRL_CAM_INTERLACE (1 << 0) + +/* Y DMA output frame start address. n = 0..3. */ +#define S3C_CAMIF_REG_CIYSA(id, n) (0x18 + (id) * 0x54 + (n) * 4) +/* Cb plane output DMA start address. n = 0..3. Only codec path. */ +#define S3C_CAMIF_REG_CICBSA(id, n) (0x28 + (id) * 0x54 + (n) * 4) +/* Cr plane output DMA start address. n = 0..3. Only codec path. */ +#define S3C_CAMIF_REG_CICRSA(id, n) (0x38 + (id) * 0x54 + (n) * 4) + +/* CICOTRGFMT, CIPRTRGFMT - Target format */ +#define S3C_CAMIF_REG_CITRGFMT(id, _offs) (0x48 + (id) * (0x34 + (_offs))) +#define CITRGFMT_IN422 (1 << 31) /* only for s3c24xx */ +#define CITRGFMT_OUT422 (1 << 30) /* only for s3c24xx */ +#define CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) /* only for s3c6410 */ +#define CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) /* only for s3c6410 */ +#define CITRGFMT_OUTFORMAT_YCBCR422I (2 << 29) /* only for s3c6410 */ +#define CITRGFMT_OUTFORMAT_RGB (3 << 29) /* only for s3c6410 */ +#define CITRGFMT_OUTFORMAT_MASK (3 << 29) /* only for s3c6410 */ +#define CITRGFMT_TARGETHSIZE(x) ((x) << 16) +#define CITRGFMT_FLIP_NORMAL (0 << 14) +#define CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define CITRGFMT_FLIP_180 (3 << 14) +#define CITRGFMT_FLIP_MASK (3 << 14) +/* Preview path only */ +#define CITRGFMT_ROT90_PR (1 << 13) +#define CITRGFMT_TARGETVSIZE(x) ((x) << 0) +#define CITRGFMT_TARGETSIZE_MASK ((0x1fff << 16) | 0x1fff) + +/* CICOCTRL, CIPRCTRL. Output DMA control. */ +#define S3C_CAMIF_REG_CICTRL(id, _offs) (0x4c + (id) * (0x34 + (_offs))) +#define CICTRL_BURST_MASK (0xfffff << 4) +/* xBURSTn - 5-bits width */ +#define CICTRL_YBURST1(x) ((x) << 19) +#define CICTRL_YBURST2(x) ((x) << 14) +#define CICTRL_RGBBURST1(x) ((x) << 19) +#define CICTRL_RGBBURST2(x) ((x) << 14) +#define CICTRL_CBURST1(x) ((x) << 9) +#define CICTRL_CBURST2(x) ((x) << 4) +#define CICTRL_LASTIRQ_ENABLE (1 << 2) +#define CICTRL_ORDER422_MASK (3 << 0) + +/* CICOSCPRERATIO, CIPRSCPRERATIO. Pre-scaler control 1. */ +#define S3C_CAMIF_REG_CISCPRERATIO(id, _offs) (0x50 + (id) * (0x34 + (_offs))) + +/* CICOSCPREDST, CIPRSCPREDST. Pre-scaler control 2. */ +#define S3C_CAMIF_REG_CISCPREDST(id, _offs) (0x54 + (id) * (0x34 + (_offs))) + +/* CICOSCCTRL, CIPRSCCTRL. Main scaler control. */ +#define S3C_CAMIF_REG_CISCCTRL(id, _offs) (0x58 + (id) * (0x34 + (_offs))) +#define CISCCTRL_SCALERBYPASS (1 << 31) +/* s3c244x preview path only, s3c64xx both */ +#define CIPRSCCTRL_SAMPLE (1 << 31) +/* 0 - 16-bit RGB, 1 - 24-bit RGB */ +#define CIPRSCCTRL_RGB_FORMAT_24BIT (1 << 30) /* only for s3c244x */ +#define CIPRSCCTRL_SCALEUP_H (1 << 29) /* only for s3c244x */ +#define CIPRSCCTRL_SCALEUP_V (1 << 28) /* only for s3c244x */ +/* s3c64xx */ +#define CISCCTRL_SCALEUP_H (1 << 30) +#define CISCCTRL_SCALEUP_V (1 << 29) +#define CISCCTRL_SCALEUP_MASK (0x3 << 29) +#define CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define CISCCTRL_CSCY2R_WIDE (1 << 27) +#define CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define CISCCTRL_INTERLACE (1 << 25) +#define CISCCTRL_SCALERSTART (1 << 15) +#define CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define CISCCTRL_INRGB_FMT_MASK (3 << 13) +#define CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define CISCCTRL_OUTRGB_FMT_MASK (3 << 11) +#define CISCCTRL_EXTRGB_EXTENSION (1 << 10) +#define CISCCTRL_ONE2ONE (1 << 9) +#define CISCCTRL_MAIN_RATIO_MASK (0x1ff << 16 | 0x1ff) + +/* CICOTAREA, CIPRTAREA. Target area for DMA (Hsize x Vsize). */ +#define S3C_CAMIF_REG_CITAREA(id, _offs) (0x5c + (id) * (0x34 + (_offs))) +#define CITAREA_MASK 0xfffffff + +/* Codec (id = 0) or preview (id = 1) path status. */ +#define S3C_CAMIF_REG_CISTATUS(id, _offs) (0x64 + (id) * (0x34 + (_offs))) +#define CISTATUS_OVFIY_STATUS (1 << 31) +#define CISTATUS_OVFICB_STATUS (1 << 30) +#define CISTATUS_OVFICR_STATUS (1 << 29) +#define CISTATUS_OVF_MASK (0x7 << 29) +#define CIPRSTATUS_OVF_MASK (0x3 << 30) +#define CISTATUS_VSYNC_STATUS (1 << 28) +#define CISTATUS_FRAMECNT_MASK (3 << 26) +#define CISTATUS_FRAMECNT(__reg) (((__reg) >> 26) & 0x3) +#define CISTATUS_WINOFSTEN_STATUS (1 << 25) +#define CISTATUS_IMGCPTEN_STATUS (1 << 22) +#define CISTATUS_IMGCPTENSC_STATUS (1 << 21) +#define CISTATUS_VSYNC_A_STATUS (1 << 20) +#define CISTATUS_FRAMEEND_STATUS (1 << 19) /* 17 on s3c64xx */ + +/* Image capture enable */ +#define S3C_CAMIF_REG_CIIMGCPT(_offs) (0xa0 + (_offs)) +#define CIIMGCPT_IMGCPTEN (1 << 31) +#define CIIMGCPT_IMGCPTEN_SC(id) (1 << (30 - (id))) +/* Frame control: 1 - one-shot, 0 - free run */ +#define CIIMGCPT_CPT_FREN_ENABLE(id) (1 << (25 - (id))) +#define CIIMGCPT_CPT_FRMOD_ENABLE (0 << 18) +#define CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Capture sequence */ +#define S3C_CAMIF_REG_CICPTSEQ 0xc4 + +/* Image effects */ +#define S3C_CAMIF_REG_CIIMGEFF(_offs) (0xb0 + (_offs)) +#define CIIMGEFF_IE_ENABLE(id) (1 << (30 + (id))) +#define CIIMGEFF_IE_ENABLE_MASK (3 << 30) +/* Image effect: 1 - after scaler, 0 - before scaler */ +#define CIIMGEFF_IE_AFTER_SC (1 << 29) +#define CIIMGEFF_FIN_MASK (7 << 26) +#define CIIMGEFF_FIN_BYPASS (0 << 26) +#define CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff) +#define CIIMGEFF_PAT_CB(x) ((x) << 13) +#define CIIMGEFF_PAT_CR(x) (x) + +/* MSCOY0SA, MSPRY0SA. Y/Cb/Cr frame start address for input DMA. */ +#define S3C_CAMIF_REG_MSY0SA(id) (0xd4 + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCB0SA(id) (0xd8 + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCR0SA(id) (0xdc + ((id) * 0x2c)) + +/* MSCOY0END, MSCOY0END. Y/Cb/Cr frame end address for input DMA. */ +#define S3C_CAMIF_REG_MSY0END(id) (0xe0 + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCB0END(id) (0xe4 + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCR0END(id) (0xe8 + ((id) * 0x2c)) + +/* MSPRYOFF, MSPRYOFF. Y/Cb/Cr offset. n: 0 - codec, 1 - preview. */ +#define S3C_CAMIF_REG_MSYOFF(id) (0x118 + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCBOFF(id) (0x11c + ((id) * 0x2c)) +#define S3C_CAMIF_REG_MSCROFF(id) (0x120 + ((id) * 0x2c)) + +/* Real input DMA data size. n = 0 - codec, 1 - preview. */ +#define S3C_CAMIF_REG_MSWIDTH(id) (0xf8 + (id) * 0x2c) +#define AUTOLOAD_ENABLE (1 << 31) +#define ADDR_CH_DIS (1 << 30) +#define MSHEIGHT(x) (((x) & 0x3ff) << 16) +#define MSWIDTH(x) ((x) & 0x3ff) + +/* Input DMA control. n = 0 - codec, 1 - preview */ +#define S3C_CAMIF_REG_MSCTRL(id) (0xfc + (id) * 0x2c) +#define MSCTRL_ORDER422_M_YCBYCR (0 << 4) +#define MSCTRL_ORDER422_M_YCRYCB (1 << 4) +#define MSCTRL_ORDER422_M_CBYCRY (2 << 4) +#define MSCTRL_ORDER422_M_CRYCBY (3 << 4) +/* 0 - camera, 1 - DMA */ +#define MSCTRL_SEL_DMA_CAM (1 << 3) +#define MSCTRL_INFORMAT_M_YCBCR420 (0 << 1) +#define MSCTRL_INFORMAT_M_YCBCR422 (1 << 1) +#define MSCTRL_INFORMAT_M_YCBCR422I (2 << 1) +#define MSCTRL_INFORMAT_M_RGB (3 << 1) +#define MSCTRL_ENVID_M (1 << 0) + +/* CICOSCOSY, CIPRSCOSY. Scan line Y/Cb/Cr offset. */ +#define S3C_CAMIF_REG_CISSY(id) (0x12c + (id) * 0x0c) +#define S3C_CAMIF_REG_CISSCB(id) (0x130 + (id) * 0x0c) +#define S3C_CAMIF_REG_CISSCR(id) (0x134 + (id) * 0x0c) +#define S3C_CISS_OFFS_INITIAL(x) ((x) << 16) +#define S3C_CISS_OFFS_LINE(x) ((x) << 0) + +/* ------------------------------------------------------------------ */ + +void camif_hw_reset(struct camif_dev *camif); +void camif_hw_clear_pending_irq(struct camif_vp *vp); +void camif_hw_clear_fifo_overflow(struct camif_vp *vp); +void camif_hw_set_lastirq(struct camif_vp *vp, int enable); +void camif_hw_set_input_path(struct camif_vp *vp); +void camif_hw_enable_scaler(struct camif_vp *vp, bool on); +void camif_hw_enable_capture(struct camif_vp *vp); +void camif_hw_disable_capture(struct camif_vp *vp); +void camif_hw_set_camera_bus(struct camif_dev *camif); +void camif_hw_set_source_format(struct camif_dev *camif); +void camif_hw_set_camera_crop(struct camif_dev *camif); +void camif_hw_set_scaler(struct camif_vp *vp); +void camif_hw_set_flip(struct camif_vp *vp); +void camif_hw_set_output_dma(struct camif_vp *vp); +void camif_hw_set_target_format(struct camif_vp *vp); +void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern); +void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect, + unsigned int cr, unsigned int cb); +void camif_hw_set_output_addr(struct camif_vp *vp, struct camif_addr *paddr, + int index); +void camif_hw_dump_regs(struct camif_dev *camif, const char *label); + +static inline u32 camif_hw_get_status(struct camif_vp *vp) +{ + return readl(vp->camif->io_base + S3C_CAMIF_REG_CISTATUS(vp->id, + vp->offset)); +} + +#endif /* CAMIF_REGS_H_ */ diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 891ee87..fdb6740 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1230,6 +1230,14 @@ static int fimc_cap_qbuf(struct file *file, void *priv, return vb2_qbuf(&fimc->vid_cap.vbq, buf); } +static int fimc_cap_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct fimc_dev *fimc = video_drvdata(file); + + return vb2_expbuf(&fimc->vid_cap.vbq, eb); +} + static int fimc_cap_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -1354,6 +1362,7 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_qbuf = fimc_cap_qbuf, .vidioc_dqbuf = fimc_cap_dqbuf, + .vidioc_expbuf = fimc_cap_expbuf, .vidioc_prepare_buf = fimc_cap_prepare_buf, .vidioc_create_bufs = fimc_cap_create_bufs, @@ -1729,7 +1738,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q = &fimc->vid_cap.vbq; memset(q, 0, sizeof(*q)); q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->drv_priv = fimc->vid_cap.ctx; q->ops = &fimc_capture_qops; q->mem_ops = &vb2_dma_contig_memops; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 62afed3..1d21da4 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -105,7 +105,7 @@ static void fimc_device_run(void *priv) struct fimc_frame *sf, *df; struct fimc_dev *fimc; unsigned long flags; - u32 ret; + int ret; if (WARN(!ctx, "Null context\n")) return; @@ -439,6 +439,15 @@ static int fimc_m2m_dqbuf(struct file *file, void *fh, return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } +static int fimc_m2m_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + + static int fimc_m2m_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { @@ -607,6 +616,7 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_querybuf = fimc_m2m_querybuf, .vidioc_qbuf = fimc_m2m_qbuf, .vidioc_dqbuf = fimc_m2m_dqbuf, + .vidioc_expbuf = fimc_m2m_expbuf, .vidioc_streamon = fimc_m2m_streamon, .vidioc_streamoff = fimc_m2m_streamoff, .vidioc_g_crop = fimc_m2m_g_crop, @@ -622,7 +632,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, int ret; src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &fimc_qops; src_vq->mem_ops = &vb2_dma_contig_memops; @@ -633,7 +643,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, return ret; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &fimc_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 0531ab7..1bd5678 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -213,7 +213,7 @@ static int fimc_pipeline_close(struct fimc_pipeline *p) * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) +static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { int i, ret; @@ -547,7 +547,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (ret) break; - v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", source->name, flags ? '=' : '-', sink->name); if (flags == 0 || sensor == NULL) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index eb6a70b..6dad9a7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -636,6 +636,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EINVAL; } +/* Export DMA buffer */ +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + + if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return vb2_expbuf(&ctx->vq_src, eb); + if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return vb2_expbuf(&ctx->vq_dst, eb); + return -EINVAL; +} + /* Stream on */ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) @@ -813,6 +826,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_crop = vidioc_g_crop, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2af6d52..f92f6dd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1165,6 +1165,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return ret; } +/* Export DMA buffer */ +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + + if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return vb2_expbuf(&ctx->vq_src, eb); + if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return vb2_expbuf(&ctx->vq_dst, eb); + return -EINVAL; +} + /* Stream on */ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) @@ -1542,7 +1555,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv, } static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) + const struct v4l2_event_subscription *sub) { switch (sub->type) { case V4L2_EVENT_EOS: @@ -1568,6 +1581,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_s_parm = vidioc_s_parm, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 367db75..2895333 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -28,7 +28,7 @@ static struct s5p_mfc_pm *pm; static struct s5p_mfc_dev *p_dev; #ifdef CLK_DEBUG -atomic_t clk_ref; +static atomic_t clk_ref; #endif int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 0c1cd89..7379e77 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -19,7 +19,6 @@ #include <linux/videodev2.h> #include <linux/mm.h> #include <linux/module.h> -#include <linux/version.h> #include <linux/timer.h> #include <media/videobuf2-dma-contig.h> @@ -698,6 +697,15 @@ static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); } +static int mxr_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct mxr_layer *layer = video_drvdata(file); + + mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); + return vb2_expbuf(&layer->vb_queue, eb); +} + static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct mxr_layer *layer = video_drvdata(file); @@ -725,6 +733,7 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = { .vidioc_querybuf = mxr_querybuf, .vidioc_qbuf = mxr_qbuf, .vidioc_dqbuf = mxr_dqbuf, + .vidioc_expbuf = mxr_expbuf, /* Streaming control */ .vidioc_streamon = mxr_streamon, .vidioc_streamoff = mxr_streamoff, @@ -1093,7 +1102,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, layer->vb_queue = (struct vb2_queue) { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - .io_modes = VB2_MMAP | VB2_USERPTR, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF, .drv_priv = layer, .buf_struct_size = sizeof(struct mxr_buffer), .ops = &mxr_video_qops, diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 9afe1e7..cb6791e 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -19,6 +19,7 @@ config MX1_VIDEO config VIDEO_MX1 tristate "i.MX1/i.MXL CMOS Sensor Interface driver" + depends on BROKEN depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA select FIQ select VIDEOBUF_DMA_CONTIG diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index d3f0b84..4e37356 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -645,11 +645,17 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct soc_camera_device *icd = file->private_data; - int err = -EINVAL; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + dev_dbg(icd->pdev, "read called, buf %p\n", buf); + + if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) + return vb2_read(&icd->vb2_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); dev_err(icd->pdev, "camera device read not implemented\n"); - return err; + return -EINVAL; } static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) @@ -1048,10 +1054,8 @@ static void scan_add_host(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { - int ret; - icd->parent = ici->v4l2_dev.dev; - ret = soc_camera_probe(icd); + soc_camera_probe(icd); } } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index b366b05..0d59b9d 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -39,7 +39,6 @@ /* Wake up at about 30 fps */ #define WAKE_NUMERATOR 30 #define WAKE_DENOMINATOR 1001 -#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ #define MAX_WIDTH 1920 #define MAX_HEIGHT 1200 @@ -352,11 +351,6 @@ static void precalculate_bars(struct vivi_dev *dev) } } -#define TSTAMP_MIN_Y 24 -#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15) -#define TSTAMP_INPUT_X 10 -#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) - /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) { @@ -1308,7 +1302,7 @@ static int __init vivi_create_instance(int inst) /* initialize queue */ q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops; |