From e1393b5992b2b5b20b086c5f587e6c269d68e157 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 13 Oct 2012 07:18:34 -0300 Subject: [media] s5p-mfc: Fix compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added missing const qualifier. Without this patch compiler gives the following warning: drivers/media/platform/s5p-mfc/s5p_mfc_enc.c:1576:2: warning: initialization from incompatible pointer type [enabled by default] drivers/media/platform/s5p-mfc/s5p_mfc_enc.c:1576:2: warning: (near initialization for ‘s5p_mfc_enc_ioctl_ops.vidioc_subscribe_event’) [enabled by default] Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2af6d522f4a..5c1d727268b 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1542,7 +1542,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: -- cgit v1.2.3 From 5631a7d74d0b7a113b64e2cac99bea1e7603442f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 17 Oct 2012 07:11:49 -0300 Subject: [media] exynos-gsc: Fix compilation warning Used type casting to avoid the following compilation warning: drivers/media/platform/exynos-gsc/gsc-core.c:983:37: warning: incorrect type in assignment (different modifiers) drivers/media/platform/exynos-gsc/gsc-core.c:983:37: expected struct gsc_driverdata *driver_data drivers/media/platform/exynos-gsc/gsc-core.c:983:37: got void const *data Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index bfec9e65aef..949c93ceaab 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -980,7 +980,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; -- cgit v1.2.3 From bbbfcb4e32532c9c5f0718a95b6c2a74b8ad50de Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 17 Oct 2012 07:11:50 -0300 Subject: [media] s5p-mfc: Make 'clk_ref' static in s5p_mfc_pm.c Fixes the following sparse warning: drivers/media/platform/s5p-mfc/s5p_mfc_pm.c:31:10: warning: symbol 'clk_ref' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 367db755228..2895333866f 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) -- cgit v1.2.3 From 57fcfb6f935889c2086681d1bbc4ba0d47aeb768 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 17 Oct 2012 07:11:51 -0300 Subject: [media] s5p-fimc: Make 'fimc_pipeline_s_stream' function static Fixes the following sparse warning: drivers/media/platform/s5p-fimc/fimc-mdevice.c:216:5: warning: symbol 'fimc_pipeline_s_stream' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 80ada5882f6..c6c2166b00d 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; -- cgit v1.2.3 From 840354381127fe1097a332bb3e56a1a479cb47f0 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 16 Oct 2012 10:43:44 -0300 Subject: [media] exynos-gsc: change driver compatible string As G-Scaler is going to stay unchanged across all exynos5 series SoCs, changing the driver compatible string name to "samsung,exynos5-gsc" from "samsung,exynos5250-gsc". This change is as per the discussion in the devicetree forum. http://www.mail-archive.com/devicetree-discuss@lists.ozlabs.org/msg16448.html Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 949c93ceaab..cc7b218d047 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -965,8 +965,10 @@ static struct platform_device_id gsc_driver_ids[] = { MODULE_DEVICE_TABLE(platform, gsc_driver_ids); static const struct of_device_id exynos_gsc_match[] = { - { .compatible = "samsung,exynos5250-gsc", - .data = &gsc_v_100_drvdata, }, + { + .compatible = "samsung,exynos5-gsc", + .data = &gsc_v_100_drvdata, + }, {}, }; MODULE_DEVICE_TABLE(of, exynos_gsc_match); -- cgit v1.2.3 From 4bd0e030aadaba4a0e8fd5c0233d7e61f2e6207d Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 16 Oct 2012 09:38:34 -0300 Subject: [media] exynos-gsc: fix variable type in gsc_m2m_device_run() In gsc_m2m_device_run(), variable "ret" is accepting signed integer values. But currently it is defined as u32. This patch will modify the type of "ret" variable to "int". Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 3c7f00577bd..047f0f0434e 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")) -- cgit v1.2.3 From a63bafd8e3a48263983e046ba34607d1ceb1743b Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 16 Oct 2012 09:39:08 -0300 Subject: [media] s5p-fimc: fix variable type in fimc_device_run() In fimc_device_run(), variable "ret" is accepting signed integer values. But currently it is defined as u32. This patch will modify the type of "ret" variable to "int". Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 4500e44f685..4c4e9010511 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; -- cgit v1.2.3 From efde3191a73f4dda5267cbc31735f3b34ea59d85 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 18 Oct 2012 15:28:10 -0300 Subject: [media] s5p-tv: don't include linux/version.h in mixer_video.c The header is not needed, so remove it. Signed-off-by: Jesper Juhl Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 0c1cd895ff6..9b52f3a48c2 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 542fb082b9c76af8e71e9ea286d2dafdfba27099 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 25 Oct 2012 05:48:19 -0300 Subject: [media] s5p-fimc: Add missing new line character Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index c6c2166b00d..715b258a0ca 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -548,7 +548,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) -- cgit v1.2.3 From 0666c6b8d60eea25eb2dfa6b4773262ea91cf4a3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 8 Oct 2012 08:34:09 -0300 Subject: [media] v4l2: use list_move_tail instead of list_del/list_add_tail Using list_move_tail() instead of list_del() + list_add_tail(). dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 31ac4dc6924..a8ddb0cacab 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); -- cgit v1.2.3 From 2249b45701f9817f91d9a334fa30147e133badc4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 9 Oct 2012 23:33:29 -0300 Subject: [media] coda: Do not use __cancel_delayed_work() commit 136b5721d (workqueue: deprecate __cancel_delayed_work()) made __cancel_delayed_work deprecated. Use cancel_delayed_work instead and get rid of the following warning: drivers/media/platform/coda.c:1543: warning: '__cancel_delayed_work' is deprecated (declared at include/linux/workqueue.h:437) Signed-off-by: Fabio Estevam Acked-by: Tejun Heo Acked-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index cd04ae252c3..764050556b6 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); -- cgit v1.2.3 From 4e44cd08d9581423f10df7887ffeff685571122a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 10 Oct 2012 00:07:38 -0300 Subject: [media] coda: Fix 'driver_data' for mx53 CODA_7541 is the coda_product type for mx53. The 'driver_data' for mx53 is CODA_IMX53 instead. Signed-off-by: Fabio Estevam Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 764050556b6..7b8b547f2d5 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -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); -- cgit v1.2.3 From 32d640dea87bc973b91975ea408a04c6dcca3f45 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Wed, 7 Mar 2012 05:01:16 -0300 Subject: [media] omap_vout: Set DSS overlay_info only if paddr is non zero The omap_vout driver tries to set the DSS overlay_info using set_overlay_info() when the physical address for the overlay is still not configured. This happens in omap_vout_probe() and vidioc_s_fmt_vid_out(). The calls to omapvid_init(which internally calls set_overlay_info()) are removed from these functions. They don't need to be called as the omap_vout_device struct anyway maintains the overlay related changes made. Also, remove the explicit call to set_overlay_info() in vidioc_streamon(), this was used to set the paddr, this isn't needed as omapvid_init() does the same thing later. These changes are required as the DSS2 driver since 3.3 kernel doesn't let you set the overlay info with paddr as 0. Signed-off-by: Archit Taneja Tested-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 36 +++++---------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index a3b1a34c896..837cb6db747 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; -- cgit v1.2.3 From 7f2b3a7d8c8dbf3c9f70561f8a6d3a85b36cd16b Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 10 Oct 2012 14:33:38 -0300 Subject: [media] media/bfin: use module_platform_driver macro This patch removes some code duplication by using module_platform_driver. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/bfin_capture.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index cb2eb26850b..ec476ef5b70 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 "); -- cgit v1.2.3 From 50509e5cfe6cb7dffde71a454c3266fbb55fbff1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 10 Oct 2012 14:33:46 -0300 Subject: [media] media/m2m: use module_platform_driver macro This patch removes some code duplication by using module_platform_driver. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 45164c4f845..fcdbb2744eb 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -1108,17 +1108,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); -- cgit v1.2.3 From 0e368f302e897b8a6a14191dfc66d92bc354014f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 10 Oct 2012 14:33:52 -0300 Subject: [media] [3.6.0-,3/5] media/mx2_emmaprp: use module_platform_driver macro This patch removes some code duplication by using module_platform_driver. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mx2_emmaprp.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 8f22ce543cf..c45a9f5e5f2 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); -- cgit v1.2.3 From 2869a318ce313f97f987935dd225d88a9b30b2fc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 27 Oct 2012 16:22:25 -0300 Subject: [media] m2m-deinterlace: remove unused vars drivers/media/platform/m2m-deinterlace.c:229:15: warning: variable 'd_size' set but not used [-Wunused-but-set-variable] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index fcdbb2744eb..05c560f2ef0 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) { -- cgit v1.2.3 From 84cc2377ee72cfa5745fcd26919d4b1aa3e0e9e5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 27 Oct 2012 16:42:05 -0300 Subject: [media] soc_camera: ret is never used. get rid of it drivers/media/platform/soc_camera/soc_camera.c:1051:8: warning: variable 'ret' set but not used [-Wunused-but-set-variable] Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index d3f0b84e2d7..6f76ada1b2d 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1048,10 +1048,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); } } -- cgit v1.2.3 From 2669f1073958584295f71010e2d8a141da14a392 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Mon, 22 Oct 2012 09:54:43 -0300 Subject: [media] vivi: Kill BUFFER_TIMEOUT macro Usage of BUFFER_TIMEOUT has gone in 2008 in 78718e5d (V4L/DVB (7492): vivi: Simplify the vivi driver and avoid deadlocks), but the macro remains. Say goodbye to it. Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index b366b050a3d..3e6902a1bd2 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 -- cgit v1.2.3 From 6ee28d94c9bfa0640a7c94c289ca0df239d90a0d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 13 Nov 2012 11:06:28 -0200 Subject: [media] siano: fix build with allmodconfig As reported by Stephen: After merging the v4l-dvb tree, today's linux-next build (x86_64 allmodconfig) failed like this: ERROR: "sms_ir_exit" [drivers/media/common/siano/smsmdtv.ko] undefined! ERROR: "sms_ir_event" [drivers/media/common/siano/smsmdtv.ko] undefined! ERROR: "sms_ir_init" [drivers/media/common/siano/smsmdtv.ko] undefined! The smsir file should be part of the smsmdtv core, if RC is defined. Fix it. Reported-by: Stephen Rothwell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isppreview.c | 37 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 1ae1c0909ed..479e5c862b7 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->bpp == 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: @@ -1389,6 +1396,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 +1410,19 @@ 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); + ALIGN(format->width, 0x20) * + DIV_ROUND_UP(info->bpp, 8)); 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, -- cgit v1.2.3 From 2948b0b3ebbae98601c9b38e9841dbc754ca488c Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Tue, 23 Oct 2012 13:07:55 -0300 Subject: [media] vivi: Kill TSTAMP_* macros Usage of TSTAMP_* macros has gone in 2010 in 730947bc (V4L/DVB: vivi: clean up and a major overhaul) but the macros remain. Say goodbye to them. Signed-off-by: Kirill Smelkov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 3e6902a1bd2..ef0fc276f7e 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -351,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) { -- cgit v1.2.3 From baa29837a76d5738837602ff249a0c371591d7c7 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Fri, 9 Nov 2012 05:59:32 -0300 Subject: [media] media: davinci: vpbe: fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warnings were generated because of the following commit changed data type for address pointer 195bbca ARM: 7500/1: io: avoid writeback addressing modes for __raw_ accessors add __iomem annotation to fix following warnings drivers/media/platform/davinci/vpbe_osd.c: In function ‘osd_read’: drivers/media/platform/davinci/vpbe_osd.c:49:2: warning: passing argument 1 of ‘__raw_readl’ makes pointer from integer without a cast [enabled by default] arch/arm/include/asm/io.h:104:19: note: expected ‘const volatile void *’ but argument is of type ‘long unsigned int’ This patch stores the ioremap_nocache() returned address in a void __iomem * instead of a unsigned long and passes the same to readl/writel functions which fixes the above warnings. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_osd.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index bba299dbf39..707f243f810 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; -- cgit v1.2.3 From 13fc23d3067e996c03d8b602266f47d27d205d36 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Mon, 22 Oct 2012 09:27:13 -0300 Subject: [media] media: davinci: vpbe: migrate driver to videobuf2 This patch migrates VPBE display driver to videobuf2 framework. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/Kconfig | 2 +- drivers/media/platform/davinci/vpbe_display.c | 294 +++++++++++++++----------- 2 files changed, 176 insertions(+), 120 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 78e26d24f63..3c56037c82f 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/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 161c77650e2..974957fd7f7 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, @@ -1161,7 +1243,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 +1281,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 +1316,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 +1346,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 +1355,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 +1364,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 +1374,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 +1389,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 +1418,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 +1437,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 +1454,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 +1544,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 */ -- cgit v1.2.3 From d046628d2c81f64015c68cd14d5e09cbe99f56d8 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Mon, 22 Oct 2012 09:27:14 -0300 Subject: [media] media: davinci: vpbe: set device capabilities set device_caps and also change the driver and bus_info to proper values as per standard. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 974957fd7f7..2bfde7958fe 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -702,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; -- cgit v1.2.3 From 1f5a5e62aed455a4004685b6ee65ea91cc5acb87 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 22 Oct 2012 11:41:36 -0300 Subject: [media] media:davinci: clk - {prepare/unprepare} for common clk As a first step towards migrating davinci platforms to use common clock framework, replace all instances of clk_enable() with clk_prepare_enable() and clk_disable() with clk_disable_unprepare(). Also fixes some issues related to clk clean up in the driver Signed-off-by: Murali Karicheri Acked-by: Lad, Prabhakar Tested-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/dm355_ccdc.c | 8 ++++++-- drivers/media/platform/davinci/dm644x_ccdc.c | 16 ++++++++++------ drivers/media/platform/davinci/isif.c | 5 ++++- drivers/media/platform/davinci/vpbe.c | 10 +++++++--- drivers/media/platform/davinci/vpif.c | 8 ++++---- 5 files changed, 31 insertions(+), 16 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index ce0e4131c06..030950dcfb1 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 ee7942b1996..0215ab6ebc9 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 b99d5423e3a..2c26c3e1837 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 69d7a58c92c..7f5cf9b347b 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/vpif.c b/drivers/media/platform/davinci/vpif.c index cff3c0ab501..0d6cc8e4deb 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; } -- cgit v1.2.3 From 0dc2d8496191510ab237bff969480d8318705625 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 20 Sep 2012 23:05:10 -0300 Subject: [media] media: mx1_camera: mark the driver BROKEN The mx1_camera driver has been broken for a few release cycles since commit 6bd0812 (dmaengine: imx-dma: merge old dma-v1.c with imx-dma.c). It seems there is no one even compile tested it since then, as doing so will end up with the following error. CC drivers/media/platform/soc_camera/mx1_camera.o In file included from drivers/media/platform/soc_camera/mx1_camera.c:44:0: arch/arm/mach-imx/include/mach/dma-mx1-mx2.h:8:25: fatal error: mach/dma-v1.h: No such file or directory It seems there is no one being interested in bringing it back to work [1] so far. Let's mark it BROKEN. [1] https://lkml.org/lkml/2012/2/9/171 Signed-off-by: Shawn Guo Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 9afe1e7bde7..cb6791e62bd 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 -- cgit v1.2.3 From 1438be56ade16653584f70a2e2a1c11197291271 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 24 Aug 2012 11:36:11 -0300 Subject: [media] V4L: soc_camera: allow reading from video device if supported Try reading on video device. If the camera bus driver supports reading we can try it and return the result. Also add a debug line. Signed-off-by: Anatolij Gustschin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 6f76ada1b2d..4e3735679f1 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) -- cgit v1.2.3 From e070f1b47ee9f3591f93a205a3d8b0b06bb0afb5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 30 Oct 2012 09:45:00 -0300 Subject: [media] davinci: vpif_capture: fix return type check for v4l2_subdev_call() The v4l2_subdev_call() call returns -ENODEV when subdev is null and -ENOIOCTLCMD wnen no icotl is present. This patch fixes the return type check for v4l2_subdev_call(). The pattern E == C1 && E == C2 is always false. This patch fix this according to the assumption that && should be ||. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun [prabhakar.lad@ti.com: reword commit message] Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index fcabc023885..2d28a96e777 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1715,7 +1715,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 +1735,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; } -- cgit v1.2.3 From 63af4af59238fc17e70d0b0046362c804d4fc567 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 30 Oct 2012 09:49:38 -0300 Subject: [media] davinci: vpif_display: fix return type check for v4l2_subdev_call() The v4l2_subdev_call() call returns -ENODEV when subdev is null and -ENOIOCTLCMD wnen no icotl is present. This patch fixes the return type check for v4l2_subdev_call(). The pattern E == C1 && E == C2 is always false. This patch fix this according to the assumption that && should be ||. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun [prabhakar.lad@ti.com: reword commit messaage] Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index b716fbd4241..977ee436c33 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1380,7 +1380,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; } -- cgit v1.2.3 From 94b76a88f16c869109c41380c77f4a2b847650e3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 15 Nov 2012 09:18:17 -0300 Subject: [media] davinci: vpif: fix return value check for vb2_dma_contig_init_ctx() In case of error, the function vb2_dma_contig_init_ctx() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Acked-by: Lad, Prabhakar [prabhakar.lad@ti.com: reword commit header] Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 4 ++-- drivers/media/platform/davinci/vpif_display.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 2d28a96e777..41890a93883 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1004,9 +1004,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; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 977ee436c33..e5109ae0593 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -972,9 +972,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; -- cgit v1.2.3 From aec968326df3c60d140c4bcb00f2ee982acd01cb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 16 Nov 2012 12:03:06 -0300 Subject: [media] vpif_capture: protect dma_queue by a spin_lock The dma_queue list is accessed by both the interrupt handler and by normal code. It needs to be protected by a lock to prevent possible list corruption. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 41890a93883..a409ccefb38 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); } -- cgit v1.2.3 From c4697d7f96f40cdc8f88bcdf4bf13d74ea11c4cc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 16 Nov 2012 12:03:07 -0300 Subject: [media] vpif_display: protect dma_queue by a spin_lock The dma_queue list is accessed by both the interrupt handler and by normal code. It needs to be protected by a lock to prevent possible list corruption. Corruption has been observed in 'real-life' conditions. Adding this lock made it go away. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_display.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index e5109ae0593..9f2b603be9c 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]) { -- cgit v1.2.3 From 6d9a06f27a640967fc95a36ccb2f2d69e2e71f8a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Nov 2012 13:46:50 -0200 Subject: Revert "[media] siano: fix build with allmodconfig" This reverts commit 6ee28d94c9bfa0640a7c94c289ca0df239d90a0d. The patch got some alien code there, not sure why. Revert it to apply it properly. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isppreview.c | 37 +++++++++------------------- 1 file changed, 11 insertions(+), 26 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 479e5c862b7..1ae1c0909ed 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_INVALAW); + ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); else isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_INVALAW); + ISPPRV_PCR_WIDTH | 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 - * @info: Sink pad format information + * @format: Format on the preview engine sink pad * * Enable and configure CFA interpolation for Bayer formats and disable it for * greyscale formats. @@ -1025,29 +1025,22 @@ 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 isp_format_info *info) + const struct v4l2_mbus_framefmt *format) { struct isp_device *isp = to_isp_device(prev); struct prev_params *params; - if (info->bpp == 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: + switch (format->code) { + case V4L2_MBUS_FMT_SGRBG10_1X10: prev->params.cfa_order = 0; break; - case V4L2_MBUS_FMT_SRGGB8_1X8: + case V4L2_MBUS_FMT_SRGGB10_1X10: prev->params.cfa_order = 1; break; - case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SBGGR10_1X10: prev->params.cfa_order = 2; break; - case V4L2_MBUS_FMT_SGBRG8_1X8: + case V4L2_MBUS_FMT_SGBRG10_1X10: prev->params.cfa_order = 3; break; default: @@ -1396,7 +1389,6 @@ 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; @@ -1410,19 +1402,17 @@ 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, info); + preview_config_input_format(prev, format); 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) * - DIV_ROUND_UP(info->bpp, 8)); + ALIGN(format->width, 0x20) * 2); preview_setup_hw(prev, update, active); @@ -1719,11 +1709,6 @@ __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, -- cgit v1.2.3 From a626f0ac9dec1d28d16f62eed6f5ea0879ddb4e0 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 10:37:47 -0300 Subject: [media] v4l: vivi: support for dmabuf importing This patch enhances VIVI driver with a support for importing a buffer from DMABUF file descriptors. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index ef0fc276f7e..0d59b9db83c 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1302,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; -- cgit v1.2.3 From b0a55033adc03d7d6a1f4693ed916e97c733e526 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 10:37:49 -0300 Subject: [media] v4l: mem2mem_testdev: add support for dmabuf importing This patch enhances mem2mem_testdev device with support for DMABUF importing via V4L2_MEMORY_DMABUF memory type. Signed-off-by: Tomasz Stanislawski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mem2mem_testdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 2e2121e9813..7487d7208de 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; -- cgit v1.2.3 From fa77521fe525ce1d7e4f099e58c1d64a136d252b Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 10:37:48 -0300 Subject: [media] v4l: s5p-tv: mixer: support for dmabuf importing This patch enhances s5p-tv with support for DMABUF importing via V4L2_MEMORY_DMABUF memory type. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 9b52f3a48c2..79a7ea3ae04 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -1092,7 +1092,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, -- cgit v1.2.3 From 9bd09fd7385739bf571a6ea848bee425327ce34e Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 10:37:49 -0300 Subject: [media] v4l: s5p-fimc: support for dmabuf importing This patch enhances s5p-fimc with support for DMABUF importing via V4L2_MEMORY_DMABUF memory type. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Sylwester Nawrocki Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 2 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 367efd164d0..246bb329c7a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1730,7 +1730,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 4c4e9010511..6d1ab49aaf0 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -622,7 +622,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 +633,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; -- cgit v1.2.3 From b28d61b622496038db621fe1dab1caf0e91c6c94 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 11:32:26 -0300 Subject: [media] v4l: s5p-fimc: support for dmabuf exporting This patch enhances s5p-fimc with support for DMABUF exporting via VIDIOC_EXPBUF ioctl. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 9 +++++++++ drivers/media/platform/s5p-fimc/fimc-m2m.c | 10 ++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 246bb329c7a..e5fd159e3ee 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1231,6 +1231,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) { @@ -1355,6 +1363,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, diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 6d1ab49aaf0..26bcf4bc420 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -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, -- cgit v1.2.3 From 3c44efd3b2af20c50769998e496d744a6d490e66 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 11:32:27 -0300 Subject: [media] v4l: s5p-tv: mixer: support for dmabuf exporting This patch enhances s5p-tv with support for DMABUF exporting via VIDIOC_EXPBUF ioctl. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 79a7ea3ae04..7379e77bf4e 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -697,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); @@ -724,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, -- cgit v1.2.3 From 6fa9dd069e62864d990ff9c7726f16b34018e718 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Thu, 14 Jun 2012 11:32:28 -0300 Subject: [media] v4l: s5p-mfc: support for dmabuf exporting This patch enhances s5p-mfc with support for DMABUF exporting via VIDIOC_EXPBUF ioctl. Signed-off-by: Tomasz Stanislawski Signed-off-by: Kyungmin Park Acked-by: Hans Verkuil Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 14 ++++++++++++++ drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 14 ++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index eb6a70b0f82..6dad9a74f61 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 5c1d727268b..f92f6ddd739 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) @@ -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, -- cgit v1.2.3 From babde1c243b25592038dd7193867a49729e3d505 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 22 Aug 2012 20:09:18 -0300 Subject: [media] V4L: Add driver for S3C24XX/S3C64XX SoC series camera interface This patch adds V4L2 driver for Samsung S3C24XX/S3C64XX SoC series camera interface. The driver exposes a subdev device node for CAMIF pixel resolution and crop control and two video capture nodes - for the "codec" and "preview" data paths. It has been tested on Mini2440 (s3c2440) and Mini6410 (s3c6410) board with gstreamer and mplayer. Signed-off-by: Sylwester Nawrocki Signed-off-by: Tomasz Figa Signed-off-by: Andrey Gusakov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 12 + drivers/media/platform/Makefile | 1 + drivers/media/platform/s3c-camif/Makefile | 5 + drivers/media/platform/s3c-camif/camif-capture.c | 1672 ++++++++++++++++++++++ drivers/media/platform/s3c-camif/camif-core.c | 662 +++++++++ drivers/media/platform/s3c-camif/camif-core.h | 393 +++++ drivers/media/platform/s3c-camif/camif-regs.c | 606 ++++++++ drivers/media/platform/s3c-camif/camif-regs.h | 269 ++++ 8 files changed, 3620 insertions(+) create mode 100644 drivers/media/platform/s3c-camif/Makefile create mode 100644 drivers/media/platform/s3c-camif/camif-capture.c create mode 100644 drivers/media/platform/s3c-camif/camif-core.c create mode 100644 drivers/media/platform/s3c-camif/camif-core.h create mode 100644 drivers/media/platform/s3c-camif/camif-regs.c create mode 100644 drivers/media/platform/s3c-camif/camif-regs.h (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 181c7686e41..3dcfea612c4 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 baaa55026c8..4817d280217 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/s3c-camif/Makefile b/drivers/media/platform/s3c-camif/Makefile new file mode 100644 index 00000000000..50bf8c59b99 --- /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 00000000000..a55793c3d81 --- /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 + * Copyright (C) 2012 Tomasz Figa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 00000000000..0dd65376c06 --- /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 + * Copyright (C) 2012 Tomasz Figa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Tomasz Figa "); +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 00000000000..261134baa65 --- /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 + * Copyright (C) 2012 Tomasz Figa + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..1a3b4fc05ec --- /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 + * Copyright (C) 2012 Tomasz Figa + * + * 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 +#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 00000000000..af2d472ea1d --- /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 + * Copyright (C) 2012 Tomasz Figa + * + * 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 + +/* + * 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_ */ -- cgit v1.2.3 From 07d19e3cce15ed4a481236fe5e24668a720d0cbd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 12 Sep 2012 22:10:40 -0300 Subject: [media] omap3isp: Use monotonic timestamps for statistics buffers V4L2 buffers use the monotonic clock, while statistics buffers use wall time. This makes it difficult to correlate video frames and statistics. Switch statistics buffers to the monotonic clock to fix this. Reported-by: Antoine Reversat Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispstat.c | 5 +++-- drivers/media/platform/omap3isp/ispstat.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index b8640be692f..6e24895002a 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -256,7 +256,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)) { @@ -536,7 +536,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 9b7c8654dc8..8221d0c05b6 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; -- cgit v1.2.3 From d83501a0416160cc45e84b6719f7a2784013cf16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 11 Oct 2012 22:08:04 -0300 Subject: [media] omap3isp: Remove unneeded module memory address definitions The OMAP3ISP_*_REG_OFFSET, OMAP3ISP_*_REG_BASE and OMAP3ISP_*_REG macros are not needed. Remove them. The only exception is the OMAP3ISP_HIST_REG_BASE address. Replace it with the memory address received through platform resources. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isphist.c | 8 +++- drivers/media/platform/omap3isp/ispreg.h | 77 ------------------------------- 2 files changed, 6 insertions(+), 79 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index d1a8dee5e1c..74326ff342a 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -72,11 +72,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; } @@ -477,6 +480,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); @@ -494,7 +499,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/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h index e2c57f334c5..fd13d8bb63e 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) -- cgit v1.2.3 From c19d19ebd200ad1f982159519017129a5ed6515e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 14 Oct 2012 07:31:48 -0300 Subject: [media] omap3isp: Add CSI configuration registers from control block to ISP resources Add the registers used to configure the CSI-2 receiver PHY on OMAP3430 and 3630 and map them in the ISP driver. The register is part of the control block but it only is needed by the ISP driver. Signed-off-by: Sakari Ailus Acked-by: Tony Lindgren Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 6 ++++-- drivers/media/platform/omap3isp/isp.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 99640d8c1db..5ea5520ff46 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -102,7 +102,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, @@ -119,7 +120,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, }, }; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 8be7487c326..6fed2228e78 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -72,6 +72,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 }; -- cgit v1.2.3 From ec51e960bc355c5ebf7501f1e74723d5d4f4212d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 14 Oct 2012 07:31:49 -0300 Subject: [media] omap3isp: Add PHY routing configuration Add PHY routing configuration for both 3430 and 3630. Also add register bit definitions of CSIRXFE and CAMERA_PHY_CTRL registers on OMAP 3430 and 3630, respectively. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispcsiphy.c | 88 +++++++++++++++++++++++++++++ drivers/media/platform/omap3isp/ispreg.h | 22 ++++++++ 2 files changed, 110 insertions(+) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 348f67ebbbc..8ac99b9dc05 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -32,6 +32,94 @@ #include "ispreg.h" #include "ispcsiphy.h" +static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, + bool ccp2_strobe) +{ + 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; + } + + /* 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; + } + + 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; + } + + if (ccp2_strobe) + csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM; + + 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); +} + /* * csiphy_lanes_config - Configuration of CSIPHY lanes. * diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h index fd13d8bb63e..b7d90e6fb01 100644 --- a/drivers/media/platform/omap3isp/ispreg.h +++ b/drivers/media/platform/omap3isp/ispreg.h @@ -1506,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 */ -- cgit v1.2.3 From a7b21061363dc980b3867a65d657ce7796303454 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 14 Oct 2012 07:31:50 -0300 Subject: [media] omap3isp: Configure CSI-2 phy based on platform data Configure CSI-2 phy based on platform data in the ISP driver. For that, the new V4L2_CID_IMAGE_SOURCE_PIXEL_RATE control is used. Previously the same was configured from the board code. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.h | 3 - drivers/media/platform/omap3isp/ispcsiphy.c | 153 +++++++++++++++------------- drivers/media/platform/omap3isp/ispcsiphy.h | 10 -- 3 files changed, 83 insertions(+), 83 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 6fed2228e78..accb3b04f5d 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -129,9 +129,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/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 8ac99b9dc05..d6eb4f9655f 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -120,36 +120,6 @@ static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on, return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe); } -/* - * csiphy_lanes_config - Configuration of CSIPHY lanes. - * - * Updates HW configuration. - * Called with phy->mutex taken. - */ -static void csiphy_lanes_config(struct isp_csiphy *phy) -{ - unsigned int i; - u32 reg; - - reg = isp_reg_readl(phy->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 |= (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 &= ~(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; - - isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); -} - /* * csiphy_power_autoswitch_enable * @enable: Sets or clears the autoswitch function enable flag. @@ -195,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; +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 - isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); -} - -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++) { @@ -250,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; } @@ -278,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) { @@ -299,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); @@ -315,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 e93a661e65d..14551fd7769 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); -- cgit v1.2.3 From aab84f55d45ff6838e708f0cb8e1c0166d339ef6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 20 Oct 2012 18:48:18 -0300 Subject: [media] omap3isp: Find source pad from external entity No longer assume pad number 0 is the source pad of the external entity. Find the source pad from the external entity and use it instead. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 5ea5520ff46..cb9bc343613 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1767,6 +1767,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) @@ -1814,7 +1815,19 @@ static int isp_register_entities(struct isp_device *isp) goto done; } - ret = media_entity_create_link(&sensor->entity, 0, input, pad, + 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, i, input, pad, flags); if (ret < 0) goto done; -- cgit v1.2.3 From 4feca39b3d9a7480b5e622a0b4a29941c85cdcd2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 22 Oct 2012 08:28:51 -0300 Subject: [media] omap3isp: Replace printk with dev_* Use the dev_* message logging API instead of raw printk. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 12 ++++++------ drivers/media/platform/omap3isp/ispcsi2.c | 6 +++--- drivers/media/platform/omap3isp/ispcsiphy.c | 2 +- drivers/media/platform/omap3isp/ispvideo.c | 3 ++- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index cb9bc343613..e8e724e2b4b 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1679,7 +1679,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); @@ -1689,7 +1689,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; } @@ -1714,7 +1714,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; } @@ -1722,7 +1722,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; } @@ -1809,8 +1809,8 @@ 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; } diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 6a3ff792af7..783f4b05b15 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 d6eb4f9655f..3d56b33f85e 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -157,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; } diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 75cd309035f..8759247cb24 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1391,7 +1391,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; -- cgit v1.2.3 From e023a11342b6624cc019c8cecfc488882f0721f9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 23 Oct 2012 08:11:55 -0300 Subject: [media] omap3isp: preview: Add support for 8-bit formats at the sink pad Support both grayscale (Y8) and Bayer (SBGGR8, SGBRG8, SGRBG8 and SRGGB8) formats. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isppreview.c | 41 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 1ae1c0909ed..691b92a3c3e 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, -- cgit v1.2.3 From b057c3c32229c0db914c03eefb2579d877612926 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Oct 2012 06:29:24 -0300 Subject: [media] omap3isp: Prepare/unprepare clocks before/after enable/disable Clock enable (disable) is split in two operations, prepare and enable (disable and unprepare). Perform both when enabling/disabling the ISP clocks. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index e8e724e2b4b..7da622ed86e 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1332,7 +1332,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) { @@ -1354,9 +1355,9 @@ static int isp_enable_clocks(struct isp_device *isp) else divisor = 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], @@ -1365,9 +1366,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]); @@ -1375,17 +1376,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; } @@ -1396,9 +1397,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[] = { -- cgit v1.2.3 From d8658bca2e5696df2b6c69bc5538f8fe54e4a01e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 27 Sep 2012 10:38:18 -0300 Subject: [media] omap3isp: Replace cpu_is_omap3630() with ISP revision check Drivers must not rely on cpu_is_omap* macros (they will soon become private). Use the ISP revision instead to identify the hardware. Signed-off-by: Laurent Pinchart Acked-by: Tony Lindgren Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 7da622ed86e..b34bf928d80 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1350,10 +1350,7 @@ 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_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); if (r) { @@ -2111,7 +2108,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; @@ -2120,6 +2121,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; @@ -2130,10 +2141,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; -- cgit v1.2.3