diff options
Diffstat (limited to 'drivers/media/platform')
95 files changed, 8341 insertions, 1702 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 05d7b6333461..0494d2769fd7 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -122,7 +122,7 @@ config VIDEO_S3C_CAMIF will be called s3c-camif. source "drivers/media/platform/soc_camera/Kconfig" -source "drivers/media/platform/s5p-fimc/Kconfig" +source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS @@ -145,7 +145,6 @@ config VIDEO_CODA depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV - select IRAM_ALLOC if SOC_IMX53 ---help--- Coda is a range of video codec IPs that supports H.264, MPEG-4, and other video formats. @@ -204,7 +203,7 @@ config VIDEO_SAMSUNG_EXYNOS_GSC config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 42089ba3600f..eee28dd78d7d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -30,7 +30,7 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.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_EXYNOS4_IS) += exynos4-is/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 5f209d5810dc..0e55b087076f 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -384,7 +384,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities - & V4L2_IN_CAP_CUSTOM_TIMINGS) { + & V4L2_IN_CAP_DV_TIMINGS) { struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; params.hdelay = bt->hsync + bt->hbackporch; @@ -633,7 +633,7 @@ static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) { struct bcap_device *bcap_dev = video_drvdata(file); int ret; @@ -641,11 +641,11 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; - ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std); + ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std); if (ret < 0) return ret; - bcap_dev->std = *std; + bcap_dev->std = std; return 0; } @@ -890,7 +890,7 @@ static int bcap_dbg_g_register(struct file *file, void *priv, } static int bcap_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct bcap_device *bcap_dev = video_drvdata(file); @@ -1029,6 +1029,7 @@ static int bcap_probe(struct platform_device *pdev) q->buf_struct_size = sizeof(struct bcap_buffer); q->ops = &bcap_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb2_queue_init(q); @@ -1110,7 +1111,7 @@ static int bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } - if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_DV_TIMINGS) { struct v4l2_dv_timings dv_timings; ret = v4l2_subdev_call(bcap_dev->sd, video, g_dv_timings, &dv_timings); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 20827ba168fc..48b8d7af386d 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -14,6 +14,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/firmware.h> +#include <linux/genalloc.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -23,7 +24,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/of.h> -#include <linux/platform_data/imx-iram.h> +#include <linux/platform_data/coda.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -43,6 +44,7 @@ #define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) +#define CODADX6_IRAM_SIZE 0xb000 #define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */ #define CODA_MAX_FRAMEBUFFERS 2 @@ -128,7 +130,10 @@ struct coda_dev { struct coda_aux_buf codebuf; struct coda_aux_buf workbuf; + struct gen_pool *iram_pool; + long unsigned int iram_vaddr; long unsigned int iram_paddr; + unsigned long iram_size; spinlock_t irqlock; struct mutex dev_mutex; @@ -1422,6 +1427,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &coda_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1433,6 +1439,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &coda_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1628,6 +1635,9 @@ static irqreturn_t coda_irq_handler(int irq, void *data) dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; } + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); @@ -1926,6 +1936,9 @@ static int coda_probe(struct platform_device *pdev) const struct of_device_id *of_id = of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev); const struct platform_device_id *pdev_id; + struct coda_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + struct gen_pool *pool; struct coda_dev *dev; struct resource *res; int ret, irq; @@ -1988,6 +2001,16 @@ static int coda_probe(struct platform_device *pdev) return -ENOENT; } + /* Get IRAM pool from device tree or platform data */ + pool = of_get_named_gen_pool(np, "iram", 0); + if (!pool && pdata) + pool = dev_get_gen_pool(pdata->iram_dev); + if (!pool) { + dev_err(&pdev->dev, "iram pool not available\n"); + return -ENOMEM; + } + dev->iram_pool = pool; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) return ret; @@ -2022,18 +2045,17 @@ static int coda_probe(struct platform_device *pdev) return -ENOMEM; } - if (dev->devtype->product == CODA_DX6) { - dev->iram_paddr = 0xffff4c00; - } else { - void __iomem *iram_vaddr; - - iram_vaddr = iram_alloc(CODA7_IRAM_SIZE, - &dev->iram_paddr); - if (!iram_vaddr) { - dev_err(&pdev->dev, "unable to alloc iram\n"); - return -ENOMEM; - } + if (dev->devtype->product == CODA_DX6) + dev->iram_size = CODADX6_IRAM_SIZE; + else + dev->iram_size = CODA7_IRAM_SIZE; + dev->iram_vaddr = gen_pool_alloc(dev->iram_pool, dev->iram_size); + if (!dev->iram_vaddr) { + dev_err(&pdev->dev, "unable to alloc iram\n"); + return -ENOMEM; } + dev->iram_paddr = gen_pool_virt_to_phys(dev->iram_pool, + dev->iram_vaddr); platform_set_drvdata(pdev, dev); @@ -2050,8 +2072,8 @@ static int coda_remove(struct platform_device *pdev) if (dev->alloc_ctx) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); - if (dev->iram_paddr) - iram_free(dev->iram_paddr, CODA7_IRAM_SIZE); + if (dev->iram_vaddr) + gen_pool_free(dev->iram_pool, dev->iram_vaddr, dev->iram_size); if (dev->codebuf.vaddr) dma_free_coherent(&pdev->dev, dev->codebuf.size, &dev->codebuf.vaddr, dev->codebuf.paddr); diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index ccfde4eb626a..afb3aec1320e 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -1,79 +1,47 @@ config VIDEO_DAVINCI_VPIF_DISPLAY - tristate "DM646x/DA850/OMAPL138 EVM Video Display" - depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) + tristate "TI DaVinci VPIF V4L2-Display driver" + depends on VIDEO_DEV && ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG - select VIDEO_DAVINCI_VPIF select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT help Enables Davinci VPIF module used for display devices. - This module is common for following DM6467/DA850/OMAPL138 - based display devices. + This module is used for display on TI DM6467/DA850/OMAPL138 + SoCs. - To compile this driver as a module, choose M here: the - module will be called vpif_display. + To compile this driver as a module, choose M here. There will + be two modules called vpif.ko and vpif_display.ko config VIDEO_DAVINCI_VPIF_CAPTURE - tristate "DM646x/DA850/OMAPL138 EVM Video Capture" - depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) + tristate "TI DaVinci VPIF video capture driver" + depends on VIDEO_DEV && ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG - select VIDEO_DAVINCI_VPIF help - Enables Davinci VPIF module used for captur devices. - This module is common for following DM6467/DA850/OMAPL138 - based capture devices. + Enables Davinci VPIF module used for capture devices. + This module is used for capture on TI DM6467/DA850/OMAPL138 + SoCs. - To compile this driver as a module, choose M here: the - module will be called vpif_capture. + To compile this driver as a module, choose M here. There will + be two modules called vpif.ko and vpif_capture.ko -config VIDEO_DAVINCI_VPIF - tristate "DaVinci VPIF Driver" - depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE - help - Support for DaVinci VPIF Driver. - - To compile this driver as a module, choose M here: the - module will be called vpif. - -config VIDEO_VPSS_SYSTEM - tristate "VPSS System module driver" - depends on ARCH_DAVINCI - help - Support for vpss system module for video driver - -config VIDEO_VPFE_CAPTURE - tristate "VPFE Video Capture Driver" +config VIDEO_DM6446_CCDC + tristate "TI DM6446 CCDC video capture driver" depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) - depends on I2C select VIDEOBUF_DMA_CONTIG help - Support for DMx/AMx VPFE based frame grabber. This is the - common V4L2 module for following DMx/AMx SoCs from Texas - Instruments:- DM6446, DM365, DM355 & AM3517/05. - - To compile this driver as a module, choose M here: the - module will be called vpfe-capture. - -config VIDEO_DM6446_CCDC - tristate "DM6446 CCDC HW module" - depends on VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces with decoder modules such as TVP5146 over BT656 or sensor module such as MT9T001 over a raw interface. This module configures the interface and CCDC/ISIF to do video frame capture from slave decoders. - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and dm644x_ccdc.ko config VIDEO_DM355_CCDC - tristate "DM355 CCDC HW module" - depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y + tristate "TI DM355 CCDC video capture driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG help Enables DM355 CCD hw module. DM355 CCDC hw interfaces with decoder modules such as TVP5146 over BT656 or @@ -81,31 +49,30 @@ config VIDEO_DM355_CCDC module configures the interface and CCDC/ISIF to do video frame capture from a slave decoders - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and dm355_ccdc.ko -config VIDEO_ISIF - tristate "ISIF HW module" - depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y +config VIDEO_DM365_ISIF + tristate "TI DM365 ISIF video capture driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG help Enables ISIF hw module. This is the hardware module for - configuring ISIF in VPFE to capture Raw Bayer RGB data from + configuring ISIF in VPFE to capture Raw Bayer RGB data from a image sensor or YUV data from a YUV source. - To compile this driver as a module, choose M here: the - module will be called vpfe. + To compile this driver as a module, choose M here. There will + be three modules called vpfe_capture.ko, vpss.ko and isif.ko config VIDEO_DAVINCI_VPBE_DISPLAY - tristate "DM644X/DM365/DM355 VPBE HW module" - depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 - select VIDEO_VPSS_SYSTEM + tristate "TI DaVinci VPBE V4L2-Display driver" + depends on ARCH_DAVINCI select VIDEOBUF2_DMA_CONTIG help Enables Davinci VPBE module used for display devices. - This module is common for following DM644x/DM365/DM355 + This module is used for display on TI DM644x/DM365/DM355 based display devices. - To compile this driver as a module, choose M here: the - module will be called vpbe. + To compile this driver as a module, choose M here. There will + be five modules created called vpss.ko, vpbe.ko, vpbe_osd.ko, + vpbe_venc.ko and vpbe_display.ko diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index f40f5219ca50..d74d9eeb0e9e 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -2,19 +2,14 @@ # Makefile for the davinci video device drivers. # -# VPIF -obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o - #VPIF Display driver -obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o #VPIF Capture driver -obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o +obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o # Capture: DM6446 and DM355 -obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o -obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o -obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o -obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o -obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ +obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o vpss.o dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o vpss.o dm355_ccdc.o +obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o vpss.o isif.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \ vpbe_venc.o vpbe_display.o diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index 4277e4ad810c..05f8fb7f7b70 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -37,7 +37,6 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/videodev2.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -59,10 +58,6 @@ static struct ccdc_oper_config { struct ccdc_params_raw bayer; /* YCbCr configuration */ struct ccdc_params_ycbcr ycbcr; - /* Master clock */ - struct clk *mclk; - /* slave clock */ - struct clk *sclk; /* ccdc base address */ void __iomem *base_addr; } ccdc_cfg = { @@ -85,7 +80,7 @@ static struct ccdc_oper_config { .mfilt1 = CCDC_NO_MEDIAN_FILTER1, .mfilt2 = CCDC_NO_MEDIAN_FILTER2, .alaw = { - .gama_wd = 2, + .gamma_wd = 2, }, .blk_clamp = { .sample_pixel = 1, @@ -303,8 +298,8 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) } if (ccdcparam->alaw.enable) { - if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || - ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) { dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); return -EINVAL; } @@ -680,8 +675,8 @@ static int ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { val |= (CCDC_ALAW_ENABLE | - ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) << + ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) << CCDC_GAMMAWD_INPUT_SHIFT)); } @@ -997,32 +992,10 @@ static int dm355_ccdc_probe(struct platform_device *pdev) goto fail_nomem; } - /* Get and enable Master clock */ - ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(ccdc_cfg.mclk)) { - status = PTR_ERR(ccdc_cfg.mclk); - goto fail_nomap; - } - if (clk_prepare_enable(ccdc_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Get and enable Slave clock */ - ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); - if (IS_ERR(ccdc_cfg.sclk)) { - status = PTR_ERR(ccdc_cfg.sclk); - goto fail_mclk; - } - if (clk_prepare_enable(ccdc_cfg.sclk)) { - status = -ENODEV; - goto fail_sclk; - } - /* Platform data holds setup_pinmux function ptr */ if (NULL == pdev->dev.platform_data) { status = -ENODEV; - goto fail_sclk; + goto fail_nomap; } setup_pinmux = pdev->dev.platform_data; /* @@ -1033,12 +1006,6 @@ static int dm355_ccdc_probe(struct platform_device *pdev) ccdc_cfg.dev = &pdev->dev; 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); fail_nomem: @@ -1052,10 +1019,6 @@ 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h index d6d2ef0533b5..2e1946e0b99f 100644 --- a/drivers/media/platform/davinci/dm355_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h @@ -153,7 +153,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE 1 -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_REC656IF_BT656_EN 3 #define CCDC_FMTCFG_FMTMODE_MASK 3 diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 318e80512998..30fa08405d61 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -38,7 +38,6 @@ #include <linux/uaccess.h> #include <linux/videodev2.h> #include <linux/gfp.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -60,10 +59,6 @@ static struct ccdc_oper_config { struct ccdc_params_raw bayer; /* YCbCr configuration */ struct ccdc_params_ycbcr ycbcr; - /* Master clock */ - struct clk *mclk; - /* slave clock */ - struct clk *sclk; /* ccdc base address */ void __iomem *base_addr; } ccdc_cfg = { @@ -228,9 +223,12 @@ static void ccdc_readregs(void) static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) { if (ccdcparam->alaw.enable) { - if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || - (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || - (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); + u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); + + if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) || + (max_gamma > max_data)) { dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); return -1; } @@ -560,8 +558,8 @@ void ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { - val = ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + val = ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); regw(val, CCDC_ALAW); dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); } @@ -988,38 +986,9 @@ static int dm644x_ccdc_probe(struct platform_device *pdev) goto fail_nomem; } - /* Get and enable Master clock */ - ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(ccdc_cfg.mclk)) { - status = PTR_ERR(ccdc_cfg.mclk); - goto fail_nomap; - } - if (clk_prepare_enable(ccdc_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Get and enable Slave clock */ - ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); - if (IS_ERR(ccdc_cfg.sclk)) { - status = PTR_ERR(ccdc_cfg.sclk); - goto fail_mclk; - } - if (clk_prepare_enable(ccdc_cfg.sclk)) { - status = -ENODEV; - goto fail_sclk; - } ccdc_cfg.dev = &pdev->dev; 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); fail_nomem: release_mem_region(res->start, resource_size(res)); fail_nores: @@ -1031,10 +1000,6 @@ 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) @@ -1049,18 +1014,12 @@ static int dm644x_ccdc_suspend(struct device *dev) ccdc_save_context(); /* Disable CCDC */ ccdc_enable(0); - /* Disable both master and slave clock */ - clk_disable_unprepare(ccdc_cfg.mclk); - clk_disable_unprepare(ccdc_cfg.sclk); return 0; } static int dm644x_ccdc_resume(struct device *dev) { - /* Enable both master and slave clock */ - 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/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h index 90370e414e2c..2b0aca5383f0 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h @@ -84,7 +84,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE (1 << 3) -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_BLK_CLAMP_ENABLE (1 << 31) #define CCDC_BLK_SGAIN_MASK 0x1F #define CCDC_BLK_ST_PXL_MASK 0x7FFF diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index 5050f9265f48..3332cca632e5 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -32,7 +32,6 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/videodev2.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> @@ -88,8 +87,6 @@ static struct isif_oper_config { struct isif_ycbcr_config ycbcr; struct isif_params_raw bayer; enum isif_data_pack data_pack; - /* Master clock */ - struct clk *mclk; /* ISIF base address */ void __iomem *base_addr; /* ISIF Linear Table 0 */ @@ -604,7 +601,7 @@ static int isif_config_raw(void) if (module_params->compress.alg == ISIF_ALAW) val |= ISIF_ALAW_ENABLE; - val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT); + val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); regw(val, CGAMMAWD); /* Configure DPCM compression settings */ @@ -1039,6 +1036,10 @@ static int isif_probe(struct platform_device *pdev) void *__iomem addr; int status = 0, i; + /* Platform data holds setup_pinmux function ptr */ + if (!pdev->dev.platform_data) + return -ENODEV; + /* * first try to register with vpfe. If not correct platform, then we * don't have to iomap @@ -1047,22 +1048,6 @@ static int isif_probe(struct platform_device *pdev) if (status < 0) return status; - /* Get and enable Master clock */ - isif_cfg.mclk = clk_get(&pdev->dev, "master"); - if (IS_ERR(isif_cfg.mclk)) { - status = PTR_ERR(isif_cfg.mclk); - goto fail_mclk; - } - if (clk_prepare_enable(isif_cfg.mclk)) { - status = -ENODEV; - goto fail_mclk; - } - - /* Platform data holds setup_pinmux function ptr */ - if (NULL == pdev->dev.platform_data) { - status = -ENODEV; - goto fail_mclk; - } setup_pinmux = pdev->dev.platform_data; /* * setup Mux configuration for ccdc which may be different for @@ -1124,9 +1109,6 @@ fail_nobase_res: release_mem_region(res->start, resource_size(res)); i--; } -fail_mclk: - clk_disable_unprepare(isif_cfg.mclk); - clk_put(isif_cfg.mclk); vpfe_unregister_ccdc_device(&isif_hw_dev); return status; } @@ -1146,8 +1128,6 @@ 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/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h index aa69a463c122..3993aece821b 100644 --- a/drivers/media/platform/davinci/isif_regs.h +++ b/drivers/media/platform/davinci/isif_regs.h @@ -203,8 +203,8 @@ #define ISIF_LPF_MASK 1 /* GAMMAWD registers */ -#define ISIF_ALAW_GAMA_WD_MASK 0xF -#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_GAMMA_WD_MASK 0xF +#define ISIF_ALAW_GAMMA_WD_SHIFT 1 #define ISIF_ALAW_ENABLE 1 #define ISIF_GAMMAWD_CFA_SHIFT 5 diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 4ca0f9a2ad8a..33b9660b7f77 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -344,7 +344,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS && + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS && !memcmp(&output->modes[i].dv_timings, dv_timings, sizeof(*dv_timings))) break; @@ -385,7 +385,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev, struct v4l2_dv_timings *dv_timings) { if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; return 0; } @@ -412,7 +412,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS) { + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) { if (j == timings->index) break; j++; @@ -431,7 +431,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, * Sets the standard if supported by the current encoder. Return the status. * 0 - success & -EINVAL on error */ -static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id) { struct vpbe_config *cfg = vpbe_dev->cfg; int out_index = vpbe_dev->current_out_index; @@ -442,14 +442,14 @@ static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) V4L2_OUT_CAP_STD)) return -EINVAL; - ret = vpbe_get_std_info(vpbe_dev, *std_id); + ret = vpbe_get_std_info(vpbe_dev, std_id); if (ret) return ret; mutex_lock(&vpbe_dev->lock); ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, - s_std_output, *std_id); + s_std_output, std_id); /* set the lcd controller output for the given mode */ if (!ret) { struct osd_state *osd_device = vpbe_dev->osd_device; @@ -513,9 +513,9 @@ static int vpbe_set_mode(struct vpbe_device *vpbe_dev, */ if (preset_mode->timings_type & VPBE_ENC_STD) return vpbe_s_std(vpbe_dev, - &preset_mode->std_id); + preset_mode->std_id); if (preset_mode->timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { dv_timings = preset_mode->dv_timings; return vpbe_s_dv_timings(vpbe_dev, &dv_timings); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 5e6b0cab514b..1802f11e939f 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -983,7 +983,7 @@ static int vpbe_display_try_fmt(struct file *file, void *priv, * 0 - success & -EINVAL on error */ static int vpbe_display_s_std(struct file *file, void *priv, - v4l2_std_id *std_id) + v4l2_std_id std_id) { struct vpbe_fh *fh = priv; struct vpbe_layer *layer = fh->layer; @@ -1176,10 +1176,6 @@ vpbe_display_s_dv_timings(struct file *file, void *priv, "Failed to set the dv timings info\n"); return -EINVAL; } - /* set the current norm to zero to be consistent. If STD is used - * v4l2 layer will set the norm properly on successful s_std call - */ - layer->video_dev.current_norm = 0; return 0; } @@ -1202,7 +1198,7 @@ vpbe_display_g_dv_timings(struct file *file, void *priv, /* Get the given standard in the encoder */ if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; } else { return -EINVAL; @@ -1404,6 +1400,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpbe_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1600,7 +1597,7 @@ static int vpbe_display_g_register(struct file *file, void *priv, } static int vpbe_display_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { return 0; } @@ -1693,12 +1690,8 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->vfl_dir = VFL_DIR_TX; if (disp_dev->vpbe_dev->current_timings.timings_type & - VPBE_ENC_STD) { + VPBE_ENC_STD) vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); - vbd->current_norm = - disp_dev->vpbe_dev->current_timings.std_id; - } else - vbd->current_norm = 0; snprintf(vbd->name, sizeof(vbd->name), "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 12ad17c52ef3..396a51cbede7 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -52,6 +52,9 @@ static struct platform_device_id vpbe_osd_devtype[] = { .name = DM355_VPBE_OSD_SUBDEV_NAME, .driver_data = VPBE_VERSION_3, }, + { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index bdbebd59df98..87eef9be08ed 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -51,6 +51,9 @@ static struct platform_device_id vpbe_venc_devtype[] = { .name = DM355_VPBE_VENC_SUBDEV_NAME, .driver_data = VPBE_VERSION_3, }, + { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); @@ -199,6 +202,25 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) } } +static void +venc_enable_vpss_clock(int venc_type, + enum vpbe_enc_timings_type type, + unsigned int pclock) +{ + if (venc_type == VPBE_VERSION_1) + return; + + if (venc_type == VPBE_VERSION_2 && (type == VPBE_ENC_STD || (type == + VPBE_ENC_DV_TIMINGS && pclock <= 27000000))) { + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + vpss_enable_clock(VPSS_VPBE_CLOCK, 1); + return; + } + + if (venc_type == VPBE_VERSION_3 && type == VPBE_ENC_STD) + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 0); +} + #define VDAC_CONFIG_SD_V3 0x0E21A6B6 #define VDAC_CONFIG_SD_V2 0x081141CF /* @@ -217,6 +239,7 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_525_60); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_3) { @@ -262,6 +285,7 @@ static int venc_set_pal(struct v4l2_subdev *sd) if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_625_50); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_3) { @@ -313,9 +337,10 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_2) @@ -360,9 +385,10 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000); venc_enabledigitaloutput(sd, 0); if (venc->venc_type == VPBE_VERSION_2) @@ -400,9 +426,10 @@ static int venc_set_720p60_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000); venc_enabledigitaloutput(sd, 0); venc_write(sd, VENC_OSDCLK0, 0); @@ -428,9 +455,10 @@ static int venc_set_1080i30_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; + venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000); venc_enabledigitaloutput(sd, 0); venc_write(sd, VENC_OSDCLK0, 0); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 28d019da4c01..8c50d3074866 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -376,7 +376,7 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) * values in ccdc */ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, - const v4l2_std_id *std_id) + v4l2_std_id std_id) { struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; struct v4l2_mbus_framefmt mbus_fmt; @@ -384,7 +384,7 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, int i, ret = 0; for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { - if (vpfe_standards[i].std_id & *std_id) { + if (vpfe_standards[i].std_id & std_id) { vpfe_dev->std_info.active_pixels = vpfe_standards[i].width; vpfe_dev->std_info.active_lines = @@ -461,7 +461,7 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) /* Configure the default format information */ ret = vpfe_config_image_format(vpfe_dev, - &vpfe_standards[vpfe_dev->std_index].std_id); + vpfe_standards[vpfe_dev->std_index].std_id); if (ret) return ret; @@ -1107,6 +1107,7 @@ static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) static int vpfe_s_input(struct file *file, void *priv, unsigned int index) { struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_subdev *sd; struct vpfe_subdev_info *sdinfo; int subdev_index, inp_index; struct vpfe_route *route; @@ -1138,14 +1139,15 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) } sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + sd = vpfe_dev->sd[subdev_index]; route = &sdinfo->routes[inp_index]; if (route && sdinfo->can_route) { input = route->input; output = route->output; } - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, s_routing, input, output, 0); + if (sd) + ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); if (ret) { v4l2_err(&vpfe_dev->v4l2_dev, @@ -1154,6 +1156,8 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) goto unlock_out; } vpfe_dev->current_subdev = sdinfo; + if (sd) + vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler; vpfe_dev->current_input = index; vpfe_dev->std_index = 0; @@ -1164,7 +1168,7 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) /* set the default image parameters in the device */ ret = vpfe_config_image_format(vpfe_dev, - &vpfe_standards[vpfe_dev->std_index].std_id); + vpfe_standards[vpfe_dev->std_index].std_id); unlock_out: mutex_unlock(&vpfe_dev->lock); return ret; @@ -1189,7 +1193,7 @@ static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) return ret; } -static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpfe_device *vpfe_dev = video_drvdata(file); struct vpfe_subdev_info *sdinfo; @@ -1211,7 +1215,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, *std_id); + core, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); goto unlock_out; @@ -1439,41 +1443,6 @@ static int vpfe_dqbuf(struct file *file, void *priv, buf, file->f_flags & O_NONBLOCK); } -static int vpfe_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, queryctrl, qctrl); - -} - -static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, g_ctrl, ctrl); -} - -static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - - sdinfo = vpfe_dev->current_subdev; - - return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_ctrl, ctrl); -} - /* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field @@ -1717,7 +1686,7 @@ unlock_out: static long vpfe_param_handler(struct file *file, void *priv, - bool valid_prio, int cmd, void *param) + bool valid_prio, unsigned int cmd, void *param) { struct vpfe_device *vpfe_dev = video_drvdata(file); int ret = 0; @@ -1781,9 +1750,6 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querystd = vpfe_querystd, .vidioc_s_std = vpfe_s_std, .vidioc_g_std = vpfe_g_std, - .vidioc_queryctrl = vpfe_queryctrl, - .vidioc_g_ctrl = vpfe_g_ctrl, - .vidioc_s_ctrl = vpfe_s_ctrl, .vidioc_reqbufs = vpfe_reqbufs, .vidioc_querybuf = vpfe_querybuf, .vidioc_qbuf = vpfe_qbuf, @@ -1918,7 +1884,6 @@ static int vpfe_probe(struct platform_device *pdev) vfd->fops = &vpfe_fops; vfd->ioctl_ops = &vpfe_ioctl_ops; vfd->tvnorms = 0; - vfd->current_norm = V4L2_STD_PAL; vfd->v4l2_dev = &vpfe_dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s_V%d.%d.%d", @@ -2007,6 +1972,7 @@ static int vpfe_probe(struct platform_device *pdev) /* set first sub device as current one */ vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler; /* We have at least one sub device to work with */ mutex_unlock(&ccdc_lock); diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index 28638a86f129..ea82a8bd2803 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -23,8 +23,8 @@ #include <linux/spinlock.h> #include <linux/kernel.h> #include <linux/io.h> -#include <linux/clk.h> #include <linux/err.h> +#include <linux/pm_runtime.h> #include <linux/v4l2-dv-timings.h> #include <mach/hardware.h> @@ -44,13 +44,13 @@ static struct resource *res; spinlock_t vpif_lock; void __iomem *vpif_base; -struct clk *vpif_clk; +EXPORT_SYMBOL_GPL(vpif_base); /** - * ch_params: video standard configuration parameters for vpif + * vpif_ch_params: video standard configuration parameters for vpif * The table must include all presets from supported subdevices. */ -const struct vpif_channel_config_params ch_params[] = { +const struct vpif_channel_config_params vpif_ch_params[] = { /* HDTV formats */ { .name = "480p59_94", @@ -220,8 +220,10 @@ const struct vpif_channel_config_params ch_params[] = { .stdid = V4L2_STD_625_50, }, }; +EXPORT_SYMBOL_GPL(vpif_ch_params); -const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params); +const unsigned int vpif_ch_params_count = ARRAY_SIZE(vpif_ch_params); +EXPORT_SYMBOL_GPL(vpif_ch_params_count); static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) { @@ -439,19 +441,13 @@ static int vpif_probe(struct platform_device *pdev) goto fail; } - vpif_clk = clk_get(&pdev->dev, "vpif"); - if (IS_ERR(vpif_clk)) { - status = PTR_ERR(vpif_clk); - goto clk_fail; - } - clk_prepare_enable(vpif_clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_get(&pdev->dev); spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); return 0; -clk_fail: - iounmap(vpif_base); fail: release_mem_region(res->start, res_len); return status; @@ -459,11 +455,7 @@ fail: static int vpif_remove(struct platform_device *pdev) { - if (vpif_clk) { - clk_disable_unprepare(vpif_clk); - clk_put(vpif_clk); - } - + pm_runtime_disable(&pdev->dev); iounmap(vpif_base); release_mem_region(res->start, res_len); return 0; @@ -472,13 +464,13 @@ static int vpif_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int vpif_suspend(struct device *dev) { - clk_disable_unprepare(vpif_clk); + pm_runtime_put(dev); return 0; } static int vpif_resume(struct device *dev) { - clk_prepare_enable(vpif_clk); + pm_runtime_get(dev); return 0; } diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h index a1ab6a0f4e9e..9956e6788693 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/davinci/vpif.h @@ -638,7 +638,7 @@ struct vpif_channel_config_params { }; extern const unsigned int vpif_ch_params_count; -extern const struct vpif_channel_config_params ch_params[]; +extern const struct vpif_channel_config_params vpif_ch_params[]; struct vpif_video_params; struct vpif_params; diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 5892d2bc8eee..5f98df1fc8a0 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -563,7 +563,7 @@ static int vpif_update_std_info(struct channel_obj *ch) vpif_dbg(2, debug, "vpif_update_std_info\n"); for (index = 0; index < vpif_ch_params_count; index++) { - config = &ch_params[index]; + config = &vpif_ch_params[index]; if (config->hd_sd == 0) { vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) { @@ -1035,6 +1035,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_cap_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1394,7 +1395,7 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) * @priv: file handle * @std_id: ptr to std id */ -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; @@ -1423,7 +1424,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) fh->initialized = 1; /* Call encoder subdevice function to set the standard */ - ch->video.stdid = *std_id; + ch->video.stdid = std_id; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); /* Get the information about the standard */ @@ -1436,7 +1437,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) vpif_config_format(ch); /* set standard in the sub device */ - ret = v4l2_subdev_call(ch->sd, core, s_std, *std_id); + ret = v4l2_subdev_call(ch->sd, core, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); return ret; @@ -1923,7 +1924,8 @@ static int vpif_dbg_g_register(struct file *file, void *priv, * Returns zero or -EINVAL if write operations fails. */ static int vpif_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ + const struct v4l2_dbg_register *reg) +{ struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index dd249c96126d..1b3fb5ca2ad4 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -511,7 +511,7 @@ static int vpif_update_std_info(struct channel_obj *ch) int i; for (i = 0; i < vpif_ch_params_count; i++) { - config = &ch_params[i]; + config = &vpif_ch_params[i]; if (config->hd_sd == 0) { vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) { @@ -1001,6 +1001,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { @@ -1058,14 +1059,14 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return vb2_qbuf(&common->buffer_queue, buf); } -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; int ret = 0; - if (!(*std_id & VPIF_V4L2_STD)) + if (!(std_id & VPIF_V4L2_STD)) return -EINVAL; if (common->started) { @@ -1074,7 +1075,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } /* Call encoder subdevice function to set the standard */ - ch->video.stdid = *std_id; + ch->video.stdid = std_id; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); /* Get the information about the standard */ if (vpif_update_resolution(ch)) @@ -1092,14 +1093,14 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) vpif_config_format(ch); ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, - s_std_output, *std_id); + s_std_output, std_id); if (ret < 0) { vpif_err("Failed to set output standard\n"); return ret; } ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, - s_std, *std_id); + s_std, std_id); if (ret < 0) vpif_err("Failed to set standard for sub devices\n"); return ret; @@ -1567,7 +1568,8 @@ static int vpif_dbg_g_register(struct file *file, void *priv, * Returns zero or -EINVAL if write operations fails. */ static int vpif_dbg_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ + const struct v4l2_dbg_register *reg) +{ struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index a19c552232d1..8a2f01e344ee 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -17,14 +17,11 @@ * * common vpss system module platform driver for all video drivers. */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/compiler.h> #include <linux/io.h> +#include <linux/pm_runtime.h> + #include <media/davinci/vpss.h> MODULE_LICENSE("GPL"); @@ -99,7 +96,7 @@ enum vpss_platform_type { /* * vpss operations. Depends on platform. Not all functions are available - * on all platforms. The api, first check if a functio is available before + * on all platforms. The api, first check if a function is available before * invoking it. In the probe, the function ptrs are initialized based on * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. */ @@ -114,7 +111,7 @@ struct vpss_hw_ops { void (*set_sync_pol)(struct vpss_sync_pol); /* set the PG_FRAME_SIZE register*/ void (*set_pg_frame_size)(struct vpss_pg_frame_size); - /* check and clear interrupt if occured */ + /* check and clear interrupt if occurred */ int (*dma_complete_interrupt)(void); }; @@ -233,7 +230,7 @@ EXPORT_SYMBOL(vpss_clear_wbl_overflow); /* * dm355_enable_clock - Enable VPSS Clock - * @clock_sel: CLock to be enabled/disabled + * @clock_sel: Clock to be enabled/disabled * @en: enable/disable flag * * This is called to enable or disable a vpss clock @@ -490,6 +487,10 @@ static int vpss_probe(struct platform_device *pdev) } else oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + pm_runtime_enable(&pdev->dev); + + pm_runtime_get(&pdev->dev); + spin_lock_init(&oper_cfg.vpss_lock); dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); return 0; @@ -507,6 +508,7 @@ static int vpss_remove(struct platform_device *pdev) { struct resource *res; + pm_runtime_disable(&pdev->dev); iounmap(oper_cfg.vpss_regs_base0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); @@ -518,10 +520,28 @@ static int vpss_remove(struct platform_device *pdev) return 0; } +static int vpss_suspend(struct device *dev) +{ + pm_runtime_put(dev); + return 0; +} + +static int vpss_resume(struct device *dev) +{ + pm_runtime_get(dev); + return 0; +} + +static const struct dev_pm_ops vpss_pm_ops = { + .suspend = vpss_suspend, + .resume = vpss_resume, +}; + static struct platform_driver vpss_driver = { .driver = { .name = "vpss", .owner = THIS_MODULE, + .pm = &vpss_pm_ops, }, .remove = vpss_remove, .probe = vpss_probe, diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 82d9f6ac12f3..33b5ffc8d66d 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1054,16 +1054,18 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc) static int gsc_m2m_resume(struct gsc_dev *gsc) { + struct gsc_ctx *ctx; unsigned long flags; spin_lock_irqsave(&gsc->slock, flags); /* Clear for full H/W setup in first run after resume */ + ctx = gsc->m2m.ctx; gsc->m2m.ctx = NULL; spin_unlock_irqrestore(&gsc->slock, flags); if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) - gsc_m2m_job_finish(gsc->m2m.ctx, - VB2_BUF_STATE_ERROR); + gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + return 0; } @@ -1204,7 +1206,7 @@ static int gsc_resume(struct device *dev) /* Do not resume if the device was idle before system suspend */ spin_lock_irqsave(&gsc->slock, flags); if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) || - !gsc_m2m_active(gsc)) { + !gsc_m2m_opened(gsc)) { spin_unlock_irqrestore(&gsc->slock, flags); return 0; } diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 386c0a7a3a52..40a73f7d20da 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -80,6 +80,9 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (src_vb && dst_vb) { + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_vb, vb_state); v4l2_m2m_buf_done(dst_vb, vb_state); @@ -584,6 +587,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -596,6 +600,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 6f5b5a486cf3..e22d147a6940 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -12,7 +12,6 @@ #include <linux/io.h> #include <linux/delay.h> -#include <mach/map.h> #include "gsc-core.h" diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index f997a5203b7c..6ff99b5849f9 100644 --- a/drivers/media/platform/s5p-fimc/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -1,21 +1,22 @@ -config VIDEO_SAMSUNG_S5P_FIMC - bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)" +config VIDEO_SAMSUNG_EXYNOS4_IS + bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME help Say Y here to enable camera host interface devices for Samsung S5P and EXYNOS SoC series. -if VIDEO_SAMSUNG_S5P_FIMC +if VIDEO_SAMSUNG_EXYNOS4_IS config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV + select MFD_SYSCON if OF help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host - interface and video postprocessor (FIMC and FIMC-LITE) devices. + interface and video postprocessor (FIMC) devices. To compile this driver as a module, choose M here: the module will be called s5p-fimc. @@ -45,4 +46,16 @@ config VIDEO_EXYNOS_FIMC_LITE module will be called exynos-fimc-lite. endif +config VIDEO_EXYNOS4_FIMC_IS + tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" + select VIDEOBUF2_DMA_CONTIG + depends on OF + select FW_LOADER + help + This is a V4L2 driver for Samsung EXYNOS4x12 SoC series + FIMC-IS (Imaging Subsystem). + + To compile this driver as a module, choose M here: the + module will be called exynos4-fimc-is. + endif # VIDEO_SAMSUNG_S5P_FIMC diff --git a/drivers/media/platform/s5p-fimc/Makefile b/drivers/media/platform/exynos4-is/Makefile index 46485143e1ca..f25f46377399 100644 --- a/drivers/media/platform/s5p-fimc/Makefile +++ b/drivers/media/platform/exynos4-is/Makefile @@ -1,7 +1,10 @@ -s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o +exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o +exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o +obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index f553cc2a8ee8..528f41369364 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -27,32 +27,33 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include "fimc-mdevice.h" +#include "media-dev.h" #include "fimc-core.h" #include "fimc-reg.h" static int fimc_capture_hw_init(struct fimc_dev *fimc) { + struct fimc_source_info *si = &fimc->vid_cap.source_config; struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct fimc_pipeline *p = &fimc->pipeline; - struct fimc_sensor_info *sensor; + int ret; unsigned long flags; - int ret = 0; - if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL) - return -ENXIO; - if (ctx->s_frame.fmt == NULL) + if (ctx == NULL || ctx->s_frame.fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]); + if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) { + ret = fimc_hw_camblk_cfg_writeback(fimc); + if (ret < 0) + return ret; + } spin_lock_irqsave(&fimc->slock, flags); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_set_yuv_order(ctx); - fimc_hw_set_camera_polarity(fimc, &sensor->pdata); - fimc_hw_set_camera_type(fimc, &sensor->pdata); - fimc_hw_set_camera_source(fimc, &sensor->pdata); + fimc_hw_set_camera_polarity(fimc, si); + fimc_hw_set_camera_type(fimc, si); + fimc_hw_set_camera_source(fimc, si); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); ret = fimc_set_scaler_info(ctx); @@ -65,7 +66,7 @@ static int fimc_capture_hw_init(struct fimc_dev *fimc) fimc_hw_set_effect(ctx); fimc_hw_set_output_path(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); } @@ -168,7 +169,7 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) fimc_hw_set_effect(ctx); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); @@ -286,8 +287,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc_activate_capture(ctx); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + return fimc_pipeline_call(fimc, set_stream, + &fimc->pipeline, 1); } return 0; @@ -443,35 +444,28 @@ static void buffer_queue(struct vb2_buffer *vb) if (vb2_is_streaming(&vid_cap->vbq) && vid_cap->active_buf_cnt >= min_bufs && !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { + int ret; + fimc_activate_capture(ctx); spin_unlock_irqrestore(&fimc->slock, flags); - if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) + return; + + ret = fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 1); + if (ret < 0) + v4l2_err(&vid_cap->vfd, "stream on failed: %d\n", ret); return; } spin_unlock_irqrestore(&fimc->slock, flags); } -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->fimc_dev->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->fimc_dev->lock); -} - static struct vb2_ops fimc_capture_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -530,7 +524,7 @@ static int fimc_capture_open(struct file *file) goto unlock; } - if (++fimc->vid_cap.refcnt == 1) { + if (v4l2_fh_is_singular_file(file)) { ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vid_cap.vfd.entity, true); @@ -543,8 +537,9 @@ static int fimc_capture_open(struct file *file) if (ret < 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); pm_runtime_put_sync(&fimc->pdev->dev); - fimc->vid_cap.refcnt--; v4l2_fh_release(file); + } else { + fimc->vid_cap.refcnt++; } } unlock: @@ -553,59 +548,34 @@ unlock: return ret; } -static int fimc_capture_close(struct file *file) +static int fimc_capture_release(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); + struct fimc_vid_cap *vc = &fimc->vid_cap; int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); mutex_lock(&fimc->lock); - if (--fimc->vid_cap.refcnt == 0) { + if (v4l2_fh_is_singular_file(file)) { + if (vc->streaming) { + media_entity_pipeline_stop(&vc->vfd.entity); + vc->streaming = false; + } clear_bit(ST_CAPT_BUSY, &fimc->state); fimc_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); + fimc->vid_cap.refcnt--; } pm_runtime_put(&fimc->pdev->dev); - if (fimc->vid_cap.refcnt == 0) { - vb2_queue_release(&fimc->vid_cap.vbq); + if (v4l2_fh_is_singular_file(file)) fimc_ctrls_delete(fimc->vid_cap.ctx); - } - - ret = v4l2_fh_release(file); - - mutex_unlock(&fimc->lock); - return ret; -} - -static unsigned int fimc_capture_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_dev *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return POLL_ERR; - ret = vb2_poll(&fimc->vid_cap.vbq, file, wait); - mutex_unlock(&fimc->lock); - - return ret; -} - -static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_dev *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = vb2_mmap(&fimc->vid_cap.vbq, vma); + ret = vb2_fop_release(file); mutex_unlock(&fimc->lock); return ret; @@ -614,10 +584,10 @@ static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) static const struct v4l2_file_operations fimc_capture_fops = { .owner = THIS_MODULE, .open = fimc_capture_open, - .release = fimc_capture_close, - .poll = fimc_capture_poll, + .release = fimc_capture_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = fimc_capture_mmap, + .mmap = vb2_fop_mmap, }; /* @@ -642,18 +612,22 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, fimc_fmt_is_user_defined(ctx->s_frame.fmt->color)) *code = ctx->s_frame.fmt->mbus_code; - if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK) + if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE) mask |= FMT_FLAGS_M2M; + if (pad == FIMC_SD_PAD_SINK_FIFO) + mask = FMT_FLAGS_WRITEBACK; + ffmt = fimc_find_format(fourcc, code, mask, 0); if (WARN_ON(!ffmt)) return NULL; + if (code) *code = ffmt->mbus_code; if (fourcc) *fourcc = ffmt->fourcc; - if (pad == FIMC_SD_PAD_SINK) { + if (pad != FIMC_SD_PAD_SOURCE) { max_w = fimc_fmt_is_user_defined(ffmt->color) ? pl->scaler_dis_w : pl->scaler_en_w; /* Apply the camera input interface pixel constraints */ @@ -768,16 +742,13 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, /* * The video node ioctl operations */ -static int fimc_vidioc_querycap_capture(struct file *file, void *priv, +static int fimc_cap_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct fimc_dev *fimc = video_drvdata(file); - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; - + __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE); return 0; } @@ -887,7 +858,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, tfmt->width = mf->width; tfmt->height = mf->height; ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SINK); + NULL, &fcc, FIMC_SD_PAD_SINK_CAM); ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, NULL, &fcc, FIMC_SD_PAD_SOURCE); if (ffmt && ffmt->mbus_code) @@ -974,7 +945,7 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, - FIMC_SD_PAD_SINK); + FIMC_SD_PAD_SINK_CAM); ctx->s_frame.f_width = pix->width; ctx->s_frame.f_height = pix->height; } @@ -1028,7 +999,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf; + struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt; struct fimc_frame *ff = &ctx->d_frame; struct fimc_fmt *s_fmt = NULL; int ret, i; @@ -1040,7 +1011,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, - FIMC_SD_PAD_SINK); + FIMC_SD_PAD_SINK_CAM); ctx->s_frame.f_width = pix->width; ctx->s_frame.f_height = pix->height; } @@ -1157,44 +1128,51 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) static int fimc_pipeline_validate(struct fimc_dev *fimc) { struct v4l2_subdev_format sink_fmt, src_fmt; - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct v4l2_subdev *sd; - struct media_pad *pad; - int ret; - - /* Start with the video capture node pad */ - pad = media_entity_remote_source(&vid_cap->vd_pad); - if (pad == NULL) - return -EPIPE; - /* FIMC.{N} subdevice */ - sd = media_entity_to_v4l2_subdev(pad->entity); + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct v4l2_subdev *sd = &vc->subdev; + struct media_pad *sink_pad, *src_pad; + int i, ret; while (1) { - /* Retrieve format at the sink pad */ - pad = &sd->entity.pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) + /* + * Find current entity sink pad and any remote sink pad linked + * to it. We stop if there is no sink pad in current entity or + * it is not linked to any other remote entity. + */ + src_pad = NULL; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct media_pad *p = &sd->entity.pads[i]; + + if (p->flags & MEDIA_PAD_FL_SINK) { + sink_pad = p; + src_pad = media_entity_remote_source(sink_pad); + if (src_pad) + break; + } + } + + if (src_pad == NULL || + media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; + /* Don't call FIMC subdev operation to avoid nested locking */ - if (sd == &fimc->vid_cap.subdev) { - struct fimc_frame *ff = &vid_cap->ctx->s_frame; + if (sd == &vc->subdev) { + struct fimc_frame *ff = &vc->ctx->s_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; } else { - sink_fmt.pad = pad->index; + sink_fmt.pad = sink_pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; } - /* Retrieve format at the source pad */ - pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - sd = media_entity_to_v4l2_subdev(pad->entity); - src_fmt.pad = pad->index; + /* Retrieve format at the source pad */ + sd = media_entity_to_v4l2_subdev(src_pad->entity); + src_fmt.pad = src_pad->index; src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) @@ -1208,7 +1186,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (sd == fimc->pipeline.subdevs[IDX_SENSOR] && fimc_user_defined_mbus_fmt(src_fmt.format.code)) { struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES]; - struct fimc_frame *frame = &vid_cap->ctx->d_frame; + struct fimc_frame *frame = &vc->ctx->d_frame; unsigned int i; ret = fimc_get_sensor_frame_desc(sd, plane_fmt, @@ -1230,96 +1208,80 @@ static int fimc_cap_streamon(struct file *file, void *priv, { struct fimc_dev *fimc = video_drvdata(file); struct fimc_pipeline *p = &fimc->pipeline; - struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct media_entity *entity = &vc->vfd.entity; + struct fimc_source_info *si = NULL; + struct v4l2_subdev *sd; int ret; if (fimc_capture_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, p->m_pipeline); if (ret < 0) return ret; - if (fimc->vid_cap.user_subdev_api) { + sd = p->subdevs[IDX_SENSOR]; + if (sd) + si = v4l2_get_subdev_hostdata(sd); + + if (si == NULL) { + ret = -EPIPE; + goto err_p_stop; + } + /* + * Save configuration data related to currently attached image + * sensor or other data source, e.g. FIMC-IS. + */ + vc->source_config = *si; + + if (vc->input == GRP_ID_FIMC_IS) + vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; + + if (vc->user_subdev_api) { ret = fimc_pipeline_validate(fimc); - if (ret < 0) { - media_entity_pipeline_stop(&sd->entity); - return ret; - } + if (ret < 0) + goto err_p_stop; } - return vb2_streamon(&fimc->vid_cap.vbq, type); + + ret = vb2_ioctl_streamon(file, priv, type); + if (!ret) { + vc->streaming = true; + return ret; + } + +err_p_stop: + media_entity_pipeline_stop(entity); + return ret; } static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; - ret = vb2_streamoff(&fimc->vid_cap.vbq, type); - if (ret == 0) - media_entity_pipeline_stop(&sd->entity); - return ret; + ret = vb2_ioctl_streamoff(file, priv, type); + if (ret < 0) + return ret; + + media_entity_pipeline_stop(&fimc->vid_cap.vfd.entity); + fimc->vid_cap.streaming = false; + return 0; } static int fimc_cap_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { struct fimc_dev *fimc = video_drvdata(file); - int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs); + int ret; + + ret = vb2_ioctl_reqbufs(file, priv, reqbufs); if (!ret) fimc->vid_cap.reqbufs_count = reqbufs->count; - return ret; -} - -static int fimc_cap_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_querybuf(&fimc->vid_cap.vbq, buf); -} - -static int fimc_cap_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_dev *fimc = video_drvdata(file); - - 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) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); -} - -static int fimc_cap_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct fimc_dev *fimc = video_drvdata(file); - return vb2_create_bufs(&fimc->vid_cap.vbq, create); -} - -static int fimc_cap_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct fimc_dev *fimc = video_drvdata(file); - - return vb2_prepare_buf(&fimc->vid_cap.vbq, b); + return ret; } static int fimc_cap_g_selection(struct file *file, void *fh, @@ -1410,7 +1372,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh, } static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { - .vidioc_querycap = fimc_vidioc_querycap_capture, + .vidioc_querycap = fimc_cap_querycap, .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, @@ -1418,14 +1380,12 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, .vidioc_reqbufs = fimc_cap_reqbufs, - .vidioc_querybuf = fimc_cap_querybuf, - - .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, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_streamon = fimc_cap_streamon, .vidioc_streamoff = fimc_cap_streamoff, @@ -1487,7 +1447,7 @@ static const struct media_entity_operations fimc_sd_media_ops = { void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { - struct fimc_sensor_info *sensor; + struct fimc_source_info *si; struct fimc_vid_buffer *buf; struct fimc_md *fmd; struct fimc_dev *fimc; @@ -1496,11 +1456,12 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, if (sd == NULL) return; - sensor = v4l2_get_subdev_hostdata(sd); + si = v4l2_get_subdev_hostdata(sd); fmd = entity_to_fimc_mdev(&sd->entity); spin_lock_irqsave(&fmd->slock, flags); - fimc = sensor ? sensor->host : NULL; + + fimc = si ? source_to_sensor_info(si)->host : NULL; if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY && test_bit(ST_CAPT_PEND, &fimc->state)) { @@ -1537,25 +1498,37 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *ff = &ctx->s_frame; struct v4l2_mbus_framefmt *mf; - struct fimc_frame *ff; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); fmt->format = *mf; return 0; } - mf = &fmt->format; - mf->colorspace = V4L2_COLORSPACE_JPEG; - ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame; + mf = &fmt->format; mutex_lock(&fimc->lock); - /* The pixel code is same on both input and output pad */ - if (!WARN_ON(ctx->s_frame.fmt == NULL)) - mf->code = ctx->s_frame.fmt->mbus_code; - mf->width = ff->f_width; - mf->height = ff->f_height; + + switch (fmt->pad) { + case FIMC_SD_PAD_SOURCE: + if (!WARN_ON(ff->fmt == NULL)) + mf->code = ff->fmt->mbus_code; + /* Sink pads crop rectangle size */ + mf->width = ff->width; + mf->height = ff->height; + break; + case FIMC_SD_PAD_SINK_FIFO: + *mf = fimc->vid_cap.wb_fmt; + break; + case FIMC_SD_PAD_SINK_CAM: + default: + *mf = fimc->vid_cap.ci_fmt; + break; + } + mutex_unlock(&fimc->lock); + mf->colorspace = V4L2_COLORSPACE_JPEG; return 0; } @@ -1566,15 +1539,15 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; - struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct fimc_ctx *ctx = vc->ctx; struct fimc_frame *ff; struct fimc_fmt *ffmt; dbg("pad%d: code: 0x%x, %dx%d", fmt->pad, mf->code, mf->width, mf->height); - if (fmt->pad == FIMC_SD_PAD_SOURCE && - vb2_is_busy(&fimc->vid_cap.vbq)) + if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq)) return -EBUSY; mutex_lock(&fimc->lock); @@ -1596,21 +1569,32 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, fimc_alpha_ctrl_update(ctx); fimc_capture_mark_jpeg_xfer(ctx, ffmt->color); - - ff = fmt->pad == FIMC_SD_PAD_SINK ? - &ctx->s_frame : &ctx->d_frame; + if (fmt->pad == FIMC_SD_PAD_SOURCE) { + ff = &ctx->d_frame; + /* Sink pads crop rectangle size */ + mf->width = ctx->s_frame.width; + mf->height = ctx->s_frame.height; + } else { + ff = &ctx->s_frame; + } mutex_lock(&fimc->lock); set_frame_bounds(ff, mf->width, mf->height); - fimc->vid_cap.mf = *mf; + + if (fmt->pad == FIMC_SD_PAD_SINK_FIFO) + vc->wb_fmt = *mf; + else if (fmt->pad == FIMC_SD_PAD_SINK_CAM) + vc->ci_fmt = *mf; + ff->fmt = ffmt; /* Reset the crop rectangle if required. */ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE))) set_frame_crop(ff, 0, 0, mf->width, mf->height); - if (fmt->pad == FIMC_SD_PAD_SINK) + if (fmt->pad != FIMC_SD_PAD_SOURCE) ctx->state &= ~FIMC_COMPOSE; + mutex_unlock(&fimc->lock); return 0; } @@ -1625,7 +1609,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_rect *r = &sel->r; struct v4l2_rect *try_sel; - if (sel->pad != FIMC_SD_PAD_SINK) + if (sel->pad == FIMC_SD_PAD_SOURCE) return -EINVAL; mutex_lock(&fimc->lock); @@ -1681,7 +1665,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_rect *try_sel; unsigned long flags; - if (sel->pad != FIMC_SD_PAD_SINK) + if (sel->pad == FIMC_SD_PAD_SOURCE) return -EINVAL; mutex_lock(&fimc->lock); @@ -1752,9 +1736,9 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev) { struct video_device *vfd = &fimc->vid_cap.vfd; - struct fimc_vid_cap *vid_cap; + struct vb2_queue *q = &fimc->vid_cap.vbq; struct fimc_ctx *ctx; - struct vb2_queue *q; + struct fimc_vid_cap *vid_cap; int ret = -ENOMEM; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -1776,27 +1760,27 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->v4l2_dev = v4l2_dev; vfd->minor = -1; vfd->release = video_device_release_empty; + vfd->queue = q; vfd->lock = &fimc->lock; video_set_drvdata(vfd, fimc); - vid_cap = &fimc->vid_cap; vid_cap->active_buf_cnt = 0; - vid_cap->reqbufs_count = 0; - vid_cap->refcnt = 0; + vid_cap->reqbufs_count = 0; + vid_cap->ctx = ctx; INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - vid_cap->ctx = ctx; - 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 | VB2_DMABUF; - q->drv_priv = fimc->vid_cap.ctx; + q->drv_priv = ctx; q->ops = &fimc_capture_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct fimc_vid_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &fimc->lock; ret = vb2_queue_init(q); if (ret) @@ -1882,10 +1866,11 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) int ret; v4l2_subdev_init(sd, &fimc_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id); - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, fimc->vid_cap.sd_pads, 0); diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index e3916bde45cf..379a5e9d52a7 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -20,7 +20,10 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/list.h> +#include <linux/mfd/syscon.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <linux/clk.h> #include <media/v4l2-ioctl.h> @@ -29,7 +32,7 @@ #include "fimc-core.h" #include "fimc-reg.h" -#include "fimc-mdevice.h" +#include "media-dev.h" static char *fimc_clocks[MAX_FIMC_CLOCKS] = { "sclk_fimc", "fimc" @@ -77,6 +80,10 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, }, { + .name = "YUV 4:4:4", + .mbus_code = V4L2_MBUS_FMT_YUV10_1X30, + .flags = FMT_FLAGS_WRITEBACK, + }, { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = { 16 }, @@ -206,6 +213,17 @@ struct fimc_fmt *fimc_get_format(unsigned int index) return &fimc_formats[index]; } +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps) +{ + strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + cap->device_caps = caps; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; +} + int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation) { @@ -405,34 +423,34 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { case FIMC_FMT_YCRYCB422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; break; case FIMC_FMT_CBYCRY422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; break; case FIMC_FMT_CRYCBY422: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; break; case FIMC_FMT_YCBYCR422: default: - ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; break; } dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { case FIMC_FMT_YCRYCB422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; break; case FIMC_FMT_CBYCRY422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; break; case FIMC_FMT_CRYCBY422: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; break; case FIMC_FMT_YCBYCR422: default: - ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; break; } dbg("ctx->out_order_1p= %d", ctx->out_order_1p); @@ -440,14 +458,14 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; + bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) depth += f->fmt->depth[i]; f->dma_offset.y_h = f->offs_h; - if (!variant->pix_hoff) + if (!pix_hoff) f->dma_offset.y_h *= (depth >> 3); f->dma_offset.y_v = f->offs_v; @@ -458,7 +476,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cr_h = f->offs_h; f->dma_offset.cr_v = f->offs_v; - if (!variant->pix_hoff) { + if (!pix_hoff) { if (f->fmt->colplanes == 3) { f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; @@ -589,7 +607,6 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -606,7 +623,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx) ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - if (variant->has_alpha) + if (ctx->fimc_dev->drv_data->alpha_color) ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, max_alpha, 1, 0); @@ -677,7 +694,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; - if (ctrl == NULL || !fimc->variant->has_alpha) + if (ctrl == NULL || !fimc->drv_data->alpha_color) return; v4l2_ctrl_lock(ctrl); @@ -850,56 +867,128 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc) static int fimc_m2m_resume(struct fimc_dev *fimc) { + struct fimc_ctx *ctx; unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); /* Clear for full H/W setup in first run after resume */ + ctx = fimc->m2m.ctx; fimc->m2m.ctx = NULL; spin_unlock_irqrestore(&fimc->slock, flags); if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state)) - fimc_m2m_job_finish(fimc->m2m.ctx, - VB2_BUF_STATE_ERROR); + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + + return 0; +} + +static const struct of_device_id fimc_of_match[]; + +static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq) +{ + struct device *dev = &fimc->pdev->dev; + struct device_node *node = dev->of_node; + const struct of_device_id *of_id; + struct fimc_variant *v; + struct fimc_pix_limit *lim; + u32 args[FIMC_PIX_LIMITS_MAX]; + int ret; + + if (of_property_read_bool(node, "samsung,lcd-wb")) + return -ENODEV; + + v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL); + if (!v) + return -ENOMEM; + + of_id = of_match_node(fimc_of_match, node); + if (!of_id) + return -EINVAL; + fimc->drv_data = of_id->data; + ret = of_property_read_u32_array(node, "samsung,pix-limits", + args, FIMC_PIX_LIMITS_MAX); + if (ret < 0) + return ret; + + lim = (struct fimc_pix_limit *)&v[1]; + + lim->scaler_en_w = args[0]; + lim->scaler_dis_w = args[1]; + lim->out_rot_en_w = args[2]; + lim->out_rot_dis_w = args[3]; + v->pix_limit = lim; + + ret = of_property_read_u32_array(node, "samsung,min-pix-sizes", + args, 2); + v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0]; + v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1]; + ret = of_property_read_u32_array(node, "samsung,min-pix-alignment", + args, 2); + v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0]; + v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1]; + + ret = of_property_read_u32(node, "samsung,rotators", &args[1]); + v->has_inp_rot = ret ? 1 : args[1] & 0x01; + v->has_out_rot = ret ? 1 : args[1] & 0x10; + v->has_mainscaler_ext = of_property_read_bool(node, + "samsung,mainscaler-ext"); + + v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb"); + v->has_cam_if = of_property_read_bool(node, "samsung,cam-if"); + of_property_read_u32(node, "clock-frequency", clk_freq); + fimc->id = of_alias_get_id(node, "fimc"); + + fimc->variant = v; return 0; } static int fimc_probe(struct platform_device *pdev) { - const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); - struct s5p_platform_fimc *pdata; + struct device *dev = &pdev->dev; + u32 lclk_freq = 0; struct fimc_dev *fimc; struct resource *res; int ret = 0; - if (pdev->id >= drv_data->num_entities) { - dev_err(&pdev->dev, "Invalid platform device id: %d\n", - pdev->id); - return -EINVAL; - } - - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->id = pdev->id; - - fimc->variant = drv_data->variant[fimc->id]; fimc->pdev = pdev; - pdata = pdev->dev.platform_data; - fimc->pdata = pdata; + + if (dev->of_node) { + ret = fimc_parse_dt(fimc, &lclk_freq); + if (ret < 0) + return ret; + } else { + fimc->drv_data = fimc_get_drvdata(pdev); + fimc->id = pdev->id; + } + if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities || + fimc->id < 0) { + dev_err(dev, "Invalid driver data or device id (%d)\n", + fimc->id); + return -EINVAL; + } + if (!dev->of_node) + fimc->variant = fimc->drv_data->variant[fimc->id]; init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); + fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node); + if (IS_ERR(fimc->sysreg)) + return PTR_ERR(fimc->sysreg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -907,7 +996,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + if (lclk_freq == 0) + lclk_freq = fimc->drv_data->lclk_frequency; + + ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq); if (ret < 0) return ret; @@ -915,10 +1007,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, fimc_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + dev_err(dev, "failed to install irq (%d)\n", ret); goto err_clk; } @@ -927,23 +1019,23 @@ static int fimc_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id); + dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_unregister_capture_subdev(fimc); err_clk: @@ -1046,35 +1138,21 @@ static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [1] = { .scaler_en_w = 4224, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [2] = { .scaler_en_w = 1920, .scaler_dis_w = 8192, - .in_rot_en_h = 1280, - .in_rot_dis_w = 8192, .out_rot_en_w = 1280, .out_rot_dis_w = 1920, }, - [3] = { - .scaler_en_w = 1920, - .scaler_dis_w = 8192, - .in_rot_en_h = 1366, - .in_rot_dis_w = 8192, - .out_rot_en_w = 1366, - .out_rot_dis_w = 1920, - }, }; static const struct fimc_variant fimc0_variant_s5p = { @@ -1085,7 +1163,6 @@ static const struct fimc_variant fimc0_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[0], }; @@ -1095,12 +1172,10 @@ static const struct fimc_variant fimc2_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc0_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1108,12 +1183,10 @@ static const struct fimc_variant fimc0_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc1_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1122,80 +1195,18 @@ static const struct fimc_variant fimc1_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 1, .min_vsize_align = 1, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, - .pix_hoff = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; -static const struct fimc_variant fimc0_variant_exynos4210 = { - .pix_hoff = 1, - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc3_variant_exynos4210 = { - .pix_hoff = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[3], -}; - -static const struct fimc_variant fimc0_variant_exynos4x12 = { - .pix_hoff = 1, - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_isp_wb = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc3_variant_exynos4x12 = { - .pix_hoff = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[3], -}; - /* S5PC100 */ static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { @@ -1203,8 +1214,9 @@ static const struct fimc_drvdata fimc_drvdata_s5p = { [1] = &fimc0_variant_s5p, [2] = &fimc2_variant_s5p, }, - .num_entities = 3, + .num_entities = 3, .lclk_frequency = 133000000UL, + .out_buf_count = 4, }; /* S5PV210, S5PC110 */ @@ -1214,32 +1226,30 @@ static const struct fimc_drvdata fimc_drvdata_s5pv210 = { [1] = &fimc1_variant_s5pv210, [2] = &fimc2_variant_s5pv210, }, - .num_entities = 3, - .lclk_frequency = 166000000UL, + .num_entities = 3, + .lclk_frequency = 166000000UL, + .out_buf_count = 4, + .dma_pix_hoff = 1, }; /* EXYNOS4210, S5PV310, S5PC210 */ static const struct fimc_drvdata fimc_drvdata_exynos4210 = { - .variant = { - [0] = &fimc0_variant_exynos4210, - [1] = &fimc0_variant_exynos4210, - [2] = &fimc0_variant_exynos4210, - [3] = &fimc3_variant_exynos4210, - }, - .num_entities = 4, + .num_entities = 4, .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; /* EXYNOS4212, EXYNOS4412 */ static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { - .variant = { - [0] = &fimc0_variant_exynos4x12, - [1] = &fimc0_variant_exynos4x12, - [2] = &fimc0_variant_exynos4x12, - [3] = &fimc3_variant_exynos4x12, - }, - .num_entities = 4, - .lclk_frequency = 166000000UL, + .num_entities = 4, + .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; static const struct platform_device_id fimc_driver_ids[] = { @@ -1256,9 +1266,22 @@ static const struct platform_device_id fimc_driver_ids[] = { .name = "exynos4x12-fimc", .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, - {}, + { }, +}; + +static const struct of_device_id fimc_of_match[] = { + { + .compatible = "samsung,s5pv210-fimc", + .data = &fimc_drvdata_s5pv210, + }, { + .compatible = "samsung,exynos4210-fimc", + .data = &fimc_drvdata_exynos4210, + }, { + .compatible = "samsung,exynos4212-fimc", + .data = &fimc_drvdata_exynos4x12, + }, + { /* sentinel */ }, }; -MODULE_DEVICE_TABLE(platform, fimc_driver_ids); static const struct dev_pm_ops fimc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) @@ -1270,9 +1293,10 @@ static struct platform_driver fimc_driver = { .remove = fimc_remove, .id_table = fimc_driver_ids, .driver = { - .name = FIMC_MODULE_NAME, - .owner = THIS_MODULE, - .pm = &fimc_pm_ops, + .of_match_table = fimc_of_match, + .name = FIMC_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &fimc_pm_ops, } }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 412d50708f75..539a3f71c16a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -12,8 +12,10 @@ /*#define DEBUG*/ #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/spinlock.h> +#include <linux/mfd/syscon.h> #include <linux/types.h> #include <linux/videodev2.h> #include <linux/io.h> @@ -33,7 +35,7 @@ /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) #define MAX_FIMC_CLOCKS 2 -#define FIMC_MODULE_NAME "s5p-fimc" +#define FIMC_DRIVER_NAME "exynos4-fimc" #define FIMC_MAX_DEVS 4 #define FIMC_MAX_OUT_BUFS 4 #define SCALER_MAX_HRATIO 64 @@ -42,6 +44,10 @@ #define FIMC_CAMIF_MAX_HEIGHT 0x2000 #define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M) #define FIMC_MAX_PLANES 3 +#define FIMC_PIX_LIMITS_MAX 4 +#define FIMC_DEF_MIN_SIZE 16 +#define FIMC_DEF_HEIGHT_ALIGN 2 +#define FIMC_DEF_HOR_OFFS_ALIGN 1 /* indices to the clocks array */ enum { @@ -132,36 +138,6 @@ enum fimc_color_fmt { #define FIMC_COLOR_RANGE_NARROW (1 << 3) /** - * struct fimc_fmt - the driver's internal color format data - * @mbus_code: Media Bus pixel code, -1 if not applicable - * @name: format description - * @fourcc: the fourcc code for this format, 0 if not applicable - * @color: the corresponding fimc_color_fmt - * @memplanes: number of physically non-contiguous data planes - * @colplanes: number of physically contiguous data planes - * @depth: per plane driver's private 'number of bits per pixel' - * @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no) - * @flags: flags indicating which operation mode format applies to - */ -struct fimc_fmt { - enum v4l2_mbus_pixelcode mbus_code; - char *name; - u32 fourcc; - u32 color; - u16 memplanes; - u16 colplanes; - u8 depth[VIDEO_MAX_PLANES]; - u16 mdataplanes; - u16 flags; -#define FMT_FLAGS_CAM (1 << 0) -#define FMT_FLAGS_M2M_IN (1 << 1) -#define FMT_FLAGS_M2M_OUT (1 << 2) -#define FMT_FLAGS_M2M (1 << 1 | 1 << 2) -#define FMT_HAS_ALPHA (1 << 3) -#define FMT_FLAGS_COMPRESSED (1 << 4) -}; - -/** * struct fimc_dma_offset - pixel offset information for DMA * @y_h: y value horizontal offset * @y_v: y value vertical offset @@ -299,9 +275,10 @@ struct fimc_m2m_device { int refcnt; }; -#define FIMC_SD_PAD_SINK 0 -#define FIMC_SD_PAD_SOURCE 1 -#define FIMC_SD_PADS_NUM 2 +#define FIMC_SD_PAD_SINK_CAM 0 +#define FIMC_SD_PAD_SINK_FIFO 1 +#define FIMC_SD_PAD_SOURCE 2 +#define FIMC_SD_PADS_NUM 3 /** * struct fimc_vid_cap - camera capture device information @@ -310,7 +287,9 @@ struct fimc_m2m_device { * @subdev: subdev exposing the FIMC processing block * @vd_pad: fimc video capture node pad * @sd_pads: fimc video processing block pads - * @mf: media bus format at the FIMC camera input (and the scaler output) pad + * @ci_fmt: image format at the FIMC camera input (and the scaler output) + * @wb_fmt: image format at the FIMC ISP Writeback input + * @source_config: external image source related configuration structure * @pending_buf_q: the pending buffer queue head * @active_buf_q: the queue head of buffers scheduled in hardware * @vbq: the capture am video buffer queue @@ -329,8 +308,10 @@ struct fimc_vid_cap { struct video_device vfd; struct v4l2_subdev subdev; struct media_pad vd_pad; - struct v4l2_mbus_framefmt mf; struct media_pad sd_pads[FIMC_SD_PADS_NUM]; + struct v4l2_mbus_framefmt ci_fmt; + struct v4l2_mbus_framefmt wb_fmt; + struct fimc_source_info source_config; struct list_head pending_buf_q; struct list_head active_buf_q; struct vb2_queue vbq; @@ -338,6 +319,7 @@ struct fimc_vid_cap { int buf_index; unsigned int frame_count; unsigned int reqbufs_count; + bool streaming; int input_index; int refcnt; u32 input; @@ -365,10 +347,8 @@ struct fimc_pix_limit { /** * struct fimc_variant - FIMC device variant information - * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator - * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface @@ -378,23 +358,18 @@ struct fimc_pix_limit { * @min_out_pixsize: minimum output pixel size * @hor_offs_align: horizontal pixel offset aligment * @min_vsize_align: minimum vertical pixel size alignment - * @out_buf_count: the number of buffers in output DMA sequence */ struct fimc_variant { - unsigned int pix_hoff:1; unsigned int has_inp_rot:1; unsigned int has_out_rot:1; - unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; unsigned int has_isp_wb:1; - unsigned int has_alpha:1; const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; u16 min_vsize_align; - u16 out_buf_count; }; /** @@ -402,11 +377,20 @@ struct fimc_variant { * @variant: variant information for this device * @num_entities: number of fimc instances available in a SoC * @lclk_frequency: local bus clock frequency + * @cistatus2: 1 if the FIMC IPs have CISTATUS2 register + * @dma_pix_hoff: the horizontal DMA offset unit: 1 - pixels, 0 - bytes + * @alpha_color: 1 if alpha color component is supported + * @out_buf_count: maximum number of output DMA buffers supported */ struct fimc_drvdata { const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; + /* Fields common to all FIMC IP instances */ + u8 cistatus2; + u8 dma_pix_hoff; + u8 alpha_color; + u8 out_buf_count; }; #define fimc_get_drvdata(_pdev) \ @@ -420,6 +404,7 @@ struct fimc_ctx; * @lock: the mutex protecting this data structure * @pdev: pointer to the FIMC platform device * @pdata: pointer to the device platform data + * @sysreg: pointer to the SYSREG regmap * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) * @clock: clocks required for FIMC operation @@ -437,8 +422,10 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; + struct regmap *sysreg; const struct fimc_variant *variant; - u16 id; + const struct fimc_drvdata *drv_data; + int id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; wait_queue_head_t irq_queue; @@ -633,6 +620,8 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps); int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); @@ -660,6 +649,15 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc); int fimc_register_driver(void); void fimc_unregister_driver(void); +#ifdef CONFIG_MFD_SYSCON +static inline struct regmap * fimc_get_sysreg_regmap(struct device_node *node) +{ + return syscon_regmap_lookup_by_phandle(node, "samsung,sysreg"); +} +#else +#define fimc_get_sysreg_regmap(node) (NULL) +#endif + /* -----------------------------------------------------*/ /* fimc-m2m.c */ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/exynos4-is/fimc-is-command.h new file mode 100644 index 000000000000..0d1f52e394b1 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-command.h @@ -0,0 +1,137 @@ +/* + * Samsung Exynos4x12 FIMC-IS (Imaging Subsystem) driver + * + * FIMC-IS command set definitions + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_CMD_H_ +#define FIMC_IS_CMD_H_ + +#define FIMC_IS_COMMAND_VER 110 /* FIMC-IS command set version 1.10 */ + +/* Enumeration of commands beetween the FIMC-IS and the host processor. */ + +/* HOST to FIMC-IS */ +#define HIC_PREVIEW_STILL 0x0001 +#define HIC_PREVIEW_VIDEO 0x0002 +#define HIC_CAPTURE_STILL 0x0003 +#define HIC_CAPTURE_VIDEO 0x0004 +#define HIC_STREAM_ON 0x0005 +#define HIC_STREAM_OFF 0x0006 +#define HIC_SET_PARAMETER 0x0007 +#define HIC_GET_PARAMETER 0x0008 +#define HIC_SET_TUNE 0x0009 +#define HIC_GET_STATUS 0x000b +/* Sensor part */ +#define HIC_OPEN_SENSOR 0x000c +#define HIC_CLOSE_SENSOR 0x000d +#define HIC_SIMMIAN_INIT 0x000e +#define HIC_SIMMIAN_WRITE 0x000f +#define HIC_SIMMIAN_READ 0x0010 +#define HIC_POWER_DOWN 0x0011 +#define HIC_GET_SET_FILE_ADDR 0x0012 +#define HIC_LOAD_SET_FILE 0x0013 +#define HIC_MSG_CONFIG 0x0014 +#define HIC_MSG_TEST 0x0015 +/* FIMC-IS to HOST */ +#define IHC_GET_SENSOR_NUM 0x1000 +#define IHC_SET_SHOT_MARK 0x1001 +/* parameter1: frame number */ +/* parameter2: confidence level (smile 0~100) */ +/* parameter3: confidence level (blink 0~100) */ +#define IHC_SET_FACE_MARK 0x1002 +/* parameter1: coordinate count */ +/* parameter2: coordinate buffer address */ +#define IHC_FRAME_DONE 0x1003 +/* parameter1: frame start number */ +/* parameter2: frame count */ +#define IHC_AA_DONE 0x1004 +#define IHC_NOT_READY 0x1005 + +#define IH_REPLY_DONE 0x2000 +#define IH_REPLY_NOT_DONE 0x2001 + +enum fimc_is_scenario { + IS_SC_PREVIEW_STILL, + IS_SC_PREVIEW_VIDEO, + IS_SC_CAPTURE_STILL, + IS_SC_CAPTURE_VIDEO, + IS_SC_MAX +}; + +enum fimc_is_sub_scenario { + IS_SC_SUB_DEFAULT, + IS_SC_SUB_PS_VTCALL, + IS_SC_SUB_CS_VTCALL, + IS_SC_SUB_PV_VTCALL, + IS_SC_SUB_CV_VTCALL, +}; + +struct is_common_regs { + u32 hicmd; + u32 hic_sensorid; + u32 hic_param[4]; + u32 reserved1[4]; + + u32 ihcmd; + u32 ihc_sensorid; + u32 ihc_param[4]; + u32 reserved2[4]; + + u32 isp_sensor_id; + u32 isp_param[2]; + u32 reserved3[1]; + + u32 scc_sensor_id; + u32 scc_param[2]; + u32 reserved4[1]; + + u32 dnr_sensor_id; + u32 dnr_param[2]; + u32 reserved5[1]; + + u32 scp_sensor_id; + u32 scp_param[2]; + u32 reserved6[29]; +} __packed; + +struct is_mcuctl_reg { + u32 mcuctl; + u32 bboar; + + u32 intgr0; + u32 intcr0; + u32 intmr0; + u32 intsr0; + u32 intmsr0; + + u32 intgr1; + u32 intcr1; + u32 intmr1; + u32 intsr1; + u32 intmsr1; + + u32 intcr2; + u32 intmr2; + u32 intsr2; + u32 intmsr2; + + u32 gpoctrl; + u32 cpoenctlr; + u32 gpictlr; + + u32 reserved[0xd]; + + struct is_common_regs common; +} __packed; + +#endif /* FIMC_IS_CMD_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c new file mode 100644 index 000000000000..e8519e151c1a --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c @@ -0,0 +1,272 @@ +/* + * Samsung Exynos4 SoC series FIMC-IS slave interface driver + * + * Error log interface functions + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "fimc-is-errno.h" + +const char * const fimc_is_param_strerr(unsigned int error) +{ + switch (error) { + case ERROR_COMMON_CMD: + return "ERROR_COMMON_CMD: Invalid Command"; + case ERROR_COMMON_PARAMETER: + return "ERROR_COMMON_PARAMETER: Invalid Parameter"; + case ERROR_COMMON_SETFILE_LOAD: + return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading"; + case ERROR_COMMON_SETFILE_ADJUST: + return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted"; + case ERROR_COMMON_SETFILE_INDEX: + return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index"; + case ERROR_COMMON_INPUT_PATH: + return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state"; + case ERROR_COMMON_INPUT_INIT: + return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set"; + case ERROR_COMMON_OUTPUT_PATH: + return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)"; + case ERROR_COMMON_OUTPUT_INIT: + return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set"; + case ERROR_CONTROL_BYPASS: + return "ERROR_CONTROL_BYPASS"; + case ERROR_OTF_INPUT_FORMAT: + return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)"; + case ERROR_OTF_INPUT_WIDTH: + return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; + case ERROR_OTF_INPUT_HEIGHT: + return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_OTF_INPUT_BIT_WIDTH: + return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_INPUT_WIDTH: + return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; + case ERROR_DMA_INPUT_HEIGHT: + return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)"; + case ERROR_DMA_INPUT_FORMAT: + return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)"; + case ERROR_DMA_INPUT_BIT_WIDTH: + return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_INPUT_ORDER: + return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)"; + case ERROR_DMA_INPUT_PLANE: + return "ERROR_DMA_INPUT_PLANE: Invalid palne (DRC: 3, FD: 1, 2, 3)"; + case ERROR_OTF_OUTPUT_WIDTH: + return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)"; + case ERROR_OTF_OUTPUT_HEIGHT: + return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)"; + case ERROR_OTF_OUTPUT_FORMAT: + return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)"; + case ERROR_OTF_OUTPUT_BIT_WIDTH: + return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; + case ERROR_DMA_OUTPUT_WIDTH: + return "ERROR_DMA_OUTPUT_WIDTH"; + case ERROR_DMA_OUTPUT_HEIGHT: + return "ERROR_DMA_OUTPUT_HEIGHT"; + case ERROR_DMA_OUTPUT_FORMAT: + return "ERROR_DMA_OUTPUT_FORMAT"; + case ERROR_DMA_OUTPUT_BIT_WIDTH: + return "ERROR_DMA_OUTPUT_BIT_WIDTH"; + case ERROR_DMA_OUTPUT_PLANE: + return "ERROR_DMA_OUTPUT_PLANE"; + case ERROR_DMA_OUTPUT_ORDER: + return "ERROR_DMA_OUTPUT_ORDER"; + + /* Sensor Error(100~199) */ + case ERROR_SENSOR_I2C_FAIL: + return "ERROR_SENSOR_I2C_FAIL"; + case ERROR_SENSOR_INVALID_FRAMERATE: + return "ERROR_SENSOR_INVALID_FRAMERATE"; + case ERROR_SENSOR_INVALID_EXPOSURETIME: + return "ERROR_SENSOR_INVALID_EXPOSURETIME"; + case ERROR_SENSOR_INVALID_SIZE: + return "ERROR_SENSOR_INVALID_SIZE"; + case ERROR_SENSOR_INVALID_SETTING: + return "ERROR_SENSOR_INVALID_SETTING"; + case ERROR_SENSOR_ACTURATOR_INIT_FAIL: + return "ERROR_SENSOR_ACTURATOR_INIT_FAIL"; + case ERROR_SENSOR_INVALID_AF_POS: + return "ERROR_SENSOR_INVALID_AF_POS"; + case ERROR_SENSOR_UNSUPPORT_FUNC: + return "ERROR_SENSOR_UNSUPPORT_FUNC"; + case ERROR_SENSOR_UNSUPPORT_PERI: + return "ERROR_SENSOR_UNSUPPORT_PERI"; + case ERROR_SENSOR_UNSUPPORT_AF: + return "ERROR_SENSOR_UNSUPPORT_AF"; + + /* ISP Error (200~299) */ + case ERROR_ISP_AF_BUSY: + return "ERROR_ISP_AF_BUSY"; + case ERROR_ISP_AF_INVALID_COMMAND: + return "ERROR_ISP_AF_INVALID_COMMAND"; + case ERROR_ISP_AF_INVALID_MODE: + return "ERROR_ISP_AF_INVALID_MODE"; + + /* DRC Error (300~399) */ + /* FD Error (400~499) */ + case ERROR_FD_CONFIG_MAX_NUMBER_STATE: + return "ERROR_FD_CONFIG_MAX_NUMBER_STATE"; + case ERROR_FD_CONFIG_MAX_NUMBER_INVALID: + return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID"; + case ERROR_FD_CONFIG_YAW_ANGLE_STATE: + return "ERROR_FD_CONFIG_YAW_ANGLE_STATE"; + case ERROR_FD_CONFIG_YAW_ANGLE_INVALID: + return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n"; + case ERROR_FD_CONFIG_ROLL_ANGLE_STATE: + return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE"; + case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID: + return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID"; + case ERROR_FD_CONFIG_SMILE_MODE_INVALID: + return "ERROR_FD_CONFIG_SMILE_MODE_INVALID"; + case ERROR_FD_CONFIG_BLINK_MODE_INVALID: + return "ERROR_FD_CONFIG_BLINK_MODE_INVALID"; + case ERROR_FD_CONFIG_EYES_DETECT_INVALID: + return "ERROR_FD_CONFIG_EYES_DETECT_INVALID"; + case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID: + return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID"; + case ERROR_FD_CONFIG_ORIENTATION_STATE: + return "ERROR_FD_CONFIG_ORIENTATION_STATE"; + case ERROR_FD_CONFIG_ORIENTATION_INVALID: + return "ERROR_FD_CONFIG_ORIENTATION_INVALID"; + case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID: + return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID"; + case ERROR_FD_RESULT: + return "ERROR_FD_RESULT"; + case ERROR_FD_MODE: + return "ERROR_FD_MODE"; + default: + return "Unknown"; + } +} + +const char * const fimc_is_strerr(unsigned int error) +{ + error &= ~IS_ERROR_TIME_OUT_FLAG; + + switch (error) { + /* General */ + case IS_ERROR_INVALID_COMMAND: + return "IS_ERROR_INVALID_COMMAND"; + case IS_ERROR_REQUEST_FAIL: + return "IS_ERROR_REQUEST_FAIL"; + case IS_ERROR_INVALID_SCENARIO: + return "IS_ERROR_INVALID_SCENARIO"; + case IS_ERROR_INVALID_SENSORID: + return "IS_ERROR_INVALID_SENSORID"; + case IS_ERROR_INVALID_MODE_CHANGE: + return "IS_ERROR_INVALID_MODE_CHANGE"; + case IS_ERROR_INVALID_MAGIC_NUMBER: + return "IS_ERROR_INVALID_MAGIC_NUMBER"; + case IS_ERROR_INVALID_SETFILE_HDR: + return "IS_ERROR_INVALID_SETFILE_HDR"; + case IS_ERROR_BUSY: + return "IS_ERROR_BUSY"; + case IS_ERROR_SET_PARAMETER: + return "IS_ERROR_SET_PARAMETER"; + case IS_ERROR_INVALID_PATH: + return "IS_ERROR_INVALID_PATH"; + case IS_ERROR_OPEN_SENSOR_FAIL: + return "IS_ERROR_OPEN_SENSOR_FAIL"; + case IS_ERROR_ENTRY_MSG_THREAD_DOWN: + return "IS_ERROR_ENTRY_MSG_THREAD_DOWN"; + case IS_ERROR_ISP_FRAME_END_NOT_DONE: + return "IS_ERROR_ISP_FRAME_END_NOT_DONE"; + case IS_ERROR_DRC_FRAME_END_NOT_DONE: + return "IS_ERROR_DRC_FRAME_END_NOT_DONE"; + case IS_ERROR_SCALERC_FRAME_END_NOT_DONE: + return "IS_ERROR_SCALERC_FRAME_END_NOT_DONE"; + case IS_ERROR_ODC_FRAME_END_NOT_DONE: + return "IS_ERROR_ODC_FRAME_END_NOT_DONE"; + case IS_ERROR_DIS_FRAME_END_NOT_DONE: + return "IS_ERROR_DIS_FRAME_END_NOT_DONE"; + case IS_ERROR_TDNR_FRAME_END_NOT_DONE: + return "IS_ERROR_TDNR_FRAME_END_NOT_DONE"; + case IS_ERROR_SCALERP_FRAME_END_NOT_DONE: + return "IS_ERROR_SCALERP_FRAME_END_NOT_DONE"; + case IS_ERROR_WAIT_STREAM_OFF_NOT_DONE: + return "IS_ERROR_WAIT_STREAM_OFF_NOT_DONE"; + case IS_ERROR_NO_MSG_IS_RECEIVED: + return "IS_ERROR_NO_MSG_IS_RECEIVED"; + case IS_ERROR_SENSOR_MSG_FAIL: + return "IS_ERROR_SENSOR_MSG_FAIL"; + case IS_ERROR_ISP_MSG_FAIL: + return "IS_ERROR_ISP_MSG_FAIL"; + case IS_ERROR_DRC_MSG_FAIL: + return "IS_ERROR_DRC_MSG_FAIL"; + case IS_ERROR_LHFD_MSG_FAIL: + return "IS_ERROR_LHFD_MSG_FAIL"; + case IS_ERROR_UNKNOWN: + return "IS_ERROR_UNKNOWN"; + + /* Sensor */ + case IS_ERROR_SENSOR_PWRDN_FAIL: + return "IS_ERROR_SENSOR_PWRDN_FAIL"; + + /* ISP */ + case IS_ERROR_ISP_PWRDN_FAIL: + return "IS_ERROR_ISP_PWRDN_FAIL"; + case IS_ERROR_ISP_MULTIPLE_INPUT: + return "IS_ERROR_ISP_MULTIPLE_INPUT"; + case IS_ERROR_ISP_ABSENT_INPUT: + return "IS_ERROR_ISP_ABSENT_INPUT"; + case IS_ERROR_ISP_ABSENT_OUTPUT: + return "IS_ERROR_ISP_ABSENT_OUTPUT"; + case IS_ERROR_ISP_NONADJACENT_OUTPUT: + return "IS_ERROR_ISP_NONADJACENT_OUTPUT"; + case IS_ERROR_ISP_FORMAT_MISMATCH: + return "IS_ERROR_ISP_FORMAT_MISMATCH"; + case IS_ERROR_ISP_WIDTH_MISMATCH: + return "IS_ERROR_ISP_WIDTH_MISMATCH"; + case IS_ERROR_ISP_HEIGHT_MISMATCH: + return "IS_ERROR_ISP_HEIGHT_MISMATCH"; + case IS_ERROR_ISP_BITWIDTH_MISMATCH: + return "IS_ERROR_ISP_BITWIDTH_MISMATCH"; + case IS_ERROR_ISP_FRAME_END_TIME_OUT: + return "IS_ERROR_ISP_FRAME_END_TIME_OUT"; + + /* DRC */ + case IS_ERROR_DRC_PWRDN_FAIL: + return "IS_ERROR_DRC_PWRDN_FAIL"; + case IS_ERROR_DRC_MULTIPLE_INPUT: + return "IS_ERROR_DRC_MULTIPLE_INPUT"; + case IS_ERROR_DRC_ABSENT_INPUT: + return "IS_ERROR_DRC_ABSENT_INPUT"; + case IS_ERROR_DRC_NONADJACENT_INPUT: + return "IS_ERROR_DRC_NONADJACENT_INPUT"; + case IS_ERROR_DRC_ABSENT_OUTPUT: + return "IS_ERROR_DRC_ABSENT_OUTPUT"; + case IS_ERROR_DRC_NONADJACENT_OUTPUT: + return "IS_ERROR_DRC_NONADJACENT_OUTPUT"; + case IS_ERROR_DRC_FORMAT_MISMATCH: + return "IS_ERROR_DRC_FORMAT_MISMATCH"; + case IS_ERROR_DRC_WIDTH_MISMATCH: + return "IS_ERROR_DRC_WIDTH_MISMATCH"; + case IS_ERROR_DRC_HEIGHT_MISMATCH: + return "IS_ERROR_DRC_HEIGHT_MISMATCH"; + case IS_ERROR_DRC_BITWIDTH_MISMATCH: + return "IS_ERROR_DRC_BITWIDTH_MISMATCH"; + case IS_ERROR_DRC_FRAME_END_TIME_OUT: + return "IS_ERROR_DRC_FRAME_END_TIME_OUT"; + + /* FD */ + case IS_ERROR_FD_PWRDN_FAIL: + return "IS_ERROR_FD_PWRDN_FAIL"; + case IS_ERROR_FD_MULTIPLE_INPUT: + return "IS_ERROR_FD_MULTIPLE_INPUT"; + case IS_ERROR_FD_ABSENT_INPUT: + return "IS_ERROR_FD_ABSENT_INPUT"; + case IS_ERROR_FD_NONADJACENT_INPUT: + return "IS_ERROR_FD_NONADJACENT_INPUT"; + case IS_ERROR_LHFD_FRAME_END_TIME_OUT: + return "IS_ERROR_LHFD_FRAME_END_TIME_OUT"; + default: + return "Unknown"; + } +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h new file mode 100644 index 000000000000..3de6f6da6f87 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h @@ -0,0 +1,248 @@ +/* + * Samsung Exynos4 SoC series FIMC-IS slave interface driver + * + * FIMC-IS error code definition + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef FIMC_IS_ERR_H_ +#define FIMC_IS_ERR_H_ + +#define IS_ERROR_VER 011 /* IS ERROR VERSION 0.11 */ + +enum { + IS_ERROR_NONE, + + /* General 1 ~ 99 */ + IS_ERROR_INVALID_COMMAND, + IS_ERROR_REQUEST_FAIL, + IS_ERROR_INVALID_SCENARIO, + IS_ERROR_INVALID_SENSORID, + IS_ERROR_INVALID_MODE_CHANGE, + IS_ERROR_INVALID_MAGIC_NUMBER, + IS_ERROR_INVALID_SETFILE_HDR, + IS_ERROR_BUSY, + IS_ERROR_SET_PARAMETER, + IS_ERROR_INVALID_PATH, + IS_ERROR_OPEN_SENSOR_FAIL, + IS_ERROR_ENTRY_MSG_THREAD_DOWN, + IS_ERROR_ISP_FRAME_END_NOT_DONE, + IS_ERROR_DRC_FRAME_END_NOT_DONE, + IS_ERROR_SCALERC_FRAME_END_NOT_DONE, + IS_ERROR_ODC_FRAME_END_NOT_DONE, + IS_ERROR_DIS_FRAME_END_NOT_DONE, + IS_ERROR_TDNR_FRAME_END_NOT_DONE, + IS_ERROR_SCALERP_FRAME_END_NOT_DONE, + IS_ERROR_WAIT_STREAM_OFF_NOT_DONE, + IS_ERROR_NO_MSG_IS_RECEIVED, + IS_ERROR_SENSOR_MSG_FAIL, + IS_ERROR_ISP_MSG_FAIL, + IS_ERROR_DRC_MSG_FAIL, + IS_ERROR_SCALERC_MSG_FAIL, + IS_ERROR_ODC_MSG_FAIL, + IS_ERROR_DIS_MSG_FAIL, + IS_ERROR_TDNR_MSG_FAIL, + IS_ERROR_SCALERP_MSG_FAIL, + IS_ERROR_LHFD_MSG_FAIL, + IS_ERROR_LHFD_INTERNAL_STOP, + + /* Sensor 100 ~ 199 */ + IS_ERROR_SENSOR_PWRDN_FAIL = 100, + IS_ERROR_SENSOR_STREAM_ON_FAIL, + IS_ERROR_SENSOR_STREAM_OFF_FAIL, + + /* ISP 200 ~ 299 */ + IS_ERROR_ISP_PWRDN_FAIL = 200, + IS_ERROR_ISP_MULTIPLE_INPUT, + IS_ERROR_ISP_ABSENT_INPUT, + IS_ERROR_ISP_ABSENT_OUTPUT, + IS_ERROR_ISP_NONADJACENT_OUTPUT, + IS_ERROR_ISP_FORMAT_MISMATCH, + IS_ERROR_ISP_WIDTH_MISMATCH, + IS_ERROR_ISP_HEIGHT_MISMATCH, + IS_ERROR_ISP_BITWIDTH_MISMATCH, + IS_ERROR_ISP_FRAME_END_TIME_OUT, + + /* DRC 300 ~ 399 */ + IS_ERROR_DRC_PWRDN_FAIL = 300, + IS_ERROR_DRC_MULTIPLE_INPUT, + IS_ERROR_DRC_ABSENT_INPUT, + IS_ERROR_DRC_NONADJACENT_INPUT, + IS_ERROR_DRC_ABSENT_OUTPUT, + IS_ERROR_DRC_NONADJACENT_OUTPUT, + IS_ERROR_DRC_FORMAT_MISMATCH, + IS_ERROR_DRC_WIDTH_MISMATCH, + IS_ERROR_DRC_HEIGHT_MISMATCH, + IS_ERROR_DRC_BITWIDTH_MISMATCH, + IS_ERROR_DRC_FRAME_END_TIME_OUT, + + /* SCALERC 400 ~ 499 */ + IS_ERROR_SCALERC_PWRDN_FAIL = 400, + + /* ODC 500 ~ 599 */ + IS_ERROR_ODC_PWRDN_FAIL = 500, + + /* DIS 600 ~ 699 */ + IS_ERROR_DIS_PWRDN_FAIL = 600, + + /* TDNR 700 ~ 799 */ + IS_ERROR_TDNR_PWRDN_FAIL = 700, + + /* SCALERC 800 ~ 899 */ + IS_ERROR_SCALERP_PWRDN_FAIL = 800, + + /* FD 900 ~ 999 */ + IS_ERROR_FD_PWRDN_FAIL = 900, + IS_ERROR_FD_MULTIPLE_INPUT, + IS_ERROR_FD_ABSENT_INPUT, + IS_ERROR_FD_NONADJACENT_INPUT, + IS_ERROR_LHFD_FRAME_END_TIME_OUT, + + IS_ERROR_UNKNOWN = 1000, +}; + +#define IS_ERROR_TIME_OUT_FLAG 0x80000000 + +/* Set parameter error enum */ +enum fimc_is_error { + /* Common error (0~99) */ + ERROR_COMMON_NONE = 0, + ERROR_COMMON_CMD = 1, /* Invalid command */ + ERROR_COMMON_PARAMETER = 2, /* Invalid parameter */ + /* setfile is not loaded before adjusting */ + ERROR_COMMON_SETFILE_LOAD = 3, + /* setfile is not Adjusted before runnng. */ + ERROR_COMMON_SETFILE_ADJUST = 4, + /* Index of setfile is not valid (0~MAX_SETFILE_NUM-1) */ + ERROR_COMMON_SETFILE_INDEX = 5, + /* Input path can be changed in ready state(stop) */ + ERROR_COMMON_INPUT_PATH = 6, + /* IP can not start if input path is not set */ + ERROR_COMMON_INPUT_INIT = 7, + /* Output path can be changed in ready state (stop) */ + ERROR_COMMON_OUTPUT_PATH = 8, + /* IP can not start if output path is not set */ + ERROR_COMMON_OUTPUT_INIT = 9, + + ERROR_CONTROL_NONE = ERROR_COMMON_NONE, + ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */ + + ERROR_OTF_INPUT_NONE = ERROR_COMMON_NONE, + ERROR_OTF_INPUT_CMD = 21, + /* invalid format (DRC: YUV444, FD: YUV444, 422, 420) */ + ERROR_OTF_INPUT_FORMAT = 22, + /* invalid width (DRC: 128~8192, FD: 32~8190) */ + ERROR_OTF_INPUT_WIDTH = 23, + /* invalid height (DRC: 64~8192, FD: 16~8190) */ + ERROR_OTF_INPUT_HEIGHT = 24, + /* invalid bit-width (DRC: 8~12bits, FD: 8bit) */ + ERROR_OTF_INPUT_BIT_WIDTH = 25, + /* invalid FrameTime for ISP */ + ERROR_OTF_INPUT_USER_FRAMETIIME = 26, + + ERROR_DMA_INPUT_NONE = ERROR_COMMON_NONE, + /* invalid width (DRC: 128~8192, FD: 32~8190) */ + ERROR_DMA_INPUT_WIDTH = 31, + /* invalid height (DRC: 64~8192, FD: 16~8190) */ + ERROR_DMA_INPUT_HEIGHT = 32, + /* invalid format (DRC: YUV444 or YUV422, FD: YUV444, 422, 420) */ + ERROR_DMA_INPUT_FORMAT = 33, + /* invalid bit-width (DRC: 8~12bit, FD: 8bit) */ + ERROR_DMA_INPUT_BIT_WIDTH = 34, + /* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */ + ERROR_DMA_INPUT_ORDER = 35, + /* invalid palne (DRC: 3, FD: 1, 2, 3) */ + ERROR_DMA_INPUT_PLANE = 36, + + ERROR_OTF_OUTPUT_NONE = ERROR_COMMON_NONE, + /* invalid width (DRC: 128~8192) */ + ERROR_OTF_OUTPUT_WIDTH = 41, + /* invalid height (DRC: 64~8192) */ + ERROR_OTF_OUTPUT_HEIGHT = 42, + /* invalid format (DRC: YUV444) */ + ERROR_OTF_OUTPUT_FORMAT = 43, + /* invalid bit-width (DRC: 8~12bits) */ + ERROR_OTF_OUTPUT_BIT_WIDTH = 44, + + ERROR_DMA_OUTPUT_NONE = ERROR_COMMON_NONE, + ERROR_DMA_OUTPUT_WIDTH = 51, /* invalid width */ + ERROR_DMA_OUTPUT_HEIGHT = 52, /* invalid height */ + ERROR_DMA_OUTPUT_FORMAT = 53, /* invalid format */ + ERROR_DMA_OUTPUT_BIT_WIDTH = 54, /* invalid bit-width */ + ERROR_DMA_OUTPUT_PLANE = 55, /* invalid plane */ + ERROR_DMA_OUTPUT_ORDER = 56, /* invalid order */ + + ERROR_GLOBAL_SHOTMODE_NONE = ERROR_COMMON_NONE, + + /* SENSOR Error(100~199) */ + ERROR_SENSOR_NONE = ERROR_COMMON_NONE, + ERROR_SENSOR_I2C_FAIL = 101, + ERROR_SENSOR_INVALID_FRAMERATE, + ERROR_SENSOR_INVALID_EXPOSURETIME, + ERROR_SENSOR_INVALID_SIZE, + ERROR_SENSOR_INVALID_SETTING, + ERROR_SENSOR_ACTURATOR_INIT_FAIL, + ERROR_SENSOR_INVALID_AF_POS, + ERROR_SENSOR_UNSUPPORT_FUNC, + ERROR_SENSOR_UNSUPPORT_PERI, + ERROR_SENSOR_UNSUPPORT_AF, + + /* ISP Error (200~299) */ + ERROR_ISP_AF_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AF_BUSY = 201, + ERROR_ISP_AF_INVALID_COMMAND = 202, + ERROR_ISP_AF_INVALID_MODE = 203, + ERROR_ISP_FLASH_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AWB_NONE = ERROR_COMMON_NONE, + ERROR_ISP_IMAGE_EFFECT_NONE = ERROR_COMMON_NONE, + ERROR_ISP_ISO_NONE = ERROR_COMMON_NONE, + ERROR_ISP_ADJUST_NONE = ERROR_COMMON_NONE, + ERROR_ISP_METERING_NONE = ERROR_COMMON_NONE, + ERROR_ISP_AFC_NONE = ERROR_COMMON_NONE, + + /* DRC Error (300~399) */ + + /* FD Error (400~499) */ + ERROR_FD_NONE = ERROR_COMMON_NONE, + /* Invalid max number (1~16) */ + ERROR_FD_CONFIG_MAX_NUMBER_STATE = 401, + ERROR_FD_CONFIG_MAX_NUMBER_INVALID = 402, + ERROR_FD_CONFIG_YAW_ANGLE_STATE = 403, + ERROR_FD_CONFIG_YAW_ANGLE_INVALID = 404, + ERROR_FD_CONFIG_ROLL_ANGLE_STATE = 405, + ERROR_FD_CONFIG_ROLL_ANGLE_INVALID = 406, + ERROR_FD_CONFIG_SMILE_MODE_INVALID = 407, + ERROR_FD_CONFIG_BLINK_MODE_INVALID = 408, + ERROR_FD_CONFIG_EYES_DETECT_INVALID = 409, + ERROR_FD_CONFIG_MOUTH_DETECT_INVALID = 410, + ERROR_FD_CONFIG_ORIENTATION_STATE = 411, + ERROR_FD_CONFIG_ORIENTATION_INVALID = 412, + ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID = 413, + /* PARAM_FdResultStr can be only applied in ready-state or stream off */ + ERROR_FD_RESULT = 414, + /* PARAM_FdModeStr can be only applied in ready-state or stream off */ + ERROR_FD_MODE = 415, + /* Scaler Error (500 ~ 599) */ + ERROR_SCALER_NO_NONE = ERROR_COMMON_NONE, + ERROR_SCALER_DMA_OUTSEL = 501, + ERROR_SCALER_H_RATIO = 502, + ERROR_SCALER_V_RATIO = 503, + + ERROR_SCALER_IMAGE_EFFECT = 510, + + ERROR_SCALER_ROTATE = 520, + ERROR_SCALER_FLIP = 521, +}; + +const char * const fimc_is_strerr(unsigned int error); +const char * const fimc_is_param_strerr(unsigned int error); + +#endif /* FIMC_IS_ERR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c new file mode 100644 index 000000000000..c397777d7cbb --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -0,0 +1,126 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_i2c.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include "fimc-is-i2c.h" + +struct fimc_is_i2c { + struct i2c_adapter adapter; + struct clk *clock; +}; + +/* + * An empty algorithm is used as the actual I2C bus controller driver + * is implemented in the FIMC-IS subsystem firmware and the host CPU + * doesn't access the I2C bus controller. + */ +static const struct i2c_algorithm fimc_is_i2c_algorithm; + +static int fimc_is_i2c_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct fimc_is_i2c *isp_i2c; + struct i2c_adapter *i2c_adap; + int ret; + + isp_i2c = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c), GFP_KERNEL); + if (!isp_i2c) + return -ENOMEM; + + isp_i2c->clock = devm_clk_get(&pdev->dev, "i2c_isp"); + if (IS_ERR(isp_i2c->clock)) { + dev_err(&pdev->dev, "failed to get the clock\n"); + return PTR_ERR(isp_i2c->clock); + } + + i2c_adap = &isp_i2c->adapter; + i2c_adap->dev.of_node = node; + i2c_adap->dev.parent = &pdev->dev; + strlcpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name)); + i2c_adap->owner = THIS_MODULE; + i2c_adap->algo = &fimc_is_i2c_algorithm; + i2c_adap->class = I2C_CLASS_SPD; + + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add I2C bus %s\n", + node->full_name); + return ret; + } + + platform_set_drvdata(pdev, isp_i2c); + + pm_runtime_enable(&pdev->dev); + pm_runtime_enable(&i2c_adap->dev); + + of_i2c_register_devices(i2c_adap); + + return 0; +} + +static int fimc_is_i2c_remove(struct platform_device *pdev) +{ + struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev); + + pm_runtime_disable(&isp_i2c->adapter.dev); + pm_runtime_disable(&pdev->dev); + i2c_del_adapter(&isp_i2c->adapter); + + return 0; +} + +static int fimc_is_i2c_suspend(struct device *dev) +{ + struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + clk_disable_unprepare(isp_i2c->clock); + return 0; +} + +static int fimc_is_i2c_resume(struct device *dev) +{ + struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + return clk_prepare_enable(isp_i2c->clock); +} + +UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, + fimc_is_i2c_resume, NULL); + +static const struct of_device_id fimc_is_i2c_of_match[] = { + { .compatible = FIMC_IS_I2C_COMPATIBLE }, + { }, +}; + +static struct platform_driver fimc_is_i2c_driver = { + .probe = fimc_is_i2c_probe, + .remove = fimc_is_i2c_remove, + .driver = { + .of_match_table = fimc_is_i2c_of_match, + .name = "fimc-isp-i2c", + .owner = THIS_MODULE, + .pm = &fimc_is_i2c_pm_ops, + } +}; + +int fimc_is_register_i2c_driver(void) +{ + return platform_driver_register(&fimc_is_i2c_driver); +} + +void fimc_is_unregister_i2c_driver(void) +{ + platform_driver_unregister(&fimc_is_i2c_driver); +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/exynos4-is/fimc-is-i2c.h new file mode 100644 index 000000000000..0d38d6bb963b --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.h @@ -0,0 +1,15 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp" + +int fimc_is_register_i2c_driver(void); +void fimc_is_unregister_i2c_driver(void); diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c new file mode 100644 index 000000000000..53fe2a2b4db3 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -0,0 +1,900 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-errno.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" +#include "fimc-is-sensor.h" + +static void __hw_param_copy(void *dst, void *src) +{ + memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE); +} + +void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) +{ + struct param_global_shotmode *dst, *src; + + dst = &is->is_p_region->parameter.global.shotmode; + src = &is->config[is->config_index].global.shotmode; + __hw_param_copy(dst, src); +} + +void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) +{ + struct param_sensor_framerate *dst, *src; + + dst = &is->is_p_region->parameter.sensor.frame_rate; + src = &is->config[is->config_index].sensor.frame_rate; + __hw_param_copy(dst, src); +} + +int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) +{ + struct is_param_region *par = &is->is_p_region->parameter; + struct chain_config *cfg = &is->config[is->config_index]; + + switch (offset) { + case PARAM_ISP_CONTROL: + __hw_param_copy(&par->isp.control, &cfg->isp.control); + break; + + case PARAM_ISP_OTF_INPUT: + __hw_param_copy(&par->isp.otf_input, &cfg->isp.otf_input); + break; + + case PARAM_ISP_DMA1_INPUT: + __hw_param_copy(&par->isp.dma1_input, &cfg->isp.dma1_input); + break; + + case PARAM_ISP_DMA2_INPUT: + __hw_param_copy(&par->isp.dma2_input, &cfg->isp.dma2_input); + break; + + case PARAM_ISP_AA: + __hw_param_copy(&par->isp.aa, &cfg->isp.aa); + break; + + case PARAM_ISP_FLASH: + __hw_param_copy(&par->isp.flash, &cfg->isp.flash); + break; + + case PARAM_ISP_AWB: + __hw_param_copy(&par->isp.awb, &cfg->isp.awb); + break; + + case PARAM_ISP_IMAGE_EFFECT: + __hw_param_copy(&par->isp.effect, &cfg->isp.effect); + break; + + case PARAM_ISP_ISO: + __hw_param_copy(&par->isp.iso, &cfg->isp.iso); + break; + + case PARAM_ISP_ADJUST: + __hw_param_copy(&par->isp.adjust, &cfg->isp.adjust); + break; + + case PARAM_ISP_METERING: + __hw_param_copy(&par->isp.metering, &cfg->isp.metering); + break; + + case PARAM_ISP_AFC: + __hw_param_copy(&par->isp.afc, &cfg->isp.afc); + break; + + case PARAM_ISP_OTF_OUTPUT: + __hw_param_copy(&par->isp.otf_output, &cfg->isp.otf_output); + break; + + case PARAM_ISP_DMA1_OUTPUT: + __hw_param_copy(&par->isp.dma1_output, &cfg->isp.dma1_output); + break; + + case PARAM_ISP_DMA2_OUTPUT: + __hw_param_copy(&par->isp.dma2_output, &cfg->isp.dma2_output); + break; + + case PARAM_DRC_CONTROL: + __hw_param_copy(&par->drc.control, &cfg->drc.control); + break; + + case PARAM_DRC_OTF_INPUT: + __hw_param_copy(&par->drc.otf_input, &cfg->drc.otf_input); + break; + + case PARAM_DRC_DMA_INPUT: + __hw_param_copy(&par->drc.dma_input, &cfg->drc.dma_input); + break; + + case PARAM_DRC_OTF_OUTPUT: + __hw_param_copy(&par->drc.otf_output, &cfg->drc.otf_output); + break; + + case PARAM_FD_CONTROL: + __hw_param_copy(&par->fd.control, &cfg->fd.control); + break; + + case PARAM_FD_OTF_INPUT: + __hw_param_copy(&par->fd.otf_input, &cfg->fd.otf_input); + break; + + case PARAM_FD_DMA_INPUT: + __hw_param_copy(&par->fd.dma_input, &cfg->fd.dma_input); + break; + + case PARAM_FD_CONFIG: + __hw_param_copy(&par->fd.config, &cfg->fd.config); + break; + + default: + return -EINVAL; + } + + return 0; +} + +unsigned int __get_pending_param_count(struct fimc_is *is) +{ + struct chain_config *config = &is->config[is->config_index]; + unsigned long flags; + unsigned int count; + + spin_lock_irqsave(&is->slock, flags); + count = hweight32(config->p_region_index1); + count += hweight32(config->p_region_index2); + spin_unlock_irqrestore(&is->slock, flags); + + return count; +} + +int __is_hw_update_params(struct fimc_is *is) +{ + unsigned long *p_index1, *p_index2; + int i, id, ret = 0; + + id = is->config_index; + p_index1 = &is->config[id].p_region_index1; + p_index2 = &is->config[id].p_region_index2; + + if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index1)) + __fimc_is_hw_update_param_global_shotmode(is); + + if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) + __fimc_is_hw_update_param_sensor_framerate(is); + + for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) { + if (test_bit(i, p_index1)) + ret = __fimc_is_hw_update_param(is, i); + } + + for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) { + if (test_bit(i, p_index1)) + ret = __fimc_is_hw_update_param(is, i); + } + + for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) { + if (test_bit((i - 32), p_index2)) + ret = __fimc_is_hw_update_param(is, i); + } + + return ret; +} + +void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) +{ + struct isp_param *isp; + + isp = &is->config[is->config_index].isp; + mf->width = isp->otf_input.width; + mf->height = isp->otf_input.height; +} + +void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + struct drc_param *drc; + struct fd_param *fd; + + isp = &is->config[index].isp; + drc = &is->config[index].drc; + fd = &is->config[index].fd; + + /* Update isp size info (OTF only) */ + isp->otf_input.width = mf->width; + isp->otf_input.height = mf->height; + isp->otf_output.width = mf->width; + isp->otf_output.height = mf->height; + /* Update drc size info (OTF only) */ + drc->otf_input.width = mf->width; + drc->otf_input.height = mf->height; + drc->otf_output.width = mf->width; + drc->otf_output.height = mf->height; + /* Update fd size info (OTF only) */ + fd->otf_input.width = mf->width; + fd->otf_input.height = mf->height; + + if (test_bit(PARAM_ISP_OTF_INPUT, + &is->config[index].p_region_index1)) + return; + + /* Update field */ + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); + fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); + fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); + fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); + fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); +} + +int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is) +{ + switch (is->sensor->drvdata->id) { + case FIMC_IS_SENSOR_ID_S5K6A3: + return 30; + default: + return 15; + } +} + +void __is_set_sensor(struct fimc_is *is, int fps) +{ + unsigned int index = is->config_index; + struct sensor_param *sensor; + struct isp_param *isp; + + sensor = &is->config[index].sensor; + isp = &is->config[index].isp; + + if (fps == 0) { + sensor->frame_rate.frame_rate = + fimc_is_hw_get_sensor_max_framerate(is); + isp->otf_input.frametime_min = 0; + isp->otf_input.frametime_max = 66666; + } else { + sensor->frame_rate.frame_rate = fps; + isp->otf_input.frametime_min = 0; + isp->otf_input.frametime_max = (u32)1000000 / fps; + } + + fimc_is_set_param_bit(is, PARAM_SENSOR_FRAME_RATE); + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); +} + +void __is_set_init_isp_aa(struct fimc_is *is) +{ + struct isp_param *isp; + + isp = &is->config[is->config_index].isp; + + isp->aa.cmd = ISP_AA_COMMAND_START; + isp->aa.target = ISP_AA_TARGET_AF | ISP_AA_TARGET_AE | + ISP_AA_TARGET_AWB; + isp->aa.mode = 0; + isp->aa.scene = 0; + isp->aa.sleep = 0; + isp->aa.face = 0; + isp->aa.touch_x = 0; + isp->aa.touch_y = 0; + isp->aa.manual_af_setting = 0; + isp->aa.err = ISP_AF_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AA); +} + +void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye) +{ + unsigned int index = is->config_index; + struct isp_param *isp = &is->config[index].isp; + + isp->flash.cmd = cmd; + isp->flash.redeye = redeye; + isp->flash.err = ISP_FLASH_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_FLASH); +} + +void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->awb.cmd = cmd; + isp->awb.illumination = val; + isp->awb.err = ISP_AWB_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AWB); +} + +void __is_set_isp_effect(struct fimc_is *is, u32 cmd) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->effect.cmd = cmd; + isp->effect.err = ISP_IMAGE_EFFECT_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_IMAGE_EFFECT); +} + +void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->iso.cmd = cmd; + isp->iso.value = val; + isp->iso.err = ISP_ISO_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_ISO); +} + +void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + unsigned long *p_index; + struct isp_param *isp; + + p_index = &is->config[index].p_region_index1; + isp = &is->config[index].isp; + + switch (cmd) { + case ISP_ADJUST_COMMAND_MANUAL_CONTRAST: + isp->adjust.contrast = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_SATURATION: + isp->adjust.saturation = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_SHARPNESS: + isp->adjust.sharpness = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_EXPOSURE: + isp->adjust.exposure = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS: + isp->adjust.brightness = val; + break; + case ISP_ADJUST_COMMAND_MANUAL_HUE: + isp->adjust.hue = val; + break; + case ISP_ADJUST_COMMAND_AUTO: + isp->adjust.contrast = 0; + isp->adjust.saturation = 0; + isp->adjust.sharpness = 0; + isp->adjust.exposure = 0; + isp->adjust.brightness = 0; + isp->adjust.hue = 0; + break; + } + + if (!test_bit(PARAM_ISP_ADJUST, p_index)) { + isp->adjust.cmd = cmd; + isp->adjust.err = ISP_ADJUST_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_ADJUST); + } else { + isp->adjust.cmd |= cmd; + } +} + +void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index1; + isp = &is->config[index].isp; + + switch (id) { + case IS_METERING_CONFIG_CMD: + isp->metering.cmd = val; + break; + case IS_METERING_CONFIG_WIN_POS_X: + isp->metering.win_pos_x = val; + break; + case IS_METERING_CONFIG_WIN_POS_Y: + isp->metering.win_pos_y = val; + break; + case IS_METERING_CONFIG_WIN_WIDTH: + isp->metering.win_width = val; + break; + case IS_METERING_CONFIG_WIN_HEIGHT: + isp->metering.win_height = val; + break; + default: + return; + } + + if (!test_bit(PARAM_ISP_METERING, p_index)) { + isp->metering.err = ISP_METERING_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_METERING); + } +} + +void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val) +{ + unsigned int index = is->config_index; + struct isp_param *isp; + + isp = &is->config[index].isp; + + isp->afc.cmd = cmd; + isp->afc.manual = val; + isp->afc.err = ISP_AFC_ERROR_NONE; + + fimc_is_set_param_bit(is, PARAM_ISP_AFC); +} + +void __is_set_drc_control(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct drc_param *drc; + + drc = &is->config[index].drc; + + drc->control.bypass = val; + + fimc_is_set_param_bit(is, PARAM_DRC_CONTROL); +} + +void __is_set_fd_control(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->control.cmd = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) + fimc_is_set_param_bit(is, PARAM_FD_CONTROL); +} + +void __is_set_fd_config_maxface(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.max_number = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_MAXIMUM_NUMBER; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_MAXIMUM_NUMBER; + } +} + +void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.roll_angle = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ROLL_ANGLE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ROLL_ANGLE; + } +} + +void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.yaw_angle = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_YAW_ANGLE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_YAW_ANGLE; + } +} + +void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.smile_mode = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_SMILE_MODE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_SMILE_MODE; + } +} + +void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.blink_mode = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_BLINK_MODE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_BLINK_MODE; + } +} + +void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.eye_detect = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_EYES_DETECT; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_EYES_DETECT; + } +} + +void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.mouth_detect = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_MOUTH_DETECT; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_MOUTH_DETECT; + } +} + +void __is_set_fd_config_orientation(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.orientation = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION; + } +} + +void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val) +{ + unsigned int index = is->config_index; + struct fd_param *fd; + unsigned long *p_index; + + p_index = &is->config[index].p_region_index2; + fd = &is->config[index].fd; + + fd->config.orientation_value = val; + + if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { + fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION_VALUE; + fd->config.err = ERROR_FD_NONE; + fimc_is_set_param_bit(is, PARAM_FD_CONFIG); + } else { + fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION_VALUE; + } +} + +void fimc_is_set_initial_params(struct fimc_is *is) +{ + struct global_param *global; + struct sensor_param *sensor; + struct isp_param *isp; + struct drc_param *drc; + struct fd_param *fd; + unsigned long *p_index1, *p_index2; + unsigned int index; + + index = is->config_index; + global = &is->config[index].global; + sensor = &is->config[index].sensor; + isp = &is->config[index].isp; + drc = &is->config[index].drc; + fd = &is->config[index].fd; + p_index1 = &is->config[index].p_region_index1; + p_index2 = &is->config[index].p_region_index2; + + /* Global */ + global->shotmode.cmd = 1; + fimc_is_set_param_bit(is, PARAM_GLOBAL_SHOTMODE); + + /* ISP */ + isp->control.cmd = CONTROL_COMMAND_START; + isp->control.bypass = CONTROL_BYPASS_DISABLE; + isp->control.err = CONTROL_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_CONTROL); + + isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_ISP_OTF_INPUT, p_index1)) { + isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); + } + if (is->sensor->test_pattern) + isp->otf_input.format = OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER; + else + isp->otf_input.format = OTF_INPUT_FORMAT_BAYER; + isp->otf_input.bitwidth = 10; + isp->otf_input.order = OTF_INPUT_ORDER_BAYER_GR_BG; + isp->otf_input.crop_offset_x = 0; + isp->otf_input.crop_offset_y = 0; + isp->otf_input.err = OTF_INPUT_ERROR_NONE; + + isp->dma1_input.cmd = DMA_INPUT_COMMAND_DISABLE; + isp->dma1_input.width = 0; + isp->dma1_input.height = 0; + isp->dma1_input.format = 0; + isp->dma1_input.bitwidth = 0; + isp->dma1_input.plane = 0; + isp->dma1_input.order = 0; + isp->dma1_input.buffer_number = 0; + isp->dma1_input.width = 0; + isp->dma1_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA1_INPUT); + + isp->dma2_input.cmd = DMA_INPUT_COMMAND_DISABLE; + isp->dma2_input.width = 0; + isp->dma2_input.height = 0; + isp->dma2_input.format = 0; + isp->dma2_input.bitwidth = 0; + isp->dma2_input.plane = 0; + isp->dma2_input.order = 0; + isp->dma2_input.buffer_number = 0; + isp->dma2_input.width = 0; + isp->dma2_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA2_INPUT); + + isp->aa.cmd = ISP_AA_COMMAND_START; + isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + + if (!test_bit(PARAM_ISP_FLASH, p_index1)) + __is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE, + ISP_FLASH_REDEYE_DISABLE); + + if (!test_bit(PARAM_ISP_AWB, p_index1)) + __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); + + if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index1)) + __is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE); + + if (!test_bit(PARAM_ISP_ISO, p_index1)) + __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); + + if (!test_bit(PARAM_ISP_ADJUST, p_index1)) { + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0); + __is_set_isp_adjust(is, + ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, 0); + __is_set_isp_adjust(is, + ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, 0); + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0); + } + + if (!test_bit(PARAM_ISP_METERING, p_index1)) { + __is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER); + __is_set_isp_metering(is, 1, 0); + __is_set_isp_metering(is, 2, 0); + __is_set_isp_metering(is, 3, 0); + __is_set_isp_metering(is, 4, 0); + } + + if (!test_bit(PARAM_ISP_AFC, p_index1)) + __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); + + isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index1)) { + isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; + isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); + } + isp->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; + isp->otf_output.bitwidth = 12; + isp->otf_output.order = 0; + isp->otf_output.err = OTF_OUTPUT_ERROR_NONE; + + if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index1)) { + isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + isp->dma1_output.width = 0; + isp->dma1_output.height = 0; + isp->dma1_output.format = 0; + isp->dma1_output.bitwidth = 0; + isp->dma1_output.plane = 0; + isp->dma1_output.order = 0; + isp->dma1_output.buffer_number = 0; + isp->dma1_output.buffer_address = 0; + isp->dma1_output.notify_dma_done = 0; + isp->dma1_output.dma_out_mask = 0; + isp->dma1_output.err = DMA_OUTPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT); + } + + if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index1)) { + isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + isp->dma2_output.width = 0; + isp->dma2_output.height = 0; + isp->dma2_output.format = 0; + isp->dma2_output.bitwidth = 0; + isp->dma2_output.plane = 0; + isp->dma2_output.order = 0; + isp->dma2_output.buffer_number = 0; + isp->dma2_output.buffer_address = 0; + isp->dma2_output.notify_dma_done = 0; + isp->dma2_output.dma_out_mask = 0; + isp->dma2_output.err = DMA_OUTPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); + } + + /* Sensor */ + if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) { + if (is->config_index == 0) + __is_set_sensor(is, 0); + } + + /* DRC */ + drc->control.cmd = CONTROL_COMMAND_START; + __is_set_drc_control(is, CONTROL_BYPASS_ENABLE); + + drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_DRC_OTF_INPUT, p_index1)) { + drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); + } + drc->otf_input.format = OTF_INPUT_FORMAT_YUV444; + drc->otf_input.bitwidth = 12; + drc->otf_input.order = 0; + drc->otf_input.err = OTF_INPUT_ERROR_NONE; + + drc->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; + drc->dma_input.width = 0; + drc->dma_input.height = 0; + drc->dma_input.format = 0; + drc->dma_input.bitwidth = 0; + drc->dma_input.plane = 0; + drc->dma_input.order = 0; + drc->dma_input.buffer_number = 0; + drc->dma_input.width = 0; + drc->dma_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT); + + drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index1)) { + drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; + drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); + } + drc->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; + drc->otf_output.bitwidth = 8; + drc->otf_output.order = 0; + drc->otf_output.err = OTF_OUTPUT_ERROR_NONE; + + /* FD */ + __is_set_fd_control(is, CONTROL_COMMAND_STOP); + fd->control.bypass = CONTROL_BYPASS_DISABLE; + + fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; + if (!test_bit((PARAM_FD_OTF_INPUT - 32), p_index2)) { + fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; + fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; + fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); + } + + fd->otf_input.format = OTF_INPUT_FORMAT_YUV444; + fd->otf_input.bitwidth = 8; + fd->otf_input.order = 0; + fd->otf_input.err = OTF_INPUT_ERROR_NONE; + + fd->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; + fd->dma_input.width = 0; + fd->dma_input.height = 0; + fd->dma_input.format = 0; + fd->dma_input.bitwidth = 0; + fd->dma_input.plane = 0; + fd->dma_input.order = 0; + fd->dma_input.buffer_number = 0; + fd->dma_input.width = 0; + fd->dma_input.err = DMA_INPUT_ERROR_NONE; + fimc_is_set_param_bit(is, PARAM_FD_DMA_INPUT); + + __is_set_fd_config_maxface(is, 5); + __is_set_fd_config_rollangle(is, FD_CONFIG_ROLL_ANGLE_FULL); + __is_set_fd_config_yawangle(is, FD_CONFIG_YAW_ANGLE_45_90); + __is_set_fd_config_smilemode(is, FD_CONFIG_SMILE_MODE_DISABLE); + __is_set_fd_config_blinkmode(is, FD_CONFIG_BLINK_MODE_DISABLE); + __is_set_fd_config_eyedetect(is, FD_CONFIG_EYES_DETECT_ENABLE); + __is_set_fd_config_mouthdetect(is, FD_CONFIG_MOUTH_DETECT_DISABLE); + __is_set_fd_config_orientation(is, FD_CONFIG_ORIENTATION_DISABLE); + __is_set_fd_config_orientation_val(is, 0); +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h new file mode 100644 index 000000000000..f9358c27ae2d --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.h @@ -0,0 +1,1020 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_IS_PARAM_H_ +#define FIMC_IS_PARAM_H_ + +#include <linux/compiler.h> + +#define FIMC_IS_CONFIG_TIMEOUT 3000 /* ms */ +#define IS_DEFAULT_WIDTH 1280 +#define IS_DEFAULT_HEIGHT 720 + +#define DEFAULT_PREVIEW_STILL_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_PREVIEW_STILL_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_CAPTURE_STILL_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_CAPTURE_STILL_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_PREVIEW_VIDEO_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_PREVIEW_VIDEO_HEIGHT IS_DEFAULT_HEIGHT +#define DEFAULT_CAPTURE_VIDEO_WIDTH IS_DEFAULT_WIDTH +#define DEFAULT_CAPTURE_VIDEO_HEIGHT IS_DEFAULT_HEIGHT + +#define DEFAULT_PREVIEW_STILL_FRAMERATE 30 +#define DEFAULT_CAPTURE_STILL_FRAMERATE 15 +#define DEFAULT_PREVIEW_VIDEO_FRAMERATE 30 +#define DEFAULT_CAPTURE_VIDEO_FRAMERATE 30 + +#define FIMC_IS_REGION_VER 124 /* IS REGION VERSION 1.24 */ +#define FIMC_IS_PARAM_SIZE (FIMC_IS_REGION_SIZE + 1) +#define FIMC_IS_MAGIC_NUMBER 0x01020304 +#define FIMC_IS_PARAM_MAX_SIZE 64 /* in bytes */ +#define FIMC_IS_PARAM_MAX_ENTRIES (FIMC_IS_PARAM_MAX_SIZE / 4) + +/* The parameter bitmask bit definitions. */ +enum is_param_bit { + PARAM_GLOBAL_SHOTMODE, + PARAM_SENSOR_CONTROL, + PARAM_SENSOR_OTF_OUTPUT, + PARAM_SENSOR_FRAME_RATE, + PARAM_BUFFER_CONTROL, + PARAM_BUFFER_OTF_INPUT, + PARAM_BUFFER_OTF_OUTPUT, + PARAM_ISP_CONTROL, + PARAM_ISP_OTF_INPUT, + PARAM_ISP_DMA1_INPUT, + /* 10 */ + PARAM_ISP_DMA2_INPUT, + PARAM_ISP_AA, + PARAM_ISP_FLASH, + PARAM_ISP_AWB, + PARAM_ISP_IMAGE_EFFECT, + PARAM_ISP_ISO, + PARAM_ISP_ADJUST, + PARAM_ISP_METERING, + PARAM_ISP_AFC, + PARAM_ISP_OTF_OUTPUT, + /* 20 */ + PARAM_ISP_DMA1_OUTPUT, + PARAM_ISP_DMA2_OUTPUT, + PARAM_DRC_CONTROL, + PARAM_DRC_OTF_INPUT, + PARAM_DRC_DMA_INPUT, + PARAM_DRC_OTF_OUTPUT, + PARAM_SCALERC_CONTROL, + PARAM_SCALERC_OTF_INPUT, + PARAM_SCALERC_IMAGE_EFFECT, + PARAM_SCALERC_INPUT_CROP, + /* 30 */ + PARAM_SCALERC_OUTPUT_CROP, + PARAM_SCALERC_OTF_OUTPUT, + PARAM_SCALERC_DMA_OUTPUT, + PARAM_ODC_CONTROL, + PARAM_ODC_OTF_INPUT, + PARAM_ODC_OTF_OUTPUT, + PARAM_DIS_CONTROL, + PARAM_DIS_OTF_INPUT, + PARAM_DIS_OTF_OUTPUT, + PARAM_TDNR_CONTROL, + /* 40 */ + PARAM_TDNR_OTF_INPUT, + PARAM_TDNR_1ST_FRAME, + PARAM_TDNR_OTF_OUTPUT, + PARAM_TDNR_DMA_OUTPUT, + PARAM_SCALERP_CONTROL, + PARAM_SCALERP_OTF_INPUT, + PARAM_SCALERP_IMAGE_EFFECT, + PARAM_SCALERP_INPUT_CROP, + PARAM_SCALERP_OUTPUT_CROP, + PARAM_SCALERP_ROTATION, + /* 50 */ + PARAM_SCALERP_FLIP, + PARAM_SCALERP_OTF_OUTPUT, + PARAM_SCALERP_DMA_OUTPUT, + PARAM_FD_CONTROL, + PARAM_FD_OTF_INPUT, + PARAM_FD_DMA_INPUT, + PARAM_FD_CONFIG, +}; + +/* Interrupt map */ +#define FIMC_IS_INT_GENERAL 0 +#define FIMC_IS_INT_FRAME_DONE_ISP 1 + +/* Input */ + +#define CONTROL_COMMAND_STOP 0 +#define CONTROL_COMMAND_START 1 + +#define CONTROL_BYPASS_DISABLE 0 +#define CONTROL_BYPASS_ENABLE 1 + +#define CONTROL_ERROR_NONE 0 + +/* OTF (On-The-Fly) input interface commands */ +#define OTF_INPUT_COMMAND_DISABLE 0 +#define OTF_INPUT_COMMAND_ENABLE 1 + +/* OTF input interface color formats */ +enum oft_input_fmt { + OTF_INPUT_FORMAT_BAYER = 0, /* 1 channel */ + OTF_INPUT_FORMAT_YUV444 = 1, /* 3 channels */ + OTF_INPUT_FORMAT_YUV422 = 2, /* 3 channels */ + OTF_INPUT_FORMAT_YUV420 = 3, /* 3 channels */ + OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER = 10, + OTF_INPUT_FORMAT_BAYER_DMA = 11, +}; + +#define OTF_INPUT_ORDER_BAYER_GR_BG 0 + +/* OTF input error codes */ +#define OTF_INPUT_ERROR_NONE 0 /* Input setting is done */ + +/* DMA input commands */ +#define DMA_INPUT_COMMAND_DISABLE 0 +#define DMA_INPUT_COMMAND_ENABLE 1 + +/* DMA input color formats */ +enum dma_input_fmt { + DMA_INPUT_FORMAT_BAYER = 0, + DMA_INPUT_FORMAT_YUV444 = 1, + DMA_INPUT_FORMAT_YUV422 = 2, + DMA_INPUT_FORMAT_YUV420 = 3, +}; + +enum dma_input_order { + /* (for DMA_INPUT_PLANE_3) */ + DMA_INPUT_ORDER_NO = 0, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CBCR = 1, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CRCB = 2, + /* (only valid at DMA_INPUT_PLANE_1 & DMA_INPUT_FORMAT_YUV444) */ + DMA_INPUT_ORDER_YCBCR = 3, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YYCBCR = 4, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCBYCR = 5, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCRYCB = 6, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CBYCRY = 7, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CRYCBY = 8, + /* (only valid at DMA_INPUT_FORMAT_BAYER) */ + DMA_INPUT_ORDER_GR_BG = 9 +}; + +#define DMA_INPUT_ERROR_NONE 0 /* DMA input setting + is done */ +/* + * Data output parameter definitions + */ +#define OTF_OUTPUT_CROP_DISABLE 0 +#define OTF_OUTPUT_CROP_ENABLE 1 + +#define OTF_OUTPUT_COMMAND_DISABLE 0 +#define OTF_OUTPUT_COMMAND_ENABLE 1 + +enum otf_output_fmt { + OTF_OUTPUT_FORMAT_YUV444 = 1, + OTF_OUTPUT_FORMAT_YUV422 = 2, + OTF_OUTPUT_FORMAT_YUV420 = 3, + OTF_OUTPUT_FORMAT_RGB = 4, +}; + +#define OTF_OUTPUT_ORDER_BAYER_GR_BG 0 + +#define OTF_OUTPUT_ERROR_NONE 0 /* Output Setting is done */ + +#define DMA_OUTPUT_COMMAND_DISABLE 0 +#define DMA_OUTPUT_COMMAND_ENABLE 1 + +enum dma_output_fmt { + DMA_OUTPUT_FORMAT_BAYER = 0, + DMA_OUTPUT_FORMAT_YUV444 = 1, + DMA_OUTPUT_FORMAT_YUV422 = 2, + DMA_OUTPUT_FORMAT_YUV420 = 3, + DMA_OUTPUT_FORMAT_RGB = 4, +}; + +enum dma_output_order { + DMA_OUTPUT_ORDER_NO = 0, + /* for DMA_OUTPUT_PLANE_3 */ + DMA_OUTPUT_ORDER_CBCR = 1, + /* only valid at DMA_INPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_CRCB = 2, + /* only valid at DMA_OUTPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_YYCBCR = 3, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCBYCR = 4, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCRYCB = 5, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBYCRY = 6, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRYCBY = 7, + /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCBCR = 8, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRYCB = 9, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CRCBY = 10, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBYCR = 11, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_YCRCB = 12, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_CBCRY = 13, + /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ + DMA_OUTPUT_ORDER_BGR = 14, + /* only valid at DMA_OUTPUT_FORMAT_RGB */ + DMA_OUTPUT_ORDER_GB_BG = 15 + /* only valid at DMA_OUTPUT_FORMAT_BAYER */ +}; + +/* enum dma_output_notify_dma_done */ +#define DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE 0 +#define DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE 1 + +/* DMA output error codes */ +#define DMA_OUTPUT_ERROR_NONE 0 /* DMA output setting + is done */ + +/* ---------------------- Global ----------------------------------- */ +#define GLOBAL_SHOTMODE_ERROR_NONE 0 /* shot-mode setting + is done */ +/* 3A lock commands */ +#define ISP_AA_COMMAND_START 0 +#define ISP_AA_COMMAND_STOP 1 + +/* 3A lock target */ +#define ISP_AA_TARGET_AF 1 +#define ISP_AA_TARGET_AE 2 +#define ISP_AA_TARGET_AWB 4 + +enum isp_af_mode { + ISP_AF_MODE_MANUAL = 0, + ISP_AF_MODE_SINGLE = 1, + ISP_AF_MODE_CONTINUOUS = 2, + ISP_AF_MODE_TOUCH = 3, + ISP_AF_MODE_SLEEP = 4, + ISP_AF_MODE_INIT = 5, + ISP_AF_MODE_SET_CENTER_WINDOW = 6, + ISP_AF_MODE_SET_TOUCH_WINDOW = 7 +}; + +/* Face AF commands */ +#define ISP_AF_FACE_DISABLE 0 +#define ISP_AF_FACE_ENABLE 1 + +/* AF range */ +#define ISP_AF_RANGE_NORMAL 0 +#define ISP_AF_RANGE_MACRO 1 + +/* AF sleep */ +#define ISP_AF_SLEEP_OFF 0 +#define ISP_AF_SLEEP_ON 1 + +/* Continuous AF commands */ +#define ISP_AF_CONTINUOUS_DISABLE 0 +#define ISP_AF_CONTINUOUS_ENABLE 1 + +/* ISP AF error codes */ +#define ISP_AF_ERROR_NONE 0 /* AF mode change is done */ +#define ISP_AF_ERROR_NONE_LOCK_DONE 1 /* AF lock is done */ + +/* Flash commands */ +#define ISP_FLASH_COMMAND_DISABLE 0 +#define ISP_FLASH_COMMAND_MANUAL_ON 1 /* (forced flash) */ +#define ISP_FLASH_COMMAND_AUTO 2 +#define ISP_FLASH_COMMAND_TORCH 3 /* 3 sec */ + +/* Flash red-eye commads */ +#define ISP_FLASH_REDEYE_DISABLE 0 +#define ISP_FLASH_REDEYE_ENABLE 1 + +/* Flash error codes */ +#define ISP_FLASH_ERROR_NONE 0 /* Flash setting is done */ + +/* -------------------------- AWB ------------------------------------ */ +enum isp_awb_command { + ISP_AWB_COMMAND_AUTO = 0, + ISP_AWB_COMMAND_ILLUMINATION = 1, + ISP_AWB_COMMAND_MANUAL = 2 +}; + +enum isp_awb_illumination { + ISP_AWB_ILLUMINATION_DAYLIGHT = 0, + ISP_AWB_ILLUMINATION_CLOUDY = 1, + ISP_AWB_ILLUMINATION_TUNGSTEN = 2, + ISP_AWB_ILLUMINATION_FLUORESCENT = 3 +}; + +/* ISP AWN error codes */ +#define ISP_AWB_ERROR_NONE 0 /* AWB setting is done */ + +/* -------------------------- Effect ----------------------------------- */ +enum isp_imageeffect_command { + ISP_IMAGE_EFFECT_DISABLE = 0, + ISP_IMAGE_EFFECT_MONOCHROME = 1, + ISP_IMAGE_EFFECT_NEGATIVE_MONO = 2, + ISP_IMAGE_EFFECT_NEGATIVE_COLOR = 3, + ISP_IMAGE_EFFECT_SEPIA = 4 +}; + +/* Image effect error codes */ +#define ISP_IMAGE_EFFECT_ERROR_NONE 0 /* Image effect setting + is done */ +/* ISO commands */ +#define ISP_ISO_COMMAND_AUTO 0 +#define ISP_ISO_COMMAND_MANUAL 1 + +/* ISO error codes */ +#define ISP_ISO_ERROR_NONE 0 /* ISO setting is done */ + +/* ISP adjust commands */ +#define ISP_ADJUST_COMMAND_AUTO (0 << 0) +#define ISP_ADJUST_COMMAND_MANUAL_CONTRAST (1 << 0) +#define ISP_ADJUST_COMMAND_MANUAL_SATURATION (1 << 1) +#define ISP_ADJUST_COMMAND_MANUAL_SHARPNESS (1 << 2) +#define ISP_ADJUST_COMMAND_MANUAL_EXPOSURE (1 << 3) +#define ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS (1 << 4) +#define ISP_ADJUST_COMMAND_MANUAL_HUE (1 << 5) +#define ISP_ADJUST_COMMAND_MANUAL_ALL 0x7f + +/* ISP adjustment error codes */ +#define ISP_ADJUST_ERROR_NONE 0 /* Adjust setting is done */ + +/* + * Exposure metering + */ +enum isp_metering_command { + ISP_METERING_COMMAND_AVERAGE = 0, + ISP_METERING_COMMAND_SPOT = 1, + ISP_METERING_COMMAND_MATRIX = 2, + ISP_METERING_COMMAND_CENTER = 3 +}; + +/* ISP metering error codes */ +#define ISP_METERING_ERROR_NONE 0 /* Metering setting is done */ + +/* + * AFC + */ +enum isp_afc_command { + ISP_AFC_COMMAND_DISABLE = 0, + ISP_AFC_COMMAND_AUTO = 1, + ISP_AFC_COMMAND_MANUAL = 2, +}; + +#define ISP_AFC_MANUAL_50HZ 50 +#define ISP_AFC_MANUAL_60HZ 60 + +/* ------------------------ SCENE MODE--------------------------------- */ +enum isp_scene_mode { + ISP_SCENE_NONE = 0, + ISP_SCENE_PORTRAIT = 1, + ISP_SCENE_LANDSCAPE = 2, + ISP_SCENE_SPORTS = 3, + ISP_SCENE_PARTYINDOOR = 4, + ISP_SCENE_BEACHSNOW = 5, + ISP_SCENE_SUNSET = 6, + ISP_SCENE_DAWN = 7, + ISP_SCENE_FALL = 8, + ISP_SCENE_NIGHT = 9, + ISP_SCENE_AGAINSTLIGHTWLIGHT = 10, + ISP_SCENE_AGAINSTLIGHTWOLIGHT = 11, + ISP_SCENE_FIRE = 12, + ISP_SCENE_TEXT = 13, + ISP_SCENE_CANDLE = 14 +}; + +/* AFC error codes */ +#define ISP_AFC_ERROR_NONE 0 /* AFC setting is done */ + +/* ---------------------------- FD ------------------------------------- */ +enum fd_config_command { + FD_CONFIG_COMMAND_MAXIMUM_NUMBER = 0x1, + FD_CONFIG_COMMAND_ROLL_ANGLE = 0x2, + FD_CONFIG_COMMAND_YAW_ANGLE = 0x4, + FD_CONFIG_COMMAND_SMILE_MODE = 0x8, + FD_CONFIG_COMMAND_BLINK_MODE = 0x10, + FD_CONFIG_COMMAND_EYES_DETECT = 0x20, + FD_CONFIG_COMMAND_MOUTH_DETECT = 0x40, + FD_CONFIG_COMMAND_ORIENTATION = 0x80, + FD_CONFIG_COMMAND_ORIENTATION_VALUE = 0x100 +}; + +enum fd_config_roll_angle { + FD_CONFIG_ROLL_ANGLE_BASIC = 0, + FD_CONFIG_ROLL_ANGLE_PRECISE_BASIC = 1, + FD_CONFIG_ROLL_ANGLE_SIDES = 2, + FD_CONFIG_ROLL_ANGLE_PRECISE_SIDES = 3, + FD_CONFIG_ROLL_ANGLE_FULL = 4, + FD_CONFIG_ROLL_ANGLE_PRECISE_FULL = 5, +}; + +enum fd_config_yaw_angle { + FD_CONFIG_YAW_ANGLE_0 = 0, + FD_CONFIG_YAW_ANGLE_45 = 1, + FD_CONFIG_YAW_ANGLE_90 = 2, + FD_CONFIG_YAW_ANGLE_45_90 = 3, +}; + +/* Smile mode configuration */ +#define FD_CONFIG_SMILE_MODE_DISABLE 0 +#define FD_CONFIG_SMILE_MODE_ENABLE 1 + +/* Blink mode configuration */ +#define FD_CONFIG_BLINK_MODE_DISABLE 0 +#define FD_CONFIG_BLINK_MODE_ENABLE 1 + +/* Eyes detection configuration */ +#define FD_CONFIG_EYES_DETECT_DISABLE 0 +#define FD_CONFIG_EYES_DETECT_ENABLE 1 + +/* Mouth detection configuration */ +#define FD_CONFIG_MOUTH_DETECT_DISABLE 0 +#define FD_CONFIG_MOUTH_DETECT_ENABLE 1 + +#define FD_CONFIG_ORIENTATION_DISABLE 0 +#define FD_CONFIG_ORIENTATION_ENABLE 1 + +struct param_control { + u32 cmd; + u32 bypass; + u32 buffer_address; + u32 buffer_size; + u32 skip_frames; /* only valid at ISP */ + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; + u32 err; +}; + +struct param_otf_input { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 order; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 frametime_min; + u32 frametime_max; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 13]; + u32 err; +}; + +struct param_dma_input { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 plane; + u32 order; + u32 buffer_number; + u32 buffer_address; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_otf_output { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 order; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; + u32 err; +}; + +struct param_dma_output { + u32 cmd; + u32 width; + u32 height; + u32 format; + u32 bitwidth; + u32 plane; + u32 order; + u32 buffer_number; + u32 buffer_address; + u32 notify_dma_done; + u32 dma_out_mask; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 12]; + u32 err; +}; + +struct param_global_shotmode { + u32 cmd; + u32 skip_frames; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_sensor_framerate { + u32 frame_rate; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_isp_aa { + u32 cmd; + u32 target; + u32 mode; + u32 scene; + u32 sleep; + u32 face; + u32 touch_x; + u32 touch_y; + u32 manual_af_setting; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_isp_flash { + u32 cmd; + u32 redeye; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_awb { + u32 cmd; + u32 illumination; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_imageeffect { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_isp_iso { + u32 cmd; + u32 value; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_isp_adjust { + u32 cmd; + s32 contrast; + s32 saturation; + s32 sharpness; + s32 exposure; + s32 brightness; + s32 hue; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 8]; + u32 err; +}; + +struct param_isp_metering { + u32 cmd; + u32 win_pos_x; + u32 win_pos_y; + u32 win_width; + u32 win_height; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; + u32 err; +}; + +struct param_isp_afc { + u32 cmd; + u32 manual; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; + u32 err; +}; + +struct param_scaler_imageeffect { + u32 cmd; + u32 arbitrary_cb; + u32 arbitrary_cr; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 4]; + u32 err; +}; + +struct param_scaler_input_crop { + u32 cmd; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 in_width; + u32 in_height; + u32 out_width; + u32 out_height; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; + u32 err; +}; + +struct param_scaler_output_crop { + u32 cmd; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 out_format; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; + u32 err; +}; + +struct param_scaler_rotation { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_scaler_flip { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_3dnr_1stframe { + u32 cmd; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; + u32 err; +}; + +struct param_fd_config { + u32 cmd; + u32 max_number; + u32 roll_angle; + u32 yaw_angle; + u32 smile_mode; + u32 blink_mode; + u32 eye_detect; + u32 mouth_detect; + u32 orientation; + u32 orientation_value; + u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 11]; + u32 err; +}; + +struct global_param { + struct param_global_shotmode shotmode; +}; + +struct sensor_param { + struct param_control control; + struct param_otf_output otf_output; + struct param_sensor_framerate frame_rate; +} __packed; + +struct buffer_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_otf_output otf_output; +} __packed; + +struct isp_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma1_input; + struct param_dma_input dma2_input; + struct param_isp_aa aa; + struct param_isp_flash flash; + struct param_isp_awb awb; + struct param_isp_imageeffect effect; + struct param_isp_iso iso; + struct param_isp_adjust adjust; + struct param_isp_metering metering; + struct param_isp_afc afc; + struct param_otf_output otf_output; + struct param_dma_output dma1_output; + struct param_dma_output dma2_output; +} __packed; + +struct drc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_otf_output otf_output; +} __packed; + +struct scalerc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_scaler_imageeffect effect; + struct param_scaler_input_crop input_crop; + struct param_scaler_output_crop output_crop; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct odc_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_otf_output otf_output; +} __packed; + +struct dis_param { + struct param_control control; + struct param_otf_output otf_input; + struct param_otf_output otf_output; +} __packed; + +struct tdnr_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_3dnr_1stframe frame; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct scalerp_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_scaler_imageeffect effect; + struct param_scaler_input_crop input_crop; + struct param_scaler_output_crop output_crop; + struct param_scaler_rotation rotation; + struct param_scaler_flip flip; + struct param_otf_output otf_output; + struct param_dma_output dma_output; +} __packed; + +struct fd_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_fd_config config; +} __packed; + +struct is_param_region { + struct global_param global; + struct sensor_param sensor; + struct buffer_param buf; + struct isp_param isp; + struct drc_param drc; + struct scalerc_param scalerc; + struct odc_param odc; + struct dis_param dis; + struct tdnr_param tdnr; + struct scalerp_param scalerp; + struct fd_param fd; +} __packed; + +#define NUMBER_OF_GAMMA_CURVE_POINTS 32 + +struct is_tune_sensor { + u32 exposure; + u32 analog_gain; + u32 frame_rate; + u32 actuator_position; +}; + +struct is_tune_gammacurve { + u32 num_pts_x[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_r[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_g[NUMBER_OF_GAMMA_CURVE_POINTS]; + u32 num_pts_y_b[NUMBER_OF_GAMMA_CURVE_POINTS]; +}; + +struct is_tune_isp { + /* Brightness level: range 0...100, default 7. */ + u32 brightness_level; + /* Contrast level: range -127...127, default 0. */ + s32 contrast_level; + /* Saturation level: range -127...127, default 0. */ + s32 saturation_level; + s32 gamma_level; + struct is_tune_gammacurve gamma_curve[4]; + /* Hue: range -127...127, default 0. */ + s32 hue; + /* Sharpness blur: range -127...127, default 0. */ + s32 sharpness_blur; + /* Despeckle : range -127~127, default : 0 */ + s32 despeckle; + /* Edge color supression: range -127...127, default 0. */ + s32 edge_color_supression; + /* Noise reduction: range -127...127, default 0. */ + s32 noise_reduction; + /* (32 * 4 + 9) * 4 = 548 bytes */ +} __packed; + +struct is_tune_region { + struct is_tune_sensor sensor; + struct is_tune_isp isp; +} __packed; + +struct rational { + u32 num; + u32 den; +}; + +struct srational { + s32 num; + s32 den; +}; + +#define FLASH_FIRED_SHIFT 0 +#define FLASH_NOT_FIRED 0 +#define FLASH_FIRED 1 + +#define FLASH_STROBE_SHIFT 1 +#define FLASH_STROBE_NO_DETECTION 0 +#define FLASH_STROBE_RESERVED 1 +#define FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED 2 +#define FLASH_STROBE_RETURN_LIGHT_DETECTED 3 + +#define FLASH_MODE_SHIFT 3 +#define FLASH_MODE_UNKNOWN 0 +#define FLASH_MODE_COMPULSORY_FLASH_FIRING 1 +#define FLASH_MODE_COMPULSORY_FLASH_SUPPRESSION 2 +#define FLASH_MODE_AUTO_MODE 3 + +#define FLASH_FUNCTION_SHIFT 5 +#define FLASH_FUNCTION_PRESENT 0 +#define FLASH_FUNCTION_NONE 1 + +#define FLASH_RED_EYE_SHIFT 6 +#define FLASH_RED_EYE_DISABLED 0 +#define FLASH_RED_EYE_SUPPORTED 1 + +enum apex_aperture_value { + F1_0 = 0, + F1_4 = 1, + F2_0 = 2, + F2_8 = 3, + F4_0 = 4, + F5_6 = 5, + F8_9 = 6, + F11_0 = 7, + F16_0 = 8, + F22_0 = 9, + F32_0 = 10, +}; + +struct exif_attribute { + struct rational exposure_time; + struct srational shutter_speed; + u32 iso_speed_rating; + u32 flash; + struct srational brightness; +} __packed; + +struct is_frame_header { + u32 valid; + u32 bad_mark; + u32 captured; + u32 frame_number; + struct exif_attribute exif; +} __packed; + +struct is_fd_rect { + u32 offset_x; + u32 offset_y; + u32 width; + u32 height; +}; + +struct is_face_marker { + u32 frame_number; + struct is_fd_rect face; + struct is_fd_rect left_eye; + struct is_fd_rect right_eye; + struct is_fd_rect mouth; + u32 roll_angle; + u32 yaw_angle; + u32 confidence; + s32 smile_level; + s32 blink_level; +} __packed; + +#define MAX_FRAME_COUNT 8 +#define MAX_FRAME_COUNT_PREVIEW 4 +#define MAX_FRAME_COUNT_CAPTURE 1 +#define MAX_FACE_COUNT 16 +#define MAX_SHARED_COUNT 500 + +struct is_region { + struct is_param_region parameter; + struct is_tune_region tune; + struct is_frame_header header[MAX_FRAME_COUNT]; + struct is_face_marker face[MAX_FACE_COUNT]; + u32 shared[MAX_SHARED_COUNT]; +} __packed; + +struct is_debug_frame_descriptor { + u32 sensor_frame_time; + u32 sensor_exposure_time; + s32 sensor_analog_gain; + /* monitor for AA */ + u32 req_lei; + + u32 next_next_lei_exp; + u32 next_next_lei_a_gain; + u32 next_next_lei_d_gain; + u32 next_next_lei_statlei; + u32 next_next_lei_lei; + + u32 dummy0; +}; + +#define MAX_FRAMEDESCRIPTOR_CONTEXT_NUM (30*20) /* 600 frames */ +#define MAX_VERSION_DISPLAY_BUF 32 + +struct is_share_region { + u32 frame_time; + u32 exposure_time; + s32 analog_gain; + + u32 r_gain; + u32 g_gain; + u32 b_gain; + + u32 af_position; + u32 af_status; + /* 0 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_NOMESSAGE */ + /* 1 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_REACHED */ + /* 2 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_UNABLETOREACH */ + /* 3 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_LOST */ + /* default : unknown */ + u32 af_scene_type; + + u32 frame_descp_onoff_control; + u32 frame_descp_update_done; + u32 frame_descp_idx; + u32 frame_descp_max_idx; + struct is_debug_frame_descriptor + dbg_frame_descp_ctx[MAX_FRAMEDESCRIPTOR_CONTEXT_NUM]; + + u32 chip_id; + u32 chip_rev_no; + u8 isp_fw_ver_no[MAX_VERSION_DISPLAY_BUF]; + u8 isp_fw_ver_date[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_ver_no[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_rev_no[MAX_VERSION_DISPLAY_BUF]; + u8 sirc_sdk_rev_date[MAX_VERSION_DISPLAY_BUF]; +} __packed; + +struct is_debug_control { + u32 write_point; /* 0~ 500KB boundary */ + u32 assert_flag; /* 0: Not invoked, 1: Invoked */ + u32 pabort_flag; /* 0: Not invoked, 1: Invoked */ + u32 dabort_flag; /* 0: Not invoked, 1: Invoked */ +}; + +struct sensor_open_extended { + u32 actuator_type; + u32 mclk; + u32 mipi_lane_num; + u32 mipi_speed; + /* Skip setfile loading when fast_open_sensor is not 0 */ + u32 fast_open_sensor; + /* Activating sensor self calibration mode (6A3) */ + u32 self_calibration_mode; + /* This field is to adjust I2c clock based on ACLK200 */ + /* This value is varied in case of rev 0.2 */ + u32 i2c_sclk; +}; + +struct fimc_is; + +int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); +void fimc_is_set_initial_params(struct fimc_is *is); +unsigned int __get_pending_param_count(struct fimc_is *is); + +int __is_hw_update_params(struct fimc_is *is); +void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); +void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); +void __is_set_sensor(struct fimc_is *is, int fps); +void __is_set_isp_aa_ae(struct fimc_is *is); +void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye); +void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_effect(struct fimc_is *is, u32 cmd); +void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val); +void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val); +void __is_set_drc_control(struct fimc_is *is, u32 val); +void __is_set_fd_control(struct fimc_is *is, u32 val); +void __is_set_fd_config_maxface(struct fimc_is *is, u32 val); +void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val); +void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val); +void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val); +void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val); +void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val); +void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val); +void __is_set_fd_config_orientation(struct fimc_is *is, u32 val); +void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val); +void __is_set_isp_aa_af_mode(struct fimc_is *is, int cmd); +void __is_set_isp_aa_af_start_stop(struct fimc_is *is, int cmd); + +#endif diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c new file mode 100644 index 000000000000..b0ff67bc1b05 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -0,0 +1,243 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> + +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-regs.h" +#include "fimc-is-sensor.h" + +void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int nr) +{ + mcuctl_write(1UL << nr, is, MCUCTL_REG_INTCR1); +} + +void fimc_is_fw_clear_irq2(struct fimc_is *is) +{ + u32 cfg = mcuctl_read(is, MCUCTL_REG_INTSR2); + mcuctl_write(cfg, is, MCUCTL_REG_INTCR2); +} + +void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is) +{ + mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0); +} + +int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is) +{ + unsigned int timeout = 2000; + u32 cfg, status; + + cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); + status = INTSR0_GET_INTSD(0, cfg); + + while (status) { + cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); + status = INTSR0_GET_INTSD(0, cfg); + if (timeout == 0) { + dev_warn(&is->pdev->dev, "%s timeout\n", + __func__); + return -ETIME; + } + timeout--; + udelay(1); + } + return 0; +} + +int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is) +{ + unsigned int timeout = 2000; + u32 cfg, status; + + cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); + status = INTMSR0_GET_INTMSD(0, cfg); + + while (status) { + cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); + status = INTMSR0_GET_INTMSD(0, cfg); + if (timeout == 0) { + dev_warn(&is->pdev->dev, "%s timeout\n", + __func__); + return -ETIME; + } + timeout--; + udelay(1); + } + return 0; +} + +int fimc_is_hw_set_param(struct fimc_is *is) +{ + struct chain_config *config = &is->config[is->config_index]; + unsigned int param_count = __get_pending_param_count(is); + + fimc_is_hw_wait_intmsr0_intmsd0(is); + + mcuctl_write(HIC_SET_PARAMETER, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2)); + + mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3)); + mcuctl_write(config->p_region_index1, is, MCUCTL_REG_ISSR(4)); + mcuctl_write(config->p_region_index2, is, MCUCTL_REG_ISSR(5)); + + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +int fimc_is_hw_set_tune(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + + mcuctl_write(HIC_SET_TUNE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->h2i_cmd.entry_id, is, MCUCTL_REG_ISSR(2)); + + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +#define FIMC_IS_MAX_PARAMS 4 + +int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args) +{ + int i; + + if (num_args > FIMC_IS_MAX_PARAMS) + return -EINVAL; + + is->i2h_cmd.num_args = num_args; + + for (i = 0; i < FIMC_IS_MAX_PARAMS; i++) { + if (i < num_args) + is->i2h_cmd.args[i] = mcuctl_read(is, + MCUCTL_REG_ISSR(12 + i)); + else + is->i2h_cmd.args[i] = 0; + } + return 0; +} + +void fimc_is_hw_set_sensor_num(struct fimc_is *is) +{ + pr_debug("setting sensor index to: %d\n", is->sensor_index); + + mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); + mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3)); +} + +void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) +{ + if (is->sensor_index != index) + return; + + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_CLOSE_SENSOR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_get_setfile_addr(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_GET_SET_FILE_ADDR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_load_setfile(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_LOAD_SET_FILE, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +int fimc_is_hw_change_mode(struct fimc_is *is) +{ + const u8 cmd[] = { + HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO, + HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO, + }; + + if (WARN_ON(is->config_index > ARRAY_SIZE(cmd))) + return -EINVAL; + + mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); + return 0; +} + +void fimc_is_hw_stream_on(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_STREAM_ON, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(0, is, MCUCTL_REG_ISSR(2)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_stream_off(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_STREAM_OFF, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +void fimc_is_hw_subip_power_off(struct fimc_is *is) +{ + fimc_is_hw_wait_intmsr0_intmsd0(is); + mcuctl_write(HIC_POWER_DOWN, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + fimc_is_hw_set_intgr0_gd0(is); +} + +int fimc_is_itf_s_param(struct fimc_is *is, bool update) +{ + int ret; + + if (update) + __is_hw_update_params(is); + + fimc_is_mem_barrier(); + + clear_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + fimc_is_hw_set_param(is); + ret = fimc_is_wait_event(is, IS_ST_BLOCK_CMD_CLEARED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) + dev_err(&is->pdev->dev, "%s() timeout\n", __func__); + + return ret; +} + +int fimc_is_itf_mode_change(struct fimc_is *is) +{ + int ret; + + clear_bit(IS_ST_CHANGE_MODE, &is->state); + fimc_is_hw_change_mode(is); + ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (!ret < 0) + dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n", + __func__, is->config_index); + return ret; +} diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h new file mode 100644 index 000000000000..5fa2fda46742 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h @@ -0,0 +1,164 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_IS_REG_H_ +#define FIMC_IS_REG_H_ + +/* WDT_ISP register */ +#define REG_WDT_ISP 0x00170000 + +/* MCUCTL registers base offset */ +#define MCUCTL_BASE 0x00180000 + +/* MCU Controller Register */ +#define MCUCTL_REG_MCUCTRL (MCUCTL_BASE + 0x00) +#define MCUCTRL_MSWRST (1 << 0) + +/* Boot Base Offset Address Register */ +#define MCUCTL_REG_BBOAR (MCUCTL_BASE + 0x04) + +/* Interrupt Generation Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTGR0 (MCUCTL_BASE + 0x08) +/* __n = 0...9 */ +#define INTGR0_INTGC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTGR0_INTGD(__n) (1 << (__n)) + +/* Interrupt Clear Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTCR0 (MCUCTL_BASE + 0x0c) +/* __n = 0...9 */ +#define INTCR0_INTGC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTCR0_INTCD(__n) (1 << ((__n) + 16)) + +/* Interrupt Mask Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTMR0 (MCUCTL_BASE + 0x10) +/* __n = 0...9 */ +#define INTMR0_INTMC(__n) (1 << ((__n) + 16)) +/* __n = 0...5 */ +#define INTMR0_INTMD(__n) (1 << (__n)) + +/* Interrupt Status Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTSR0 (MCUCTL_BASE + 0x14) +/* __n (bit number) = 0...4 */ +#define INTSR0_GET_INTSD(x, __n) (((x) >> (__n)) & 0x1) +/* __n (bit number) = 0...9 */ +#define INTSR0_GET_INTSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) + +/* Interrupt Mask Status Register 0 from Host CPU to VIC */ +#define MCUCTL_REG_INTMSR0 (MCUCTL_BASE + 0x18) +/* __n (bit number) = 0...4 */ +#define INTMSR0_GET_INTMSD(x, __n) (((x) >> (__n)) & 0x1) +/* __n (bit number) = 0...9 */ +#define INTMSR0_GET_INTMSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) + +/* Interrupt Generation Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTGR1 (MCUCTL_BASE + 0x1c) +/* __n = 0...9 */ +#define INTGR1_INTGC(__n) (1 << (__n)) + +/* Interrupt Clear Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTCR1 (MCUCTL_BASE + 0x20) +/* __n = 0...9 */ +#define INTCR1_INTCC(__n) (1 << (__n)) + +/* Interrupt Mask Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTMR1 (MCUCTL_BASE + 0x24) +/* __n = 0...9 */ +#define INTMR1_INTMC(__n) (1 << (__n)) + +/* Interrupt Status Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTSR1 (MCUCTL_BASE + 0x28) +/* Interrupt Mask Status Register 1 from ISP CPU to Host IC */ +#define MCUCTL_REG_INTMSR1 (MCUCTL_BASE + 0x2c) + +/* Interrupt Clear Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTCR2 (MCUCTL_BASE + 0x30) +/* __n = 0...5 */ +#define INTCR2_INTCC(__n) (1 << ((__n) + 16)) + +/* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTMR2 (MCUCTL_BASE + 0x34) +/* __n = 0...25 */ +#define INTMR2_INTMCIS(__n) (1 << (__n)) + +/* Interrupt Status Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTSR2 (MCUCTL_BASE + 0x38) +/* Interrupt Mask Status Register 2 from ISP BLK's interrupts to Host IC */ +#define MCUCTL_REG_INTMSR2 (MCUCTL_BASE + 0x3c) + +/* General Purpose Output Control Register (0~17) */ +#define MCUCTL_REG_GPOCTLR (MCUCTL_BASE + 0x40) +/* __n = 0...17 */ +#define GPOCTLR_GPOG(__n) (1 << (__n)) + +/* General Purpose Pad Output Enable Register (0~17) */ +#define MCUCTL_REG_GPOENCTLR (MCUCTL_BASE + 0x44) +/* __n = 0...17 */ +#define GPOENCTLR_GPOEN(__n) (1 << (__n)) + +/* General Purpose Input Control Register (0~17) */ +#define MCUCTL_REG_GPICTLR (MCUCTL_BASE + 0x48) + +/* Shared registers between ISP CPU and the host CPU - ISSRxx */ + +/* ISSR(1): Command Host -> IS */ +/* ISSR(1): Sensor ID for Command, ISSR2...5 = Parameter 1...4 */ + +/* ISSR(10): Reply IS -> Host */ +/* ISSR(11): Sensor ID for Reply, ISSR12...15 = Parameter 1...4 */ + +/* ISSR(20): ISP_FRAME_DONE : SENSOR ID */ +/* ISSR(21): ISP_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(24): SCALERC_FRAME_DONE : SENSOR ID */ +/* ISSR(25): SCALERC_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(28): 3DNR_FRAME_DONE : SENSOR ID */ +/* ISSR(29): 3DNR_FRAME_DONE : PARAMETER 1 */ + +/* ISSR(32): SCALERP_FRAME_DONE : SENSOR ID */ +/* ISSR(33): SCALERP_FRAME_DONE : PARAMETER 1 */ + +/* __n = 0...63 */ +#define MCUCTL_REG_ISSR(__n) (MCUCTL_BASE + 0x80 + ((__n) * 4)) + +/* PMU ISP register offsets */ +#define REG_CMU_RESET_ISP_SYS_PWR_REG 0x1174 +#define REG_CMU_SYSCLK_ISP_SYS_PWR_REG 0x13b8 +#define REG_PMU_ISP_ARM_SYS 0x1050 +#define REG_PMU_ISP_ARM_CONFIGURATION 0x2280 +#define REG_PMU_ISP_ARM_STATUS 0x2284 +#define REG_PMU_ISP_ARM_OPTION 0x2288 + +void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int bit); +void fimc_is_fw_clear_irq2(struct fimc_is *is); +int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num); + +void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); +int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is); +int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is); +void fimc_is_hw_set_sensor_num(struct fimc_is *is); +void fimc_is_hw_stream_on(struct fimc_is *is); +void fimc_is_hw_stream_off(struct fimc_is *is); +int fimc_is_hw_set_param(struct fimc_is *is); +int fimc_is_hw_change_mode(struct fimc_is *is); + +void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index); +void fimc_is_hw_get_setfile_addr(struct fimc_is *is); +void fimc_is_hw_load_setfile(struct fimc_is *is); +void fimc_is_hw_subip_power_off(struct fimc_is *is); + +int fimc_is_itf_s_param(struct fimc_is *is, bool update); +int fimc_is_itf_mode_change(struct fimc_is *is); + +#endif /* FIMC_IS_REG_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c new file mode 100644 index 000000000000..6647421e5d3a --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c @@ -0,0 +1,305 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <media/v4l2-subdev.h> + +#include "fimc-is.h" +#include "fimc-is-sensor.h" + +#define DRIVER_NAME "FIMC-IS-SENSOR" + +static const char * const sensor_supply_names[] = { + "svdda", + "svddio", +}; + +static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { + { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + } +}; + +static const struct v4l2_mbus_framefmt *find_sensor_format( + struct v4l2_mbus_framefmt *mf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) + if (mf->code == fimc_is_sensor_formats[i].code) + return &fimc_is_sensor_formats[i]; + + return &fimc_is_sensor_formats[0]; +} + +static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) + return -EINVAL; + + code->code = fimc_is_sensor_formats[code->index].code; + return 0; +} + +static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, + struct v4l2_mbus_framefmt *mf) +{ + const struct sensor_drv_data *dd = sensor->drvdata; + const struct v4l2_mbus_framefmt *fmt; + + fmt = find_sensor_format(mf); + mf->code = fmt->code; + v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, + &mf->height, 12 + 8, dd->height, 0, 0); +} + +static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( + struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, + u32 pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + + return &sensor->format; +} + +static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + struct v4l2_mbus_framefmt *mf; + + fimc_is_sensor_try_format(sensor, &fmt->format); + + mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); + if (mf) { + mutex_lock(&sensor->lock); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + *mf = fmt->format; + mutex_unlock(&sensor->lock); + } + return 0; +} + +static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + struct v4l2_mbus_framefmt *mf; + + mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); + + mutex_lock(&sensor->lock); + fmt->format = *mf; + mutex_unlock(&sensor->lock); + return 0; +} + +static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { + .enum_mbus_code = fimc_is_sensor_enum_mbus_code, + .get_fmt = fimc_is_sensor_get_fmt, + .set_fmt = fimc_is_sensor_set_fmt, +}; + +static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + + *format = fimc_is_sensor_formats[0]; + format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; + format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; + + return 0; +} + +static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { + .open = fimc_is_sensor_open, +}; + +static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + int gpio = sensor->gpio_reset; + int ret; + + if (on) { + ret = pm_runtime_get(sensor->dev); + if (ret < 0) + return ret; + + ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (ret < 0) { + pm_runtime_put(sensor->dev); + return ret; + } + if (gpio_is_valid(gpio)) { + gpio_set_value(gpio, 1); + usleep_range(600, 800); + gpio_set_value(gpio, 0); + usleep_range(10000, 11000); + gpio_set_value(gpio, 1); + } + + /* A delay needed for the sensor initialization. */ + msleep(20); + } else { + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, 0); + + ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (!ret) + pm_runtime_put(sensor->dev); + } + + pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); + + return ret; +} + +static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { + .s_power = fimc_is_sensor_s_power, +}; + +static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { + .core = &fimc_is_sensor_core_ops, + .pad = &fimc_is_sensor_pad_ops, +}; + +static const struct of_device_id fimc_is_sensor_of_match[]; + +static int fimc_is_sensor_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct fimc_is_sensor *sensor; + const struct of_device_id *of_id; + struct v4l2_subdev *sd; + int gpio, i, ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + mutex_init(&sensor->lock); + sensor->gpio_reset = -EINVAL; + + gpio = of_get_gpio_flags(dev->of_node, 0, NULL); + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, + DRIVER_NAME); + if (ret < 0) + return ret; + } + sensor->gpio_reset = gpio; + + for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = sensor_supply_names[i]; + + ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, + sensor->supplies); + if (ret < 0) + return ret; + + of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); + if (!of_id) + return -ENODEV; + + sensor->drvdata = of_id->data; + sensor->dev = dev; + + sd = &sensor->subdev; + v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); + snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sensor->format.code = fimc_is_sensor_formats[0].code; + sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; + sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; + + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); + if (ret < 0) + return ret; + + pm_runtime_no_callbacks(dev); + pm_runtime_enable(dev); + + return ret; +} + +static int fimc_is_sensor_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + media_entity_cleanup(&sd->entity); + return 0; +} + +static const struct i2c_device_id fimc_is_sensor_ids[] = { + { } +}; + +static const struct sensor_drv_data s5k6a3_drvdata = { + .id = FIMC_IS_SENSOR_ID_S5K6A3, + .subdev_name = "S5K6A3", + .width = S5K6A3_SENSOR_WIDTH, + .height = S5K6A3_SENSOR_HEIGHT, +}; + +static const struct of_device_id fimc_is_sensor_of_match[] = { + { + .compatible = "samsung,s5k6a3", + .data = &s5k6a3_drvdata, + }, + { } +}; + +static struct i2c_driver fimc_is_sensor_driver = { + .driver = { + .of_match_table = fimc_is_sensor_of_match, + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = fimc_is_sensor_probe, + .remove = fimc_is_sensor_remove, + .id_table = fimc_is_sensor_ids, +}; + +int fimc_is_register_sensor_driver(void) +{ + return i2c_add_driver(&fimc_is_sensor_driver); +} + +void fimc_is_unregister_sensor_driver(void) +{ + i2c_del_driver(&fimc_is_sensor_driver); +} + +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h new file mode 100644 index 000000000000..6036d49a6c68 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h @@ -0,0 +1,89 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_IS_SENSOR_H_ +#define FIMC_IS_SENSOR_H_ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> + +#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */ + +#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296 +#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732 + +#define S5K6A3_SENSOR_WIDTH 1392 +#define S5K6A3_SENSOR_HEIGHT 1392 + +#define SENSOR_NUM_SUPPLIES 2 + +enum fimc_is_sensor_id { + FIMC_IS_SENSOR_ID_S5K3H2 = 1, + FIMC_IS_SENSOR_ID_S5K6A3, + FIMC_IS_SENSOR_ID_S5K4E5, + FIMC_IS_SENSOR_ID_S5K3H7, + FIMC_IS_SENSOR_ID_CUSTOM, + FIMC_IS_SENSOR_ID_END +}; + +#define IS_SENSOR_CTRL_BUS_I2C0 0 +#define IS_SENSOR_CTRL_BUS_I2C1 1 + +struct sensor_drv_data { + enum fimc_is_sensor_id id; + const char * const subdev_name; + unsigned int width; + unsigned int height; +}; + +/** + * struct fimc_is_sensor - fimc-is sensor data structure + * @dev: pointer to this I2C client device structure + * @subdev: the image sensor's v4l2 subdev + * @pad: subdev media source pad + * @supplies: image sensor's voltage regulator supplies + * @gpio_reset: GPIO connected to the sensor's reset pin + * @drvdata: a pointer to the sensor's parameters data structure + * @i2c_bus: ISP I2C bus index (0...1) + * @test_pattern: true to enable video test pattern + * @lock: mutex protecting the structure's members below + * @format: media bus format at the sensor's source pad + */ +struct fimc_is_sensor { + struct device *dev; + struct v4l2_subdev subdev; + struct media_pad pad; + struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES]; + int gpio_reset; + const struct sensor_drv_data *drvdata; + unsigned int i2c_bus; + bool test_pattern; + + struct mutex lock; + struct v4l2_mbus_framefmt format; +}; + +static inline +struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) +{ + return container_of(sd, struct fimc_is_sensor, subdev); +} + +int fimc_is_register_sensor_driver(void); +void fimc_is_unregister_sensor_driver(void); + +#endif /* FIMC_IS_SENSOR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c new file mode 100644 index 000000000000..47c6363d04e2 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -0,0 +1,1007 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/dma-contiguous.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_i2c.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <media/v4l2-of.h> +#include <media/videobuf2-dma-contig.h> + +#include "media-dev.h" +#include "fimc-is.h" +#include "fimc-is-command.h" +#include "fimc-is-errno.h" +#include "fimc-is-i2c.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" + + +static char *fimc_is_clocks[ISS_CLKS_MAX] = { + [ISS_CLK_PPMUISPX] = "ppmuispx", + [ISS_CLK_PPMUISPMX] = "ppmuispmx", + [ISS_CLK_LITE0] = "lite0", + [ISS_CLK_LITE1] = "lite1", + [ISS_CLK_MPLL] = "mpll", + [ISS_CLK_SYSREG] = "sysreg", + [ISS_CLK_ISP] = "isp", + [ISS_CLK_DRC] = "drc", + [ISS_CLK_FD] = "fd", + [ISS_CLK_MCUISP] = "mcuisp", + [ISS_CLK_UART] = "uart", + [ISS_CLK_ISP_DIV0] = "ispdiv0", + [ISS_CLK_ISP_DIV1] = "ispdiv1", + [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0", + [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1", + [ISS_CLK_ACLK200] = "aclk200", + [ISS_CLK_ACLK200_DIV] = "div_aclk200", + [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp", + [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp", +}; + +static void fimc_is_put_clocks(struct fimc_is *is) +{ + int i; + + for (i = 0; i < ISS_CLKS_MAX; i++) { + if (IS_ERR(is->clocks[i])) + continue; + clk_unprepare(is->clocks[i]); + clk_put(is->clocks[i]); + is->clocks[i] = ERR_PTR(-EINVAL); + } +} + +static int fimc_is_get_clocks(struct fimc_is *is) +{ + int i, ret; + + for (i = 0; i < ISS_CLKS_MAX; i++) + is->clocks[i] = ERR_PTR(-EINVAL); + + for (i = 0; i < ISS_CLKS_MAX; i++) { + is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]); + if (IS_ERR(is->clocks[i])) { + ret = PTR_ERR(is->clocks[i]); + goto err; + } + ret = clk_prepare(is->clocks[i]); + if (ret < 0) { + clk_put(is->clocks[i]); + is->clocks[i] = ERR_PTR(-EINVAL); + goto err; + } + } + + return 0; +err: + fimc_is_put_clocks(is); + dev_err(&is->pdev->dev, "failed to get clock: %s\n", + fimc_is_clocks[i]); + return -ENXIO; +} + +static int fimc_is_setup_clocks(struct fimc_is *is) +{ + int ret; + + ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200], + is->clocks[ISS_CLK_ACLK200_DIV]); + if (ret < 0) + return ret; + + ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP], + is->clocks[ISS_CLK_ACLK400MCUISP_DIV]); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY); + if (ret < 0) + return ret; + + ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0], + ATCLK_MCUISP_FREQUENCY); + if (ret < 0) + return ret; + + return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1], + ATCLK_MCUISP_FREQUENCY); +} + +int fimc_is_enable_clocks(struct fimc_is *is) +{ + int i, ret; + + for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { + if (IS_ERR(is->clocks[i])) + continue; + ret = clk_enable(is->clocks[i]); + if (ret < 0) { + dev_err(&is->pdev->dev, "clock %s enable failed\n", + fimc_is_clocks[i]); + for (--i; i >= 0; i--) + clk_disable(is->clocks[i]); + return ret; + } + pr_debug("enabled clock: %s\n", fimc_is_clocks[i]); + } + return 0; +} + +void fimc_is_disable_clocks(struct fimc_is *is) +{ + int i; + + for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { + if (!IS_ERR(is->clocks[i])) { + clk_disable(is->clocks[i]); + pr_debug("disabled clock: %s\n", fimc_is_clocks[i]); + } + } +} + +static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, + struct device_node *np) +{ + u32 tmp = 0; + int ret; + + np = v4l2_of_get_next_endpoint(np, NULL); + if (!np) + return -ENXIO; + np = v4l2_of_get_remote_port(np); + if (!np) + return -ENXIO; + + /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ + ret = of_property_read_u32(np, "reg", &tmp); + sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; + + return ret; +} + +static int fimc_is_register_subdevs(struct fimc_is *is) +{ + struct device_node *adapter, *child; + int ret; + + ret = fimc_isp_subdev_create(&is->isp); + if (ret < 0) + return ret; + + for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { + if (!of_find_device_by_node(adapter)) { + of_node_put(adapter); + return -EPROBE_DEFER; + } + + for_each_available_child_of_node(adapter, child) { + struct i2c_client *client; + struct v4l2_subdev *sd; + + client = of_find_i2c_device_by_node(child); + if (!client) + goto e_retry; + + sd = i2c_get_clientdata(client); + if (!sd) + goto e_retry; + + /* FIXME: Add support for multiple sensors. */ + if (WARN_ON(is->sensor)) + continue; + + is->sensor = sd_to_fimc_is_sensor(sd); + + if (fimc_is_parse_sensor_config(is->sensor, child)) { + dev_warn(&is->pdev->dev, "DT parse error: %s\n", + child->full_name); + } + pr_debug("%s(): registered subdev: %p\n", + __func__, sd->name); + } + } + return 0; + +e_retry: + of_node_put(child); + return -EPROBE_DEFER; +} + +static int fimc_is_unregister_subdevs(struct fimc_is *is) +{ + fimc_isp_subdev_destroy(&is->isp); + is->sensor = NULL; + return 0; +} + +static int fimc_is_load_setfile(struct fimc_is *is, char *file_name) +{ + const struct firmware *fw; + void *buf; + int ret; + + ret = request_firmware(&fw, file_name, &is->pdev->dev); + if (ret < 0) { + dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret); + return ret; + } + buf = is->memory.vaddr + is->setfile.base; + memcpy(buf, fw->data, fw->size); + fimc_is_mem_barrier(); + is->setfile.size = fw->size; + + pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf); + + memcpy(is->fw.setfile_info, + fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN, + FIMC_IS_SETFILE_INFO_LEN - 1); + + is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0'; + is->setfile.state = 1; + + pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n", + is->setfile.base, fw->size); + + release_firmware(fw); + return ret; +} + +int fimc_is_cpu_set_power(struct fimc_is *is, int on) +{ + unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT; + + if (on) { + /* Disable watchdog */ + mcuctl_write(0, is, REG_WDT_ISP); + + /* Cortex-A5 start address setting */ + mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR); + + /* Enable and start Cortex-A5 */ + pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION); + pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION); + } else { + /* A5 power off */ + pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION); + pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION); + + while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) { + if (timeout == 0) + return -ETIME; + timeout--; + udelay(1); + } + } + + return 0; +} + +/* Wait until @bit of @is->state is set to @state in the interrupt handler. */ +int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, + unsigned int state, unsigned int timeout) +{ + + int ret = wait_event_timeout(is->irq_queue, + !state ^ test_bit(bit, &is->state), + timeout); + if (ret == 0) { + dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__); + return -ETIME; + } + return 0; +} + +int fimc_is_start_firmware(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + int ret; + + memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size); + wmb(); + + ret = fimc_is_cpu_set_power(is, 1); + if (ret < 0) + return ret; + + ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1, + msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT)); + if (ret < 0) + dev_err(dev, "FIMC-IS CPU power on failed\n"); + + return ret; +} + +/* Allocate working memory for the FIMC-IS CPU. */ +static int fimc_is_alloc_cpu_memory(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + + is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE, + &is->memory.paddr, GFP_KERNEL); + if (is->memory.vaddr == NULL) + return -ENOMEM; + + is->memory.size = FIMC_IS_CPU_MEM_SIZE; + memset(is->memory.vaddr, 0, is->memory.size); + + dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr); + + if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) { + dev_err(dev, "invalid firmware memory alignment: %#x\n", + (u32)is->memory.paddr); + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, + is->memory.paddr); + return -EIO; + } + + is->is_p_region = (struct is_region *)(is->memory.vaddr + + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE); + + is->is_dma_p_region = is->memory.paddr + + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE; + + is->is_shared_region = (struct is_share_region *)(is->memory.vaddr + + FIMC_IS_SHARED_REGION_OFFSET); + return 0; +} + +static void fimc_is_free_cpu_memory(struct fimc_is *is) +{ + struct device *dev = &is->pdev->dev; + + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, + is->memory.paddr); +} + +static void fimc_is_load_firmware(const struct firmware *fw, void *context) +{ + struct fimc_is *is = context; + struct device *dev = &is->pdev->dev; + void *buf; + int ret; + + if (fw == NULL) { + dev_err(dev, "firmware request failed\n"); + return; + } + mutex_lock(&is->lock); + + if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) { + dev_err(dev, "wrong firmware size: %d\n", fw->size); + goto done; + } + + is->fw.size = fw->size; + + ret = fimc_is_alloc_cpu_memory(is); + if (ret < 0) { + dev_err(dev, "failed to allocate FIMC-IS CPU memory\n"); + goto done; + } + + memcpy(is->memory.vaddr, fw->data, fw->size); + wmb(); + + /* Read firmware description. */ + buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN); + memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN); + is->fw.info[FIMC_IS_FW_INFO_LEN] = 0; + + buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN); + memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN); + is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0; + + is->fw.state = 1; + + dev_info(dev, "loaded firmware: %s, rev. %s\n", + is->fw.info, is->fw.version); + dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr); + + is->is_shared_region->chip_id = 0xe4412; + is->is_shared_region->chip_rev_no = 1; + + fimc_is_mem_barrier(); + + /* + * FIXME: The firmware is not being released for now, as it is + * needed around for copying to the IS working memory every + * time before the Cortex-A5 is restarted. + */ + if (is->fw.f_w) + release_firmware(is->fw.f_w); + is->fw.f_w = fw; +done: + mutex_unlock(&is->lock); +} + +static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name) +{ + return request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev, + GFP_KERNEL, is, fimc_is_load_firmware); +} + +/* General IS interrupt handler */ +static void fimc_is_general_irq_handler(struct fimc_is *is) +{ + is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10)); + + switch (is->i2h_cmd.cmd) { + case IHC_GET_SENSOR_NUM: + fimc_is_hw_get_params(is, 1); + fimc_is_hw_wait_intmsr0_intmsd0(is); + fimc_is_hw_set_sensor_num(is); + pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]); + break; + case IHC_SET_FACE_MARK: + case IHC_FRAME_DONE: + fimc_is_hw_get_params(is, 2); + break; + case IHC_SET_SHOT_MARK: + case IHC_AA_DONE: + case IH_REPLY_DONE: + fimc_is_hw_get_params(is, 3); + break; + case IH_REPLY_NOT_DONE: + fimc_is_hw_get_params(is, 4); + break; + case IHC_NOT_READY: + break; + default: + pr_info("unknown command: %#x\n", is->i2h_cmd.cmd); + } + + fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL); + + switch (is->i2h_cmd.cmd) { + case IHC_GET_SENSOR_NUM: + fimc_is_hw_set_intgr0_gd0(is); + set_bit(IS_ST_A5_PWR_ON, &is->state); + break; + + case IHC_SET_SHOT_MARK: + break; + + case IHC_SET_FACE_MARK: + is->fd_header.count = is->i2h_cmd.args[0]; + is->fd_header.index = is->i2h_cmd.args[1]; + is->fd_header.offset = 0; + break; + + case IHC_FRAME_DONE: + break; + + case IHC_AA_DONE: + pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0], + is->i2h_cmd.args[1], is->i2h_cmd.args[2]); + break; + + case IH_REPLY_DONE: + pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]); + + switch (is->i2h_cmd.args[0]) { + case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO: + /* Get CAC margin */ + set_bit(IS_ST_CHANGE_MODE, &is->state); + is->isp.cac_margin_x = is->i2h_cmd.args[1]; + is->isp.cac_margin_y = is->i2h_cmd.args[2]; + pr_debug("CAC margin (x,y): (%d,%d)\n", + is->isp.cac_margin_x, is->isp.cac_margin_y); + break; + + case HIC_STREAM_ON: + clear_bit(IS_ST_STREAM_OFF, &is->state); + set_bit(IS_ST_STREAM_ON, &is->state); + break; + + case HIC_STREAM_OFF: + clear_bit(IS_ST_STREAM_ON, &is->state); + set_bit(IS_ST_STREAM_OFF, &is->state); + break; + + case HIC_SET_PARAMETER: + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + pr_debug("HIC_SET_PARAMETER\n"); + break; + + case HIC_GET_PARAMETER: + break; + + case HIC_SET_TUNE: + break; + + case HIC_GET_STATUS: + break; + + case HIC_OPEN_SENSOR: + set_bit(IS_ST_OPEN_SENSOR, &is->state); + pr_debug("data lanes: %d, settle line: %d\n", + is->i2h_cmd.args[2], is->i2h_cmd.args[1]); + break; + + case HIC_CLOSE_SENSOR: + clear_bit(IS_ST_OPEN_SENSOR, &is->state); + is->sensor_index = 0; + break; + + case HIC_MSG_TEST: + pr_debug("config MSG level completed\n"); + break; + + case HIC_POWER_DOWN: + clear_bit(IS_ST_PWR_SUBIP_ON, &is->state); + break; + + case HIC_GET_SET_FILE_ADDR: + is->setfile.base = is->i2h_cmd.args[1]; + set_bit(IS_ST_SETFILE_LOADED, &is->state); + break; + + case HIC_LOAD_SET_FILE: + set_bit(IS_ST_SETFILE_LOADED, &is->state); + break; + } + break; + + case IH_REPLY_NOT_DONE: + pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0], + is->i2h_cmd.args[1], + fimc_is_strerr(is->i2h_cmd.args[1])); + + if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG) + pr_err("IS_ERROR_TIME_OUT\n"); + + switch (is->i2h_cmd.args[1]) { + case IS_ERROR_SET_PARAMETER: + fimc_is_mem_barrier(); + } + + switch (is->i2h_cmd.args[0]) { + case HIC_SET_PARAMETER: + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); + break; + } + break; + + case IHC_NOT_READY: + pr_err("IS control sequence error: Not Ready\n"); + break; + } + + wake_up(&is->irq_queue); +} + +static irqreturn_t fimc_is_irq_handler(int irq, void *priv) +{ + struct fimc_is *is = priv; + unsigned long flags; + u32 status; + + spin_lock_irqsave(&is->slock, flags); + status = mcuctl_read(is, MCUCTL_REG_INTSR1); + + if (status & (1UL << FIMC_IS_INT_GENERAL)) + fimc_is_general_irq_handler(is); + + if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP)) + fimc_isp_irq_handler(is); + + spin_unlock_irqrestore(&is->slock, flags); + return IRQ_HANDLED; +} + +static int fimc_is_hw_open_sensor(struct fimc_is *is, + struct fimc_is_sensor *sensor) +{ + struct sensor_open_extended *soe = (void *)&is->is_p_region->shared; + + fimc_is_hw_wait_intmsr0_intmsd0(is); + + soe->self_calibration_mode = 1; + soe->actuator_type = 0; + soe->mipi_lane_num = 0; + soe->mclk = 0; + soe->mipi_speed = 0; + soe->fast_open_sensor = 0; + soe->i2c_sclk = 88000000; + + fimc_is_mem_barrier(); + + mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0)); + mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); + mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2)); + mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3)); + mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4)); + + fimc_is_hw_set_intgr0_gd0(is); + + return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, + FIMC_IS_SENSOR_OPEN_TIMEOUT); +} + + +int fimc_is_hw_initialize(struct fimc_is *is) +{ + const int config_ids[] = { + IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO, + IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO + }; + struct device *dev = &is->pdev->dev; + u32 prev_id; + int i, ret; + + /* Sensor initialization. */ + ret = fimc_is_hw_open_sensor(is, is->sensor); + if (ret < 0) + return ret; + + /* Get the setfile address. */ + fimc_is_hw_get_setfile_addr(is); + + ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "get setfile address timed out\n"); + return ret; + } + pr_debug("setfile.base: %#x\n", is->setfile.base); + + /* Load the setfile. */ + fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3); + clear_bit(IS_ST_SETFILE_LOADED, &is->state); + fimc_is_hw_load_setfile(is); + ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "loading setfile timed out\n"); + return ret; + } + + pr_debug("setfile: base: %#x, size: %d\n", + is->setfile.base, is->setfile.size); + pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info); + + /* Check magic number. */ + if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] != + FIMC_IS_MAGIC_NUMBER) { + dev_err(dev, "magic number error!\n"); + return -EIO; + } + + pr_debug("shared region: %#x, parameter region: %#x\n", + is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, + is->is_dma_p_region); + + is->setfile.sub_index = 0; + + /* Stream off. */ + fimc_is_hw_stream_off(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + dev_err(dev, "stream off timeout\n"); + return ret; + } + + /* Preserve previous mode. */ + prev_id = is->config_index; + + /* Set initial parameter values. */ + for (i = 0; i < ARRAY_SIZE(config_ids); i++) { + is->config_index = config_ids[i]; + fimc_is_set_initial_params(is); + ret = fimc_is_itf_s_param(is, true); + if (ret < 0) { + is->config_index = prev_id; + return ret; + } + } + is->config_index = prev_id; + + set_bit(IS_ST_INIT_DONE, &is->state); + dev_info(dev, "initialization sequence completed (%d)\n", + is->config_index); + return 0; +} + +static int fimc_is_log_show(struct seq_file *s, void *data) +{ + struct fimc_is *is = s->private; + const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; + + if (is->memory.vaddr == NULL) { + dev_err(&is->pdev->dev, "firmware memory is not initialized\n"); + return -EIO; + } + + seq_printf(s, "%s\n", buf); + return 0; +} + +static int fimc_is_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, fimc_is_log_show, inode->i_private); +} + +static const struct file_operations fimc_is_debugfs_fops = { + .open = fimc_is_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void fimc_is_debugfs_remove(struct fimc_is *is) +{ + debugfs_remove_recursive(is->debugfs_entry); + is->debugfs_entry = NULL; +} + +static int fimc_is_debugfs_create(struct fimc_is *is) +{ + struct dentry *dentry; + + is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); + + dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, + is, &fimc_is_debugfs_fops); + if (!dentry) + fimc_is_debugfs_remove(is); + + return is->debugfs_entry == NULL ? -EIO : 0; +} + +static int fimc_is_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimc_is *is; + struct resource res; + struct device_node *node; + int ret; + + is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL); + if (!is) + return -ENOMEM; + + is->pdev = pdev; + is->isp.pdev = pdev; + + init_waitqueue_head(&is->irq_queue); + spin_lock_init(&is->slock); + mutex_init(&is->lock); + + ret = of_address_to_resource(dev->of_node, 0, &res); + if (ret < 0) + return ret; + + is->regs = devm_ioremap_resource(dev, &res); + if (IS_ERR(is->regs)) + return PTR_ERR(is->regs); + + node = of_get_child_by_name(dev->of_node, "pmu"); + if (!node) + return -ENODEV; + + is->pmu_regs = of_iomap(node, 0); + if (!is->pmu_regs) + return -ENOMEM; + + is->irq = irq_of_parse_and_map(dev->of_node, 0); + if (is->irq < 0) { + dev_err(dev, "no irq found\n"); + return is->irq; + } + + ret = fimc_is_get_clocks(is); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, is); + + ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_clk; + } + pm_runtime_enable(dev); + /* + * Enable only the ISP power domain, keep FIMC-IS clocks off until + * the whole clock tree is configured. The ISP power domain needs + * be active in order to acces any CMU_ISP clock registers. + */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_irq; + + ret = fimc_is_setup_clocks(is); + pm_runtime_put_sync(dev); + + if (ret < 0) + goto err_irq; + + is->clk_init = true; + + is->alloc_ctx = vb2_dma_contig_init_ctx(dev); + if (IS_ERR(is->alloc_ctx)) { + ret = PTR_ERR(is->alloc_ctx); + goto err_irq; + } + /* + * Register FIMC-IS V4L2 subdevs to this driver. The video nodes + * will be created within the subdev's registered() callback. + */ + ret = fimc_is_register_subdevs(is); + if (ret < 0) + goto err_vb; + + ret = fimc_is_debugfs_create(is); + if (ret < 0) + goto err_sd; + + ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME); + if (ret < 0) + goto err_dfs; + + dev_dbg(dev, "FIMC-IS registered successfully\n"); + return 0; + +err_dfs: + fimc_is_debugfs_remove(is); +err_vb: + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); +err_sd: + fimc_is_unregister_subdevs(is); +err_irq: + free_irq(is->irq, is); +err_clk: + fimc_is_put_clocks(is); + return ret; +} + +static int fimc_is_runtime_resume(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + if (!is->clk_init) + return 0; + + return fimc_is_enable_clocks(is); +} + +static int fimc_is_runtime_suspend(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + if (is->clk_init) + fimc_is_disable_clocks(is); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_is_resume(struct device *dev) +{ + /* TODO: */ + return 0; +} + +static int fimc_is_suspend(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + + /* TODO: */ + if (test_bit(IS_ST_A5_PWR_ON, &is->state)) + return -EBUSY; + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static int fimc_is_remove(struct platform_device *pdev) +{ + struct fimc_is *is = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + free_irq(is->irq, is); + fimc_is_unregister_subdevs(is); + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + fimc_is_put_clocks(is); + fimc_is_debugfs_remove(is); + release_firmware(is->fw.f_w); + fimc_is_free_cpu_memory(is); + + return 0; +} + +static const struct of_device_id fimc_is_of_match[] = { + { .compatible = "samsung,exynos4212-fimc-is" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, fimc_is_of_match); + +static const struct dev_pm_ops fimc_is_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume) + SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume, + NULL) +}; + +static struct platform_driver fimc_is_driver = { + .probe = fimc_is_probe, + .remove = fimc_is_remove, + .driver = { + .of_match_table = fimc_is_of_match, + .name = FIMC_IS_DRV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_pm_ops, + } +}; + +static int fimc_is_module_init(void) +{ + int ret; + + ret = fimc_is_register_sensor_driver(); + if (ret < 0) + return ret; + + ret = fimc_is_register_i2c_driver(); + if (ret < 0) + goto err_sens; + + ret = platform_driver_register(&fimc_is_driver); + if (!ret) + return ret; + + fimc_is_unregister_i2c_driver(); +err_sens: + fimc_is_unregister_sensor_driver(); + return ret; +} + +static void fimc_is_module_exit(void) +{ + fimc_is_unregister_sensor_driver(); + fimc_is_unregister_i2c_driver(); + platform_driver_unregister(&fimc_is_driver); +} + +module_init(fimc_is_module_init); +module_exit(fimc_is_module_exit); + +MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); +MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h new file mode 100644 index 000000000000..f5275a5b0156 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -0,0 +1,345 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Younghwan Joo <yhwan.joo@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_IS_H_ +#define FIMC_IS_H_ + +#include <asm/barrier.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <media/videobuf2-core.h> +#include <media/v4l2-ctrls.h> + +#include "fimc-isp.h" +#include "fimc-is-command.h" +#include "fimc-is-sensor.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" + +#define FIMC_IS_DRV_NAME "exynos4-fimc-is" + +#define FIMC_IS_FW_FILENAME "fimc_is_fw.bin" +#define FIMC_IS_SETFILE_6A3 "setfile.bin" + +#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ +#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ + +#define FIMC_IS_SENSOR_NUM 2 + +/* Memory definitions */ +#define FIMC_IS_CPU_MEM_SIZE (0xa00000) +#define FIMC_IS_CPU_BASE_MASK ((1 << 26) - 1) +#define FIMC_IS_REGION_SIZE 0x5000 + +#define FIMC_IS_DEBUG_REGION_OFFSET 0x0084b000 +#define FIMC_IS_SHARED_REGION_OFFSET 0x008c0000 +#define FIMC_IS_FW_INFO_LEN 31 +#define FIMC_IS_FW_VER_LEN 7 +#define FIMC_IS_FW_DESC_LEN (FIMC_IS_FW_INFO_LEN + \ + FIMC_IS_FW_VER_LEN) +#define FIMC_IS_SETFILE_INFO_LEN 39 + +#define FIMC_IS_EXTRA_MEM_SIZE (FIMC_IS_EXTRA_FW_SIZE + \ + FIMC_IS_EXTRA_SETFILE_SIZE + 0x1000) +#define FIMC_IS_EXTRA_FW_SIZE 0x180000 +#define FIMC_IS_EXTRA_SETFILE_SIZE 0x4b000 + +/* TODO: revisit */ +#define FIMC_IS_FW_ADDR_MASK ((1 << 26) - 1) +#define FIMC_IS_FW_SIZE_MAX (SZ_4M) +#define FIMC_IS_FW_SIZE_MIN (SZ_32K) + +#define ATCLK_MCUISP_FREQUENCY 100000000UL +#define ACLK_AXI_FREQUENCY 100000000UL + +enum { + ISS_CLK_PPMUISPX, + ISS_CLK_PPMUISPMX, + ISS_CLK_LITE0, + ISS_CLK_LITE1, + ISS_CLK_MPLL, + ISS_CLK_SYSREG, + ISS_CLK_ISP, + ISS_CLK_DRC, + ISS_CLK_FD, + ISS_CLK_MCUISP, + ISS_CLK_UART, + ISS_GATE_CLKS_MAX, + ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX, + ISS_CLK_ISP_DIV1, + ISS_CLK_MCUISP_DIV0, + ISS_CLK_MCUISP_DIV1, + ISS_CLK_ACLK200, + ISS_CLK_ACLK200_DIV, + ISS_CLK_ACLK400MCUISP, + ISS_CLK_ACLK400MCUISP_DIV, + ISS_CLKS_MAX +}; + +/* The driver's internal state flags */ +enum { + IS_ST_IDLE, + IS_ST_PWR_ON, + IS_ST_A5_PWR_ON, + IS_ST_FW_LOADED, + IS_ST_OPEN_SENSOR, + IS_ST_SETFILE_LOADED, + IS_ST_INIT_DONE, + IS_ST_STREAM_ON, + IS_ST_STREAM_OFF, + IS_ST_CHANGE_MODE, + IS_ST_BLOCK_CMD_CLEARED, + IS_ST_SET_ZOOM, + IS_ST_PWR_SUBIP_ON, + IS_ST_END, +}; + +enum af_state { + FIMC_IS_AF_IDLE = 0, + FIMC_IS_AF_SETCONFIG = 1, + FIMC_IS_AF_RUNNING = 2, + FIMC_IS_AF_LOCK = 3, + FIMC_IS_AF_ABORT = 4, + FIMC_IS_AF_FAILED = 5, +}; + +enum af_lock_state { + FIMC_IS_AF_UNLOCKED = 0, + FIMC_IS_AF_LOCKED = 2 +}; + +enum ae_lock_state { + FIMC_IS_AE_UNLOCKED = 0, + FIMC_IS_AE_LOCKED = 1 +}; + +enum awb_lock_state { + FIMC_IS_AWB_UNLOCKED = 0, + FIMC_IS_AWB_LOCKED = 1 +}; + +enum { + IS_METERING_CONFIG_CMD, + IS_METERING_CONFIG_WIN_POS_X, + IS_METERING_CONFIG_WIN_POS_Y, + IS_METERING_CONFIG_WIN_WIDTH, + IS_METERING_CONFIG_WIN_HEIGHT, + IS_METERING_CONFIG_MAX +}; + +struct is_setfile { + const struct firmware *info; + int state; + u32 sub_index; + u32 base; + size_t size; +}; + +struct is_fd_result_header { + u32 offset; + u32 count; + u32 index; + u32 curr_index; + u32 width; + u32 height; +}; + +struct is_af_info { + u16 mode; + u32 af_state; + u32 af_lock_state; + u32 ae_lock_state; + u32 awb_lock_state; + u16 pos_x; + u16 pos_y; + u16 prev_pos_x; + u16 prev_pos_y; + u16 use_af; +}; + +struct fimc_is_firmware { + const struct firmware *f_w; + + dma_addr_t paddr; + void *vaddr; + unsigned int size; + + char info[FIMC_IS_FW_INFO_LEN + 1]; + char version[FIMC_IS_FW_VER_LEN + 1]; + char setfile_info[FIMC_IS_SETFILE_INFO_LEN + 1]; + u8 state; +}; + +struct fimc_is_memory { + /* physical base address */ + dma_addr_t paddr; + /* virtual base address */ + void *vaddr; + /* total length */ + unsigned int size; +}; + +#define FIMC_IS_I2H_MAX_ARGS 12 + +struct i2h_cmd { + u32 cmd; + u32 sensor_id; + u16 num_args; + u32 args[FIMC_IS_I2H_MAX_ARGS]; +}; + +struct h2i_cmd { + u16 cmd_type; + u32 entry_id; +}; + +#define FIMC_IS_DEBUG_MSG 0x3f +#define FIMC_IS_DEBUG_LEVEL 3 + +struct fimc_is_setfile { + const struct firmware *info; + unsigned int state; + unsigned int size; + u32 sub_index; + u32 base; +}; + +struct chain_config { + struct global_param global; + struct sensor_param sensor; + struct isp_param isp; + struct drc_param drc; + struct fd_param fd; + + unsigned long p_region_index1; + unsigned long p_region_index2; +}; + +/** + * struct fimc_is - fimc-is data structure + * @pdev: pointer to FIMC-IS platform device + * @pctrl: pointer to pinctrl structure for this device + * @v4l2_dev: pointer to top the level v4l2_device + * @alloc_ctx: videobuf2 memory allocator context + * @lock: mutex serializing video device and the subdev operations + * @slock: spinlock protecting this data structure and the hw registers + * @clocks: FIMC-LITE gate clock + * @regs: MCUCTL mmapped registers region + * @pmu_regs: PMU ISP mmapped registers region + * @irq_queue: interrupt handling waitqueue + * @lpm: low power mode flag + * @state: internal driver's state flags + */ +struct fimc_is { + struct platform_device *pdev; + struct pinctrl *pctrl; + struct v4l2_device *v4l2_dev; + + struct fimc_is_firmware fw; + struct fimc_is_memory memory; + struct firmware *f_w; + + struct fimc_isp isp; + struct fimc_is_sensor *sensor; + struct fimc_is_setfile setfile; + + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_ctrl_handler ctrl_handler; + + struct mutex lock; + spinlock_t slock; + + struct clk *clocks[ISS_CLKS_MAX]; + bool clk_init; + void __iomem *regs; + void __iomem *pmu_regs; + int irq; + wait_queue_head_t irq_queue; + u8 lpm; + + unsigned long state; + unsigned int sensor_index; + + struct i2h_cmd i2h_cmd; + struct h2i_cmd h2i_cmd; + struct is_fd_result_header fd_header; + + struct chain_config config[IS_SC_MAX]; + unsigned config_index; + + struct is_region *is_p_region; + dma_addr_t is_dma_p_region; + struct is_share_region *is_shared_region; + struct is_af_info af; + + struct dentry *debugfs_entry; +}; + +static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp) +{ + return container_of(isp, struct fimc_is, isp); +} + +static inline void fimc_is_mem_barrier(void) +{ + mb(); +} + +static inline void fimc_is_set_param_bit(struct fimc_is *is, int num) +{ + struct chain_config *cfg = &is->config[is->config_index]; + + if (num >= 32) + set_bit(num - 32, &cfg->p_region_index2); + else + set_bit(num, &cfg->p_region_index1); +} + +static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd) +{ + is->is_p_region->parameter.isp.control.cmd = cmd; +} + +static inline void mcuctl_write(u32 v, struct fimc_is *is, unsigned int offset) +{ + writel(v, is->regs + offset); +} + +static inline u32 mcuctl_read(struct fimc_is *is, unsigned int offset) +{ + return readl(is->regs + offset); +} + +static inline void pmuisp_write(u32 v, struct fimc_is *is, unsigned int offset) +{ + writel(v, is->pmu_regs + offset); +} + +static inline u32 pmuisp_read(struct fimc_is *is, unsigned int offset) +{ + return readl(is->pmu_regs + offset); +} + +int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, + unsigned int state, unsigned int timeout); +int fimc_is_cpu_set_power(struct fimc_is *is, int on); +int fimc_is_start_firmware(struct fimc_is *is); +int fimc_is_hw_initialize(struct fimc_is *is); +void fimc_is_log_dump(const char *level, const void *buf, size_t len); + +#endif /* FIMC_IS_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c new file mode 100644 index 000000000000..d63947f7b302 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -0,0 +1,703 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/printk.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/v4l2-device.h> + +#include "media-dev.h" +#include "fimc-is-command.h" +#include "fimc-is-param.h" +#include "fimc-is-regs.h" +#include "fimc-is.h" + +static int debug; +module_param_named(debug_isp, debug, int, S_IRUGO | S_IWUSR); + +static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = { + { + .name = "RAW8 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = { 8 }, + .color = FIMC_FMT_RAW8, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + }, { + .name = "RAW10 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG10, + .depth = { 10 }, + .color = FIMC_FMT_RAW10, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + }, { + .name = "RAW12 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG12, + .depth = { 12 }, + .color = FIMC_FMT_RAW12, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + }, +}; + +/** + * fimc_isp_find_format - lookup color format by fourcc or media bus code + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + * @index: index to the fimc_isp_formats array, ignored if negative + */ +const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index) +{ + const struct fimc_fmt *fmt, *def_fmt = NULL; + unsigned int i; + int id = 0; + + if (index >= (int)ARRAY_SIZE(fimc_isp_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(fimc_isp_formats); ++i) { + fmt = &fimc_isp_formats[i]; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == id) + def_fmt = fmt; + id++; + } + return def_fmt; +} + +void fimc_isp_irq_handler(struct fimc_is *is) +{ + is->i2h_cmd.args[0] = mcuctl_read(is, MCUCTL_REG_ISSR(20)); + is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); + + fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); + + /* TODO: Complete ISP DMA interrupt handler */ + wake_up(&is->irq_queue); +} + +/* Capture subdev media entity operations */ +static int fimc_is_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + return 0; +} + +static const struct media_entity_operations fimc_is_subdev_media_ops = { + .link_setup = fimc_is_link_setup, +}; + +static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct fimc_fmt *fmt; + + fmt = fimc_isp_find_format(NULL, NULL, code->index); + if (!fmt) + return -EINVAL; + code->code = fmt->mbus_code; + return 0; +} + +static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct v4l2_mbus_framefmt cur_fmt; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&isp->subdev_lock); + __is_get_frame_size(is, &cur_fmt); + + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + /* full camera input frame size */ + mf->width = cur_fmt.width + FIMC_ISP_CAC_MARGIN_WIDTH; + mf->height = cur_fmt.height + FIMC_ISP_CAC_MARGIN_HEIGHT; + mf->code = V4L2_MBUS_FMT_SGRBG10_1X10; + } else { + /* crop size */ + mf->width = cur_fmt.width; + mf->height = cur_fmt.height; + mf->code = V4L2_MBUS_FMT_YUV10_1X30; + } + + mutex_unlock(&isp->subdev_lock); + + v4l2_dbg(1, debug, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", + __func__, fmt->pad, mf->code, mf->width, mf->height); + + return 0; +} + +static void __isp_subdev_try_format(struct fimc_isp *isp, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *mf = &fmt->format; + + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN, + FIMC_ISP_SINK_WIDTH_MAX, 0, + &mf->height, FIMC_ISP_SINK_HEIGHT_MIN, + FIMC_ISP_SINK_HEIGHT_MAX, 0, 0); + isp->subdev_fmt = *mf; + } else { + /* Allow changing format only on sink pad */ + mf->width = isp->subdev_fmt.width - FIMC_ISP_CAC_MARGIN_WIDTH; + mf->height = isp->subdev_fmt.height - FIMC_ISP_CAC_MARGIN_HEIGHT; + mf->code = isp->subdev_fmt.code; + } +} + +static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + struct v4l2_mbus_framefmt *mf = &fmt->format; + int ret = 0; + + v4l2_dbg(1, debug, sd, "%s: pad%d: code: 0x%x, %dx%d\n", + __func__, fmt->pad, mf->code, mf->width, mf->height); + + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&isp->subdev_lock); + __isp_subdev_try_format(isp, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + mutex_unlock(&isp->subdev_lock); + return 0; + } + + if (sd->entity.stream_count == 0) + __is_set_frame_size(is, mf); + else + ret = -EBUSY; + mutex_unlock(&isp->subdev_lock); + + return ret; +} + +static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + int ret; + + v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); + + if (!test_bit(IS_ST_INIT_DONE, &is->state)) + return -EBUSY; + + fimc_is_mem_barrier(); + + if (on) { + if (__get_pending_param_count(is)) { + ret = fimc_is_itf_s_param(is, true); + if (ret < 0) + return ret; + } + + v4l2_dbg(1, debug, sd, "changing mode to %d\n", + is->config_index); + ret = fimc_is_itf_mode_change(is); + if (ret) + return -EINVAL; + + clear_bit(IS_ST_STREAM_ON, &is->state); + fimc_is_hw_stream_on(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_ON, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "stream on timeout\n"); + return ret; + } + } else { + clear_bit(IS_ST_STREAM_OFF, &is->state); + fimc_is_hw_stream_off(is); + ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "stream off timeout\n"); + return ret; + } + is->setfile.sub_index = 0; + } + + return 0; +} + +static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_isp *isp = v4l2_get_subdevdata(sd); + struct fimc_is *is = fimc_isp_to_is(isp); + int ret = 0; + + pr_debug("on: %d\n", on); + + if (on) { + ret = pm_runtime_get_sync(&is->pdev->dev); + if (ret < 0) + return ret; + set_bit(IS_ST_PWR_ON, &is->state); + + ret = fimc_is_start_firmware(is); + if (ret < 0) { + v4l2_err(sd, "firmware booting failed\n"); + pm_runtime_put(&is->pdev->dev); + return ret; + } + set_bit(IS_ST_PWR_SUBIP_ON, &is->state); + + ret = fimc_is_hw_initialize(is); + } else { + /* Close sensor */ + if (!test_bit(IS_ST_PWR_ON, &is->state)) { + fimc_is_hw_close_sensor(is, 0); + + ret = fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 0, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "sensor close timeout\n"); + return ret; + } + } + + /* SUB IP power off */ + if (test_bit(IS_ST_PWR_SUBIP_ON, &is->state)) { + fimc_is_hw_subip_power_off(is); + ret = fimc_is_wait_event(is, IS_ST_PWR_SUBIP_ON, 0, + FIMC_IS_CONFIG_TIMEOUT); + if (ret < 0) { + v4l2_err(sd, "sub-IP power off timeout\n"); + return ret; + } + } + + fimc_is_cpu_set_power(is, 0); + pm_runtime_put_sync(&is->pdev->dev); + + clear_bit(IS_ST_PWR_ON, &is->state); + clear_bit(IS_ST_INIT_DONE, &is->state); + is->state = 0; + is->config[is->config_index].p_region_index1 = 0; + is->config[is->config_index].p_region_index2 = 0; + set_bit(IS_ST_IDLE, &is->state); + wmb(); + } + + return ret; +} + +static int fimc_isp_subdev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt fmt; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK); + + fmt.colorspace = V4L2_COLORSPACE_SRGB; + fmt.code = fimc_isp_formats[0].mbus_code; + fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + FIMC_ISP_CAC_MARGIN_WIDTH; + fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + FIMC_ISP_CAC_MARGIN_HEIGHT; + fmt.field = V4L2_FIELD_NONE; + *format = fmt; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO); + fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; + fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; + *format = fmt; + + format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA); + *format = fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { + .open = fimc_isp_subdev_open, +}; + +static const struct v4l2_subdev_pad_ops fimc_is_subdev_pad_ops = { + .enum_mbus_code = fimc_is_subdev_enum_mbus_code, + .get_fmt = fimc_isp_subdev_get_fmt, + .set_fmt = fimc_isp_subdev_set_fmt, +}; + +static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = { + .s_stream = fimc_isp_subdev_s_stream, +}; + +static const struct v4l2_subdev_core_ops fimc_is_core_ops = { + .s_power = fimc_isp_subdev_s_power, +}; + +static struct v4l2_subdev_ops fimc_is_subdev_ops = { + .core = &fimc_is_core_ops, + .video = &fimc_is_subdev_video_ops, + .pad = &fimc_is_subdev_pad_ops, +}; + +static int __ctrl_set_white_balance(struct fimc_is *is, int value) +{ + switch (value) { + case V4L2_WHITE_BALANCE_AUTO: + __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); + break; + case V4L2_WHITE_BALANCE_DAYLIGHT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_DAYLIGHT); + break; + case V4L2_WHITE_BALANCE_CLOUDY: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_CLOUDY); + break; + case V4L2_WHITE_BALANCE_INCANDESCENT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_TUNGSTEN); + break; + case V4L2_WHITE_BALANCE_FLUORESCENT: + __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, + ISP_AWB_ILLUMINATION_FLUORESCENT); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __ctrl_set_aewb_lock(struct fimc_is *is, + struct v4l2_ctrl *ctrl) +{ + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + struct isp_param *isp = &is->is_p_region->parameter.isp; + int cmd, ret; + + cmd = ae_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; + isp->aa.cmd = cmd; + isp->aa.target = ISP_AA_TARGET_AE; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + is->af.ae_lock_state = ae_lock; + wmb(); + + ret = fimc_is_itf_s_param(is, false); + if (ret < 0) + return ret; + + cmd = awb_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; + isp->aa.cmd = cmd; + isp->aa.target = ISP_AA_TARGET_AE; + fimc_is_set_param_bit(is, PARAM_ISP_AA); + is->af.awb_lock_state = awb_lock; + wmb(); + + return fimc_is_itf_s_param(is, false); +} + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + 50, 100, 200, 400, 800, +}; + +static int __ctrl_set_iso(struct fimc_is *is, int value) +{ + unsigned int idx, iso; + + if (value == V4L2_ISO_SENSITIVITY_AUTO) { + __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); + return 0; + } + idx = is->isp.ctrls.iso->val; + if (idx >= ARRAY_SIZE(iso_qmenu)) + return -EINVAL; + + iso = iso_qmenu[idx]; + __is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso); + return 0; +} + +static int __ctrl_set_metering(struct fimc_is *is, unsigned int value) +{ + unsigned int val; + + switch (value) { + case V4L2_EXPOSURE_METERING_AVERAGE: + val = ISP_METERING_COMMAND_AVERAGE; + break; + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + val = ISP_METERING_COMMAND_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + val = ISP_METERING_COMMAND_SPOT; + break; + case V4L2_EXPOSURE_METERING_MATRIX: + val = ISP_METERING_COMMAND_MATRIX; + break; + default: + return -EINVAL; + }; + + __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val); + return 0; +} + +static int __ctrl_set_afc(struct fimc_is *is, int value) +{ + switch (value) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + __is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: + __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __ctrl_set_image_effect(struct fimc_is *is, int value) +{ + static const u8 effects[][2] = { + { V4L2_COLORFX_NONE, ISP_IMAGE_EFFECT_DISABLE }, + { V4L2_COLORFX_BW, ISP_IMAGE_EFFECT_MONOCHROME }, + { V4L2_COLORFX_SEPIA, ISP_IMAGE_EFFECT_SEPIA }, + { V4L2_COLORFX_NEGATIVE, ISP_IMAGE_EFFECT_NEGATIVE_MONO }, + { 16 /* TODO */, ISP_IMAGE_EFFECT_NEGATIVE_COLOR }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(effects); i++) { + if (effects[i][0] != value) + continue; + + __is_set_isp_effect(is, effects[i][1]); + return 0; + } + + return -EINVAL; +} + +static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl); + struct fimc_is *is = fimc_isp_to_is(isp); + bool set_param = true; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, + ctrl->val); + break; + + case V4L2_CID_SATURATION: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION, + ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE_ABSOLUTE: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, + ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, + ctrl->val); + break; + + case V4L2_CID_HUE: + __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, + ctrl->val); + break; + + case V4L2_CID_EXPOSURE_METERING: + ret = __ctrl_set_metering(is, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = __ctrl_set_white_balance(is, ctrl->val); + break; + + case V4L2_CID_3A_LOCK: + ret = __ctrl_set_aewb_lock(is, ctrl); + set_param = false; + break; + + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = __ctrl_set_iso(is, ctrl->val); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = __ctrl_set_afc(is, ctrl->val); + break; + + case V4L2_CID_COLORFX: + __ctrl_set_image_effect(is, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + if (ret < 0) { + v4l2_err(&isp->subdev, "Failed to set control: %s (%d)\n", + ctrl->name, ctrl->val); + return ret; + } + + if (set_param && test_bit(IS_ST_STREAM_ON, &is->state)) + return fimc_is_itf_s_param(is, true); + + return 0; +} + +static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { + .s_ctrl = fimc_is_s_ctrl, +}; + +int fimc_isp_subdev_create(struct fimc_isp *isp) +{ + const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops; + struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; + struct v4l2_subdev *sd = &isp->subdev; + struct fimc_isp_ctrls *ctrls = &isp->ctrls; + int ret; + + mutex_init(&isp->subdev_lock); + + v4l2_subdev_init(sd, &fimc_is_subdev_ops); + sd->grp_id = GRP_ID_FIMC_IS; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); + + isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; + isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, + isp->subdev_pads, 0); + if (ret) + return ret; + + v4l2_ctrl_handler_init(handler, 20); + + ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION, + -2, 2, 1, 0); + ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS, + -4, 4, 1, 0); + ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST, + -2, 2, 1, 0); + ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS, + -2, 2, 1, 0); + ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE, + -2, 2, 1, 0); + + ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO); + + ctrls->exposure = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + -4, 4, 1, 0); + + ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_EXPOSURE_METERING, 3, + ~0xf, V4L2_EXPOSURE_METERING_AVERAGE); + + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + /* ISO sensitivity */ + ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, + V4L2_ISO_SENSITIVITY_AUTO); + + ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_3A_LOCK, 0, 0x3, 0, 0); + + /* TODO: Add support for NEGATIVE_COLOR option */ + ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SET_CBCR + 1, ~0x1000f, V4L2_COLORFX_NONE); + + if (handler->error) { + media_entity_cleanup(&sd->entity); + return handler->error; + } + + v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, + V4L2_ISO_SENSITIVITY_MANUAL, false); + + sd->ctrl_handler = handler; + sd->internal_ops = &fimc_is_subdev_internal_ops; + sd->entity.ops = &fimc_is_subdev_media_ops; + v4l2_set_subdevdata(sd, isp); + + return 0; +} + +void fimc_isp_subdev_destroy(struct fimc_isp *isp) +{ + struct v4l2_subdev *sd = &isp->subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&isp->ctrls.handler); + v4l2_set_subdevdata(sd, NULL); +} diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h new file mode 100644 index 000000000000..800aba7ab4a7 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -0,0 +1,181 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> + * Younghwan Joo <yhwan.joo@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_ISP_H_ +#define FIMC_ISP_H_ + +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/videobuf2-core.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/s5p_fimc.h> + +/* FIXME: revisit these constraints */ +#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) +#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) +#define FIMC_ISP_SOURCE_WIDTH_MIN 8 +#define FIMC_ISP_SOURC_HEIGHT_MIN 8 +#define FIMC_ISP_CAC_MARGIN_WIDTH 16 +#define FIMC_ISP_CAC_MARGIN_HEIGHT 12 + +#define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16) +#define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12) +#define FIMC_ISP_SOURCE_WIDTH_MAX 4000 +#define FIMC_ISP_SOURC_HEIGHT_MAX 4000 + +#define FIMC_ISP_NUM_FORMATS 3 +#define FIMC_ISP_REQ_BUFS_MIN 2 + +#define FIMC_ISP_SD_PAD_SINK 0 +#define FIMC_ISP_SD_PAD_SRC_FIFO 1 +#define FIMC_ISP_SD_PAD_SRC_DMA 2 +#define FIMC_ISP_SD_PADS_NUM 3 +#define FIMC_ISP_MAX_PLANES 1 + +/** + * struct fimc_isp_frame - source/target frame properties + * @width: full image width + * @height: full image height + * @rect: crop/composition rectangle + */ +struct fimc_isp_frame { + u16 width; + u16 height; + struct v4l2_rect rect; +}; + +struct fimc_isp_ctrls { + struct v4l2_ctrl_handler handler; + + /* Auto white balance */ + struct v4l2_ctrl *auto_wb; + /* Auto ISO control cluster */ + struct { + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + /* Adjust - contrast */ + struct v4l2_ctrl *contrast; + /* Adjust - saturation */ + struct v4l2_ctrl *saturation; + /* Adjust - sharpness */ + struct v4l2_ctrl *sharpness; + /* Adjust - brightness */ + struct v4l2_ctrl *brightness; + /* Adjust - hue */ + struct v4l2_ctrl *hue; + + /* Auto/manual exposure */ + struct v4l2_ctrl *auto_exp; + /* Manual exposure value */ + struct v4l2_ctrl *exposure; + /* AE/AWB lock/unlock */ + struct v4l2_ctrl *aewb_lock; + /* Exposure metering mode */ + struct v4l2_ctrl *exp_metering; + /* AFC */ + struct v4l2_ctrl *afc; + /* ISP image effect */ + struct v4l2_ctrl *colorfx; +}; + +/** + * struct fimc_is_video - fimc-is video device structure + * @vdev: video_device structure + * @type: video device type (CAPTURE/OUTPUT) + * @pad: video device media (sink) pad + * @pending_buf_q: pending buffers queue head + * @active_buf_q: a queue head of buffers scheduled in hardware + * @vb_queue: vb2 buffer queue + * @active_buf_count: number of video buffers scheduled in hardware + * @frame_count: counter of frames dequeued to user space + * @reqbufs_count: number of buffers requested with REQBUFS ioctl + * @format: current pixel format + */ +struct fimc_is_video { + struct video_device vdev; + enum v4l2_buf_type type; + struct media_pad pad; + struct list_head pending_buf_q; + struct list_head active_buf_q; + struct vb2_queue vb_queue; + unsigned int frame_count; + unsigned int reqbufs_count; + int streaming; + unsigned long payload[FIMC_ISP_MAX_PLANES]; + const struct fimc_fmt *format; +}; + +/** + * struct fimc_isp - FIMC-IS ISP data structure + * @pdev: pointer to FIMC-IS platform device + * @alloc_ctx: videobuf2 memory allocator context + * @subdev: ISP v4l2_subdev + * @subdev_pads: the ISP subdev media pads + * @ctrl_handler: v4l2 controls handler + * @test_pattern: test pattern controls + * @pipeline: video capture pipeline data structure + * @video_lock: mutex serializing video device and the subdev operations + * @fmt: pointer to color format description structure + * @payload: image size in bytes (w x h x bpp) + * @inp_frame: camera input frame structure + * @out_frame: DMA output frame structure + * @source_subdev_grp_id: group id of remote source subdev + * @cac_margin_x: horizontal CAC margin in pixels + * @cac_margin_y: vertical CAC margin in pixels + * @state: driver state flags + * @video_capture: the ISP block video capture device + */ +struct fimc_isp { + struct platform_device *pdev; + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_subdev subdev; + struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; + struct v4l2_mbus_framefmt subdev_fmt; + struct v4l2_ctrl *test_pattern; + struct fimc_isp_ctrls ctrls; + + struct mutex video_lock; + struct mutex subdev_lock; + + struct fimc_isp_frame inp_frame; + struct fimc_isp_frame out_frame; + unsigned int source_subdev_grp_id; + + unsigned int cac_margin_x; + unsigned int cac_margin_y; + + unsigned long state; + + struct fimc_is_video video_capture; +}; + +#define ctrl_to_fimc_isp(_ctrl) \ + container_of(ctrl->handler, struct fimc_isp, ctrls.handler) + +struct fimc_is; + +int fimc_isp_subdev_create(struct fimc_isp *isp); +void fimc_isp_subdev_destroy(struct fimc_isp *isp); +void fimc_isp_irq_handler(struct fimc_is *is); +int fimc_is_create_controls(struct fimc_isp *isp); +int fimc_is_delete_controls(struct fimc_isp *isp); +const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index); +#endif /* FIMC_ISP_H_ */ diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index f0af0754a7b4..8cc0d39a2fea 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -127,11 +127,11 @@ static const u32 src_pixfmt_map[8][3] = { /* Set camera input pixel format and resolution */ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) { - enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code; - unsigned int i = ARRAY_SIZE(src_pixfmt_map); + enum v4l2_mbus_pixelcode pixelcode = f->fmt->mbus_code; + int i = ARRAY_SIZE(src_pixfmt_map); u32 cfg; - while (i-- >= 0) { + while (--i >= 0) { if (src_pixfmt_map[i][0] == pixelcode) break; } @@ -224,10 +224,10 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY }, }; u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); - unsigned int i = ARRAY_SIZE(pixcode); + int i = ARRAY_SIZE(pixcode); - while (i-- >= 0) - if (pixcode[i][0] == dev->fmt->mbus_code) + while (--i >= 0) + if (pixcode[i][0] == f->fmt->mbus_code) break; cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h index 0e345844c13a..390383941c19 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h @@ -72,10 +72,10 @@ #define FLITE_REG_CIODMAFMT 0x18 #define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15) #define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) -#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4) -#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4) -#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4) -#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4) +#define FLITE_REG_CIODMAFMT_YCBYCR (0 << 4) +#define FLITE_REG_CIODMAFMT_YCRYCB (1 << 4) +#define FLITE_REG_CIODMAFMT_CBYCRY (2 << 4) +#define FLITE_REG_CIODMAFMT_CRYCBY (3 << 4) #define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4) /* Camera Output Canvas */ diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index bfc4206935c8..14bb7bc8adbe 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -11,12 +11,14 @@ #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ #include <linux/bug.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/types.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -30,7 +32,6 @@ #include <media/videobuf2-dma-contig.h> #include <media/s5p_fimc.h> -#include "fimc-mdevice.h" #include "fimc-core.h" #include "fimc-lite.h" #include "fimc-lite-reg.h" @@ -46,6 +47,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, CbYCrY", .fourcc = V4L2_PIX_FMT_UYVY, @@ -53,6 +55,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_CBYCRY422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, CrYCbY", .fourcc = V4L2_PIX_FMT_VYUY, @@ -60,6 +63,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_CRYCBY422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, @@ -67,6 +71,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, + .flags = FMT_FLAGS_YUV, }, { .name = "RAW8 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG8, @@ -74,6 +79,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW8, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + .flags = FMT_FLAGS_RAW_BAYER, }, { .name = "RAW10 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG10, @@ -81,6 +87,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW10, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + .flags = FMT_FLAGS_RAW_BAYER, }, { .name = "RAW12 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG12, @@ -88,6 +95,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .color = FIMC_FMT_RAW12, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + .flags = FMT_FLAGS_RAW_BAYER, }, }; @@ -95,10 +103,11 @@ static const struct fimc_fmt fimc_lite_formats[] = { * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code * @pixelformat: fourcc to match, ignored if null * @mbus_code: media bus code to match, ignored if null + * @mask: the color format flags to match * @index: index to the fimc_lite_formats array, ignored if negative */ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, - const u32 *mbus_code, int index) + const u32 *mbus_code, unsigned int mask, int index) { const struct fimc_fmt *fmt, *def_fmt = NULL; unsigned int i; @@ -109,6 +118,8 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { fmt = &fimc_lite_formats[i]; + if (mask && !(fmt->flags & mask)) + continue; if (pixelformat && fmt->fourcc == *pixelformat) return fmt; if (mbus_code && fmt->mbus_code == *mbus_code) @@ -120,26 +131,49 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } +/* Called with the media graph mutex held or @me stream_count > 0. */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR || + sd->grp_id == GRP_ID_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { - struct fimc_pipeline *pipeline = &fimc->pipeline; - struct v4l2_subdev *sensor; - struct fimc_sensor_info *si; + struct fimc_source_info *si; unsigned long flags; - sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; - - if (sensor == NULL) + if (fimc->sensor == NULL) return -ENXIO; - if (fimc->fmt == NULL) + if (fimc->inp_frame.fmt == NULL || fimc->out_frame.fmt == NULL) return -EINVAL; /* Get sensor configuration data from the sensor subdev */ - si = v4l2_get_subdev_hostdata(sensor); + si = v4l2_get_subdev_hostdata(fimc->sensor); + if (!si) + return -EINVAL; + spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &si->pdata); + flite_hw_set_camera_bus(fimc, si); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); @@ -339,13 +373,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, const struct v4l2_pix_format_mplane *pixm = NULL; struct fimc_lite *fimc = vq->drv_priv; struct flite_frame *frame = &fimc->out_frame; - const struct fimc_fmt *fmt = fimc->fmt; + const struct fimc_fmt *fmt = frame->fmt; unsigned long wh; int i; if (pfmt) { pixm = &pfmt->fmt.pix_mp; - fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1); + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0, -1); wh = pixm->width * pixm->height; } else { wh = frame->f_width * frame->f_height; @@ -374,10 +408,10 @@ static int buffer_prepare(struct vb2_buffer *vb) struct fimc_lite *fimc = vq->drv_priv; int i; - if (fimc->fmt == NULL) + if (fimc->out_frame.fmt == NULL) return -EINVAL; - for (i = 0; i < fimc->fmt->memplanes; i++) { + for (i = 0; i < fimc->out_frame.fmt->memplanes; i++) { unsigned long size = fimc->payload[i]; if (vb2_plane_size(vb, i) < size) { @@ -425,24 +459,12 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); } -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_lite *fimc = vb2_get_drv_priv(vq); - mutex_lock(&fimc->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_lite *fimc = vb2_get_drv_priv(vq); - mutex_unlock(&fimc->lock); -} - static const struct vb2_ops fimc_lite_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -467,99 +489,73 @@ static int fimc_lite_open(struct file *file) mutex_lock(&fimc->lock); if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { ret = -EBUSY; - goto done; + goto unlock; } set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) - goto done; + goto unlock; ret = v4l2_fh_open(file); if (ret < 0) - goto done; + goto err_pm; - if (++fimc->ref_count == 1 && - atomic_read(&fimc->out_path) == FIMC_IO_DMA) { - ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, - &fimc->vfd.entity, true); - if (ret < 0) { - pm_runtime_put_sync(&fimc->pdev->dev); - fimc->ref_count--; - v4l2_fh_release(file); - clear_bit(ST_FLITE_IN_USE, &fimc->state); - } + if (!v4l2_fh_is_singular_file(file) || + atomic_read(&fimc->out_path) != FIMC_IO_DMA) + goto unlock; + ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, + me, true); + if (!ret) { fimc_lite_clear_event_counters(fimc); + fimc->ref_count++; + goto unlock; } -done: + + v4l2_fh_release(file); +err_pm: + pm_runtime_put_sync(&fimc->pdev->dev); + clear_bit(ST_FLITE_IN_USE, &fimc->state); +unlock: mutex_unlock(&fimc->lock); mutex_unlock(&me->parent->graph_mutex); return ret; } -static int fimc_lite_close(struct file *file) +static int fimc_lite_release(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); - int ret; mutex_lock(&fimc->lock); - if (--fimc->ref_count == 0 && + if (v4l2_fh_is_singular_file(file) && atomic_read(&fimc->out_path) == FIMC_IO_DMA) { + if (fimc->streaming) { + media_entity_pipeline_stop(&fimc->vfd.entity); + fimc->streaming = false; + } clear_bit(ST_FLITE_IN_USE, &fimc->state); fimc_lite_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); - clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + fimc->ref_count--; } + vb2_fop_release(file); pm_runtime_put(&fimc->pdev->dev); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); - if (fimc->ref_count == 0) - vb2_queue_release(&fimc->vb_queue); - - ret = v4l2_fh_release(file); - - mutex_unlock(&fimc->lock); - return ret; -} - -static unsigned int fimc_lite_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_lite *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return POLL_ERR; - - ret = vb2_poll(&fimc->vb_queue, file, wait); - mutex_unlock(&fimc->lock); - - return ret; -} - -static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_lite *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = vb2_mmap(&fimc->vb_queue, vma); mutex_unlock(&fimc->lock); - - return ret; + return 0; } static const struct v4l2_file_operations fimc_lite_fops = { .owner = THIS_MODULE, .open = fimc_lite_open, - .release = fimc_lite_close, - .poll = fimc_lite_poll, + .release = fimc_lite_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = fimc_lite_mmap, + .mmap = vb2_fop_mmap, }; /* @@ -570,10 +566,23 @@ static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, u32 *width, u32 *height, u32 *code, u32 *fourcc, int pad) { - struct flite_variant *variant = fimc->variant; + struct flite_drvdata *dd = fimc->dd; const struct fimc_fmt *fmt; + unsigned int flags = 0; - fmt = fimc_lite_find_format(fourcc, code, 0); + if (pad == FLITE_SD_PAD_SINK) { + v4l_bound_align_image(width, 8, dd->max_width, + ffs(dd->out_width_align) - 1, + height, 0, dd->max_height, 0, 0); + } else { + v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, + ffs(dd->out_width_align) - 1, + height, 0, fimc->inp_frame.rect.height, + 0, 0); + flags = fimc->inp_frame.fmt->flags; + } + + fmt = fimc_lite_find_format(fourcc, code, flags, 0); if (WARN_ON(!fmt)) return NULL; @@ -582,17 +591,6 @@ static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, if (fourcc) *fourcc = fmt->fourcc; - if (pad == FLITE_SD_PAD_SINK) { - v4l_bound_align_image(width, 8, variant->max_width, - ffs(variant->out_width_align) - 1, - height, 0, variant->max_height, 0, 0); - } else { - v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, - ffs(variant->out_width_align) - 1, - height, 0, fimc->inp_frame.rect.height, - 0, 0); - } - v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", code ? *code : 0, *width, *height); @@ -608,7 +606,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) /* Adjust left/top if cropping rectangle got out of bounds */ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); - r->left = round_down(r->left, fimc->variant->win_hor_offs_align); + r->left = round_down(r->left, fimc->dd->win_hor_offs_align); r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", @@ -628,7 +626,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) /* Adjust left/top if the composing rectangle got out of bounds */ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); - r->left = round_down(r->left, fimc->variant->out_hor_offs_align); + r->left = round_down(r->left, fimc->dd->out_hor_offs_align); r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", @@ -671,7 +669,7 @@ static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; struct flite_frame *frame = &fimc->out_frame; - const struct fimc_fmt *fmt = fimc->fmt; + const struct fimc_fmt *fmt = frame->fmt; plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; @@ -689,18 +687,31 @@ static int fimc_lite_try_fmt(struct fimc_lite *fimc, struct v4l2_pix_format_mplane *pixm, const struct fimc_fmt **ffmt) { - struct flite_variant *variant = fimc->variant; u32 bpl = pixm->plane_fmt[0].bytesperline; + struct flite_drvdata *dd = fimc->dd; + const struct fimc_fmt *inp_fmt = fimc->inp_frame.fmt; const struct fimc_fmt *fmt; - fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0); + if (WARN_ON(inp_fmt == NULL)) + return -EINVAL; + /* + * We allow some flexibility only for YUV formats. In case of raw + * raw Bayer the FIMC-LITE's output format must match its camera + * interface input format. + */ + if (inp_fmt->flags & FMT_FLAGS_YUV) + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, + inp_fmt->flags, 0); + else + fmt = inp_fmt; + if (WARN_ON(fmt == NULL)) return -EINVAL; if (ffmt) *ffmt = fmt; - v4l_bound_align_image(&pixm->width, 8, variant->max_width, - ffs(variant->out_width_align) - 1, - &pixm->height, 0, variant->max_height, 0, 0); + v4l_bound_align_image(&pixm->width, 8, dd->max_width, + ffs(dd->out_width_align) - 1, + &pixm->height, 0, dd->max_height, 0, 0); if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) pixm->plane_fmt[0].bytesperline = (pixm->width * @@ -720,7 +731,6 @@ static int fimc_lite_try_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_lite *fimc = video_drvdata(file); - return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); } @@ -740,7 +750,7 @@ static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, if (ret < 0) return ret; - fimc->fmt = fmt; + frame->fmt = fmt; fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, pixm->plane_fmt[0].sizeimage); frame->f_width = pixm->width; @@ -766,7 +776,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) struct flite_frame *ff = &fimc->out_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; - sink_fmt.format.code = fimc->fmt->mbus_code; + sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; } else { sink_fmt.pad = pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -800,37 +810,47 @@ static int fimc_lite_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_lite *fimc = video_drvdata(file); - struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; + struct media_entity *entity = &fimc->vfd.entity; struct fimc_pipeline *p = &fimc->pipeline; int ret; if (fimc_lite_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, p->m_pipeline); if (ret < 0) return ret; ret = fimc_pipeline_validate(fimc); - if (ret) { - media_entity_pipeline_stop(&sensor->entity); + if (ret < 0) + goto err_p_stop; + + fimc->sensor = __find_remote_sensor(&fimc->subdev.entity); + + ret = vb2_ioctl_streamon(file, priv, type); + if (!ret) { + fimc->streaming = true; return ret; } - return vb2_streamon(&fimc->vb_queue, type); +err_p_stop: + media_entity_pipeline_stop(entity); + return 0; } static int fimc_lite_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_lite *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; - ret = vb2_streamoff(&fimc->vb_queue, type); - if (ret == 0) - media_entity_pipeline_stop(&sd->entity); - return ret; + ret = vb2_ioctl_streamoff(file, priv, type); + if (ret < 0) + return ret; + + media_entity_pipeline_stop(&fimc->vfd.entity); + fimc->streaming = false; + return 0; } static int fimc_lite_reqbufs(struct file *file, void *priv, @@ -840,53 +860,13 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, int ret; reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); - ret = vb2_reqbufs(&fimc->vb_queue, reqbufs); + ret = vb2_ioctl_reqbufs(file, priv, reqbufs); if (!ret) fimc->reqbufs_count = reqbufs->count; return ret; } -static int fimc_lite_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_querybuf(&fimc->vb_queue, buf); -} - -static int fimc_lite_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_qbuf(&fimc->vb_queue, buf); -} - -static int fimc_lite_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK); -} - -static int fimc_lite_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_create_bufs(&fimc->vb_queue, create); -} - -static int fimc_lite_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct fimc_lite *fimc = video_drvdata(file); - - return vb2_prepare_buf(&fimc->vb_queue, b); -} - /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) { @@ -966,38 +946,15 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_g_selection = fimc_lite_g_selection, .vidioc_s_selection = fimc_lite_s_selection, .vidioc_reqbufs = fimc_lite_reqbufs, - .vidioc_querybuf = fimc_lite_querybuf, - .vidioc_prepare_buf = fimc_lite_prepare_buf, - .vidioc_create_bufs = fimc_lite_create_bufs, - .vidioc_qbuf = fimc_lite_qbuf, - .vidioc_dqbuf = fimc_lite_dqbuf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_streamon = fimc_lite_streamon, .vidioc_streamoff = fimc_lite_streamoff, }; -/* Called with the media graph mutex held */ -static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) -{ - struct media_pad *pad = &me->pads[0]; - struct v4l2_subdev *sd; - - while (pad->flags & MEDIA_PAD_FL_SINK) { - /* source pad */ - pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - sd = media_entity_to_v4l2_subdev(pad->entity); - - if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) - return sd; - /* sink pad */ - pad = &sd->entity.pads[0]; - } - return NULL; -} - /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -1072,7 +1029,7 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, { const struct fimc_fmt *fmt; - fmt = fimc_lite_find_format(NULL, NULL, code->index); + fmt = fimc_lite_find_format(NULL, NULL, 0, code->index); if (!fmt) return -EINVAL; code->code = fmt->mbus_code; @@ -1085,7 +1042,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; - struct flite_frame *f = &fimc->out_frame; + struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); @@ -1095,7 +1052,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); - mf->code = fimc->fmt->mbus_code; + mf->code = f->fmt->mbus_code; if (fmt->pad == FLITE_SD_PAD_SINK) { /* full camera input frame size */ @@ -1147,7 +1104,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == FLITE_SD_PAD_SINK) { sink->f_width = mf->width; sink->f_height = mf->height; - fimc->fmt = ffmt; + sink->fmt = ffmt; /* Set sink crop rectangle */ sink->rect.width = mf->width; sink->rect.height = mf->height; @@ -1159,7 +1116,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, source->f_height = mf->height; } else { /* Allow changing format only on sink pad */ - mf->code = fimc->fmt->mbus_code; + mf->code = sink->fmt->mbus_code; mf->width = sink->rect.width; mf->height = sink->rect.height; } @@ -1300,7 +1257,8 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) memset(vfd, 0, sizeof(*vfd)); - fimc->fmt = &fimc_lite_formats[0]; + fimc->inp_frame.fmt = &fimc_lite_formats[0]; + fimc->out_frame.fmt = &fimc_lite_formats[0]; atomic_set(&fimc->out_path, FIMC_IO_DMA); snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", @@ -1311,8 +1269,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) vfd->v4l2_dev = sd->v4l2_dev; vfd->minor = -1; vfd->release = video_device_release_empty; - vfd->lock = &fimc->lock; - fimc->ref_count = 0; + vfd->queue = q; fimc->reqbufs_count = 0; INIT_LIST_HEAD(&fimc->pending_buf_q); @@ -1325,6 +1282,8 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct flite_buffer); q->drv_priv = fimc; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &fimc->lock; ret = vb2_queue_init(q); if (ret < 0) @@ -1408,6 +1367,7 @@ static const struct v4l2_ctrl_config fimc_lite_ctrl = { .id = V4L2_CTRL_CLASS_USER | 0x1001, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Test Pattern 640x480", + .step = 1, }; static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) @@ -1417,7 +1377,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) int ret; v4l2_subdev_init(sd, &fimc_lite_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; @@ -1439,6 +1399,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->ctrl_handler = handler; sd->internal_ops = &fimc_lite_subdev_internal_ops; sd->entity.ops = &fimc_lite_subdev_media_ops; + sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, fimc); return 0; @@ -1480,19 +1441,35 @@ static int fimc_lite_clk_get(struct fimc_lite *fimc) return ret; } +static const struct of_device_id flite_of_match[]; + static int fimc_lite_probe(struct platform_device *pdev) { - struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev); + struct flite_drvdata *drv_data = NULL; + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; struct fimc_lite *fimc; struct resource *res; int ret; - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->index = pdev->id; - fimc->variant = drv_data->variant[fimc->index]; + if (dev->of_node) { + of_id = of_match_node(flite_of_match, dev->of_node); + if (of_id) + drv_data = (struct flite_drvdata *)of_id->data; + fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); + } else { + drv_data = fimc_lite_get_drvdata(pdev); + fimc->index = pdev->id; + } + + if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS) + return -EINVAL; + + fimc->dd = drv_data; fimc->pdev = pdev; init_waitqueue_head(&fimc->irq_queue); @@ -1500,13 +1477,13 @@ static int fimc_lite_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -1514,10 +1491,10 @@ static int fimc_lite_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, flite_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); + dev_err(dev, "Failed to install irq (%d)\n", ret); goto err_clk; } @@ -1527,23 +1504,23 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); - dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n", + dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", fimc->index); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_lite_unregister_capture_subdev(fimc); err_clk: @@ -1634,7 +1611,14 @@ static int fimc_lite_remove(struct platform_device *pdev) return 0; } -static struct flite_variant fimc_lite0_variant_exynos4 = { +static const struct dev_pm_ops fimc_lite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) + SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, + NULL) +}; + +/* EXYNOS4212, EXYNOS4412 */ +static struct flite_drvdata fimc_lite_drvdata_exynos4 = { .max_width = 8192, .max_height = 8192, .out_width_align = 8, @@ -1642,14 +1626,6 @@ static struct flite_variant fimc_lite0_variant_exynos4 = { .out_hor_offs_align = 8, }; -/* EXYNOS4212, EXYNOS4412 */ -static struct flite_drvdata fimc_lite_drvdata_exynos4 = { - .variant = { - [0] = &fimc_lite0_variant_exynos4, - [1] = &fimc_lite0_variant_exynos4, - }, -}; - static struct platform_device_id fimc_lite_driver_ids[] = { { .name = "exynos-fimc-lite", @@ -1659,17 +1635,21 @@ static struct platform_device_id fimc_lite_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); -static const struct dev_pm_ops fimc_lite_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) - SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, - NULL) +static const struct of_device_id flite_of_match[] = { + { + .compatible = "samsung,exynos4212-fimc-lite", + .data = &fimc_lite_drvdata_exynos4, + }, + { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, .remove = fimc_lite_remove, .id_table = fimc_lite_driver_ids, .driver = { + .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, .owner = THIS_MODULE, .pm = &fimc_lite_pm_ops, diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 7085761f8c4b..47da5e049247 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -20,12 +20,11 @@ #include <media/media-entity.h> #include <media/videobuf2-core.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> #include <media/s5p_fimc.h> -#include "fimc-core.h" - #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" #define FLITE_CLK_NAME "flite" #define FIMC_LITE_MAX_DEVS 2 @@ -49,7 +48,7 @@ enum { #define FLITE_SD_PAD_SOURCE_ISP 2 #define FLITE_SD_PADS_NUM 3 -struct flite_variant { +struct flite_drvdata { unsigned short max_width; unsigned short max_height; unsigned short out_width_align; @@ -57,10 +56,6 @@ struct flite_variant { unsigned short out_hor_offs_align; }; -struct flite_drvdata { - struct flite_variant *variant[FIMC_LITE_MAX_DEVS]; -}; - #define fimc_lite_get_drvdata(_pdev) \ ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) @@ -75,11 +70,13 @@ struct fimc_lite_events { * @f_width: full pixel width * @f_height: full pixel height * @rect: crop/composition rectangle + * @fmt: pointer to pixel format description data structure */ struct flite_frame { u16 f_width; u16 f_height; struct v4l2_rect rect; + const struct fimc_fmt *fmt; }; /** @@ -97,7 +94,7 @@ struct flite_buffer { /** * struct fimc_lite - fimc lite structure * @pdev: pointer to FIMC-LITE platform device - * @variant: variant information for this IP + * @dd: SoC specific driver data structure * @v4l2_dev: pointer to top the level v4l2_device * @vfd: video device node * @fh: v4l2 file handle @@ -116,7 +113,6 @@ struct flite_buffer { * @clock: FIMC-LITE gate clock * @regs: memory mapped io registers * @irq_queue: interrupt handler waitqueue - * @fmt: pointer to color format description structure * @payload: image size in bytes (w x h x bpp) * @inp_frame: camera input frame structure * @out_frame: DMA output frame structure @@ -133,7 +129,7 @@ struct flite_buffer { */ struct fimc_lite { struct platform_device *pdev; - struct flite_variant *variant; + struct flite_drvdata *dd; struct v4l2_device *v4l2_dev; struct video_device vfd; struct v4l2_fh fh; @@ -144,7 +140,7 @@ struct fimc_lite { struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; - u32 index; + int index; struct fimc_pipeline pipeline; const struct fimc_pipeline_ops *pipeline_ops; @@ -155,7 +151,6 @@ struct fimc_lite { void __iomem *regs; wait_queue_head_t irq_queue; - const struct fimc_fmt *fmt; unsigned long payload[FLITE_MAX_PLANES]; struct flite_frame inp_frame; struct flite_frame out_frame; @@ -171,6 +166,7 @@ struct fimc_lite { int ref_count; struct fimc_lite_events events; + bool streaming; }; static inline bool fimc_lite_active(struct fimc_lite *fimc) diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index f3d535cdd87f..bde1f47f7ed3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -29,8 +29,7 @@ #include "fimc-core.h" #include "fimc-reg.h" -#include "fimc-mdevice.h" - +#include "media-dev.h" static unsigned int get_m2m_fmt_flags(unsigned int stream_type) { @@ -100,7 +99,7 @@ static int stop_streaming(struct vb2_queue *q) static void fimc_device_run(void *priv) { - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; struct fimc_ctx *ctx = priv; struct fimc_frame *sf, *df; struct fimc_dev *fimc; @@ -123,16 +122,18 @@ static void fimc_device_run(void *priv) fimc_prepare_dma_offset(ctx, df); } - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); if (ret) goto dma_unlock; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); if (ret) goto dma_unlock; + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + /* Reconfigure hardware if the context has changed. */ if (fimc->m2m.ctx != ctx) { ctx->state |= FIMC_PARAMS; @@ -152,7 +153,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); fimc_hw_set_output_path(ctx); } @@ -250,22 +251,20 @@ static struct vb2_ops fimc_qops = { * V4L2 ioctl handlers */ static int fimc_m2m_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) + struct v4l2_capability *cap) { - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); + unsigned int caps; - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; /* * This is only a mem-to-mem video device. The capture and output * device capability flags are left only for backward compatibility * and are scheduled for removal. */ - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | + caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); return 0; } @@ -623,6 +622,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &fimc_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -634,6 +634,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &fimc_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -667,16 +668,15 @@ static int fimc_m2m_open(struct file *file) struct fimc_ctx *ctx; int ret = -EBUSY; - dbg("pid: %d, state: 0x%lx, refcnt: %d", - task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); + pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state); if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; /* - * Return if the corresponding video capture node - * is already opened. + * Don't allow simultaneous open() of the mem-to-mem and the + * capture video node that belong to same FIMC IP instance. */ - if (fimc->vid_cap.refcnt > 0) + if (test_bit(ST_CAPT_BUSY, &fimc->state)) goto unlock; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 50b97c75b956..f079f36099de 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -1,22 +1,24 @@ /* * Register interface file for Samsung Camera Interface (FIMC) driver * - * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki, <s.nawrocki@samsung.com> + * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <linux/io.h> #include <linux/delay.h> +#include <linux/io.h> +#include <linux/regmap.h> + #include <media/s5p_fimc.h> +#include "media-dev.h" #include "fimc-reg.h" #include "fimc-core.h" - void fimc_hw_reset(struct fimc_dev *dev) { u32 cfg; @@ -35,7 +37,7 @@ void fimc_hw_reset(struct fimc_dev *dev) cfg &= ~FIMC_REG_CIGCTRL_SWRST; writel(cfg, dev->regs + FIMC_REG_CIGCTRL); - if (dev->variant->out_buf_count > 4) + if (dev->drv_data->out_buf_count > 4) fimc_hw_set_dma_seq(dev, 0xF); } @@ -447,7 +449,8 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK | FIMC_REG_MSCTRL_INPUT_MASK | FIMC_REG_MSCTRL_C_INT_IN_MASK - | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK); + | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK + | FIMC_REG_MSCTRL_ORDER422_MASK); cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4) | FIMC_REG_MSCTRL_INPUT_MEMORY @@ -598,7 +601,8 @@ static const struct mbus_pixfmt_desc pix_desc[] = { int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct fimc_source_info *source) { - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct fimc_frame *f = &vc->ctx->s_frame; u32 bus_width, cfg = 0; int i; @@ -606,7 +610,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, case FIMC_BUS_TYPE_ITU_601: case FIMC_BUS_TYPE_ITU_656: for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { - if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { + if (vc->ci_fmt.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; bus_width = pix_desc[i].bus_width; break; @@ -614,9 +618,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, } if (i == ARRAY_SIZE(pix_desc)) { - v4l2_err(&fimc->vid_cap.vfd, + v4l2_err(&vc->vfd, "Camera color format not supported: %d\n", - fimc->vid_cap.mf.code); + vc->ci_fmt.code); return -EINVAL; } @@ -631,6 +635,10 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, if (fimc_fmt_is_user_defined(f->fmt->color)) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; break; + default: + case FIMC_BUS_TYPE_ISP_WRITEBACK: + /* Anything to do here ? */ + break; } cfg |= (f->o_width << 16) | f->o_height; @@ -660,16 +668,17 @@ void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) int fimc_hw_set_camera_type(struct fimc_dev *fimc, struct fimc_source_info *source) { - u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; u32 csis_data_alignment = 32; + u32 cfg, tmp; cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); /* Select ITU B interface, disable Writeback path and test pattern. */ cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A | FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | - FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); + FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG | + FIMC_REG_CIGCTRL_SELWB_A); switch (source->fimc_bus_type) { case FIMC_BUS_TYPE_MIPI_CSI2: @@ -679,7 +688,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ - switch (vid_cap->mf.code) { + switch (vid_cap->ci_fmt.code) { case V4L2_MBUS_FMT_VYUY8_2X8: tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT; break; @@ -691,7 +700,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, default: v4l2_err(&vid_cap->vfd, "Not supported camera pixel format: %#x\n", - vid_cap->mf.code); + vid_cap->ci_fmt.code); return -EINVAL; } tmp |= (csis_data_alignment == 32) << 8; @@ -704,6 +713,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, break; case FIMC_BUS_TYPE_LCD_WRITEBACK_A: cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; + /* fall through */ + case FIMC_BUS_TYPE_ISP_WRITEBACK: + if (fimc->variant->has_isp_wb) + cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; + else + WARN_ONCE(1, "ISP Writeback input is not supported\n"); break; default: v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", @@ -747,7 +762,7 @@ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { s32 reg; - if (dev->variant->has_cistatus2) { + if (dev->drv_data->cistatus2) { reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3f; return reg - 1; } @@ -763,7 +778,7 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) { s32 reg; - if (!dev->variant->has_cistatus2) + if (!dev->drv_data->cistatus2) return -1; reg = readl(dev->regs + FIMC_REG_CISTATUS2); @@ -784,3 +799,43 @@ void fimc_deactivate_capture(struct fimc_dev *fimc) fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } + +int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc) +{ + struct regmap *map = fimc->sysreg; + unsigned int mask, val, camblk_cfg; + int ret; + + if (map == NULL) + return 0; + + ret = regmap_read(map, SYSREG_CAMBLK, &camblk_cfg); + if (ret < 0 || ((camblk_cfg & 0x00700000) >> 20 != 0x3)) + return ret; + + if (!WARN(fimc->id >= 3, "not supported id: %d\n", fimc->id)) + val = 0x1 << (fimc->id + 20); + else + val = 0; + + mask = SYSREG_CAMBLK_FIFORST_ISP | SYSREG_CAMBLK_ISPWB_FULL_EN; + ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + val |= SYSREG_CAMBLK_FIFORST_ISP; + ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); + if (ret < 0) + return ret; + + mask = SYSREG_ISPBLK_FIFORST_CAM_BLK; + ret = regmap_update_bits(map, SYSREG_ISPBLK, mask, ~mask); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + return regmap_update_bits(map, SYSREG_ISPBLK, mask, mask); +} diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h index 1a40df6d1a80..6c97798c75a5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-reg.h @@ -52,6 +52,8 @@ #define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19) #define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16) #define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12) +/* 0 - selects Writeback A (LCD), 1 - selects Writeback B (LCD/ISP) */ +#define FIMC_REG_CIGCTRL_SELWB_A (1 << 10) #define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8) #define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7) #define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6) @@ -93,10 +95,10 @@ /* Output DMA control */ #define FIMC_REG_CIOCTRL 0x4c #define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0) -#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (0 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (1 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (3 << 0) #define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2) #define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3) #define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3) @@ -218,10 +220,10 @@ #define FIMC_REG_MSCTRL_FLIP_180 (3 << 13) #define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12) #define FIMC_REG_MSCTRL_ORDER422_SHIFT 4 -#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4) -#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4) -#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4) -#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (0 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (1 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (2 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (3 << 4) #define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4) #define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3) #define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3) @@ -276,6 +278,14 @@ /* Output frame buffer sequence mask */ #define FIMC_REG_CIFCNTSEQ 0x1fc +/* SYSREG ISP Writeback register address offsets */ +#define SYSREG_ISPBLK 0x020c +#define SYSREG_ISPBLK_FIFORST_CAM_BLK (1 << 7) + +#define SYSREG_CAMBLK 0x0218 +#define SYSREG_CAMBLK_FIFORST_ISP (1 << 15) +#define SYSREG_CAMBLK_ISPWB_FULL_EN (7 << 20) + /* * Function declarations */ @@ -309,6 +319,7 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); +int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc); void fimc_activate_capture(struct fimc_ctx *ctx); void fimc_deactivate_capture(struct fimc_dev *fimc); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/exynos4-is/media-dev.c index a17fcb2d5d41..15ef8f28239b 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -17,32 +17,37 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/of_i2c.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/types.h> #include <linux/slab.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-of.h> #include <media/media-device.h> #include <media/s5p_fimc.h> +#include "media-dev.h" #include "fimc-core.h" +#include "fimc-is.h" #include "fimc-lite.h" -#include "fimc-mdevice.h" #include "mipi-csis.h" static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, + struct fimc_source_info *si, bool on); /** * fimc_pipeline_prepare - update pipeline information with subdevice pointers - * @fimc: fimc device terminating the pipeline + * @me: media entity terminating the pipeline * * Caller holds the graph mutex. */ static void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { - struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; int i; @@ -50,15 +55,21 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, p->subdevs[i] = NULL; while (1) { - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; + struct media_pad *pad = NULL; + + /* Find remote source pad */ + for (i = 0; i < me->num_pads; i++) { + struct media_pad *spad = &me->pads[i]; + if (!(spad->flags & MEDIA_PAD_FL_SINK)) + continue; + pad = media_entity_remote_source(spad); + if (pad) + break; + } - /* source pad */ - pad = media_entity_remote_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; - sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { @@ -75,12 +86,15 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; + case GRP_ID_FIMC_IS: + p->subdevs[IDX_IS_ISP] = sd; + break; default: - pr_warn("%s: Unknown subdev grp_id: %#x\n", - __func__, sd->grp_id); + break; } - /* sink pad */ - pad = &sd->entity.pads[0]; + me = &sd->entity; + if (me->num_pads == 1) + break; } } @@ -117,49 +131,81 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on) * * Needs to be called with the graph mutex held. */ -static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) +static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool on) { - unsigned int i; - int ret; + static const u8 seq[2][IDX_MAX - 1] = { + { IDX_IS_ISP, IDX_SENSOR, IDX_CSIS, IDX_FLITE }, + { IDX_CSIS, IDX_FLITE, IDX_SENSOR, IDX_IS_ISP }, + }; + int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) return -ENXIO; - for (i = 0; i < IDX_MAX; i++) { - unsigned int idx = state ? (IDX_MAX - 1) - i : i; + for (i = 0; i < IDX_MAX - 1; i++) { + unsigned int idx = seq[on][i]; + + ret = __subdev_set_power(p->subdevs[idx], on); + - ret = __subdev_set_power(p->subdevs[idx], state); if (ret < 0 && ret != -ENXIO) - return ret; + goto error; } - return 0; +error: + for (; i >= 0; i--) { + unsigned int idx = seq[on][i]; + __subdev_set_power(p->subdevs[idx], !on); + } + return ret; } /** * __fimc_pipeline_open - update the pipeline information, enable power * of all pipeline subdevs and the sensor clock * @me: media entity to start graph walk with - * @prep: true to acquire sensor (and csis) subdevs + * @prepare: true to walk the current pipeline and acquire all subdevs * * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct fimc_pipeline *p, - struct media_entity *me, bool prep) + struct media_entity *me, bool prepare) { + struct fimc_md *fmd = entity_to_fimc_mdev(me); + struct v4l2_subdev *sd; int ret; - if (prep) + if (WARN_ON(p == NULL || me == NULL)) + return -EINVAL; + + if (prepare) fimc_pipeline_prepare(p, me); - if (p->subdevs[IDX_SENSOR] == NULL) + sd = p->subdevs[IDX_SENSOR]; + if (sd == NULL) return -EINVAL; - ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true); - if (ret) - return ret; + /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) { + ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]); + if (ret < 0) + return ret; + } + ret = fimc_md_set_camclk(sd, true); + if (ret < 0) + goto err_wbclk; + + ret = fimc_pipeline_s_power(p, 1); + if (!ret) + return 0; - return fimc_pipeline_s_power(p, 1); + fimc_md_set_camclk(sd, false); + +err_wbclk: + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) + clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); + + return ret; } /** @@ -170,41 +216,58 @@ static int __fimc_pipeline_open(struct fimc_pipeline *p, */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { + struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; + struct fimc_md *fmd; int ret = 0; - if (!p || !p->subdevs[IDX_SENSOR]) + if (WARN_ON(sd == NULL)) return -EINVAL; if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); - fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); + fimc_md_set_camclk(sd, false); } + + fmd = entity_to_fimc_mdev(&sd->entity); + + /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ + if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) + clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); + return ret == -ENXIO ? 0 : ret; } /** - * __fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs * @pipeline: video pipeline structure - * @on: passed as the s_stream call argument + * @on: passed as the s_stream() callback argument */ static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { - int i, ret; + static const u8 seq[2][IDX_MAX] = { + { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE }, + { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, + }; + int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; for (i = 0; i < IDX_MAX; i++) { - unsigned int idx = on ? (IDX_MAX - 1) - i : i; + unsigned int idx = seq[on][i]; ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; + goto error; } - return 0; - +error: + for (; i >= 0; i--) { + unsigned int idx = seq[on][i]; + v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on); + } + return ret; } /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ @@ -218,35 +281,40 @@ static const struct fimc_pipeline_ops fimc_pipeline_ops = { * Sensor subdevice helper functions */ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, - struct fimc_sensor_info *s_info) + struct fimc_source_info *si) { struct i2c_adapter *adapter; struct v4l2_subdev *sd = NULL; - if (!s_info || !fmd) + if (!si || !fmd) return NULL; + /* + * If FIMC bus type is not Writeback FIFO assume it is same + * as sensor_bus_type. + */ + si->fimc_bus_type = si->sensor_bus_type; - adapter = i2c_get_adapter(s_info->pdata.i2c_bus_num); + adapter = i2c_get_adapter(si->i2c_bus_num); if (!adapter) { v4l2_warn(&fmd->v4l2_dev, "Failed to get I2C adapter %d, deferring probe\n", - s_info->pdata.i2c_bus_num); + si->i2c_bus_num); return ERR_PTR(-EPROBE_DEFER); } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, - s_info->pdata.board_info, NULL); + si->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); v4l2_warn(&fmd->v4l2_dev, "Failed to acquire subdev %s, deferring probe\n", - s_info->pdata.board_info->type); + si->board_info->type); return ERR_PTR(-EPROBE_DEFER); } - v4l2_set_subdev_hostdata(sd, s_info); + v4l2_set_subdev_hostdata(sd, si); sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - s_info->pdata.board_info->type); + sd->name); return sd; } @@ -257,57 +325,262 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) if (!client) return; + v4l2_device_unregister_subdev(sd); - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); + + if (!client->dev.of_node) { + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); + } } +#ifdef CONFIG_OF +/* Register I2C client subdev associated with @node. */ +static int fimc_md_of_add_sensor(struct fimc_md *fmd, + struct device_node *node, int index) +{ + struct fimc_sensor_info *si; + struct i2c_client *client; + struct v4l2_subdev *sd; + int ret; + + if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) + return -EINVAL; + si = &fmd->sensor[index]; + + client = of_find_i2c_device_by_node(node); + if (!client) + return -EPROBE_DEFER; + + device_lock(&client->dev); + + if (!client->driver || + !try_module_get(client->driver->driver.owner)) { + ret = -EPROBE_DEFER; + v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", + node->full_name); + goto dev_put; + } + + /* Enable sensor's master clock */ + ret = __fimc_md_set_camclk(fmd, &si->pdata, true); + if (ret < 0) + goto mod_put; + sd = i2c_get_clientdata(client); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + __fimc_md_set_camclk(fmd, &si->pdata, false); + if (ret < 0) + goto mod_put; + + v4l2_set_subdev_hostdata(sd, &si->pdata); + if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) + sd->grp_id = GRP_ID_FIMC_IS_SENSOR; + else + sd->grp_id = GRP_ID_SENSOR; + + si->subdev = sd; + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", + sd->name, fmd->num_sensors); + fmd->num_sensors++; + +mod_put: + module_put(client->driver->driver.owner); +dev_put: + device_unlock(&client->dev); + put_device(&client->dev); + return ret; +} + +/* Parse port node and register as a sub-device any sensor specified there. */ +static int fimc_md_parse_port_node(struct fimc_md *fmd, + struct device_node *port, + unsigned int index) +{ + struct device_node *rem, *ep, *np; + struct fimc_source_info *pd; + struct v4l2_of_endpoint endpoint; + int ret; + u32 val; + + pd = &fmd->sensor[index].pdata; + + /* Assume here a port node can have only one endpoint node. */ + ep = of_get_next_child(port, NULL); + if (!ep) + return 0; + + v4l2_of_parse_endpoint(ep, &endpoint); + if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS) + return -EINVAL; + + pd->mux_id = (endpoint.port - 1) & 0x1; + + rem = v4l2_of_get_remote_port_parent(ep); + of_node_put(ep); + if (rem == NULL) { + v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n", + ep->full_name); + return 0; + } + if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) + pd->clk_id = val; + + if (!of_property_read_u32(rem, "clock-frequency", &val)) + pd->clk_frequency = val; + + if (pd->clk_frequency == 0) { + v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", + rem->full_name); + of_node_put(rem); + return -EINVAL; + } + + if (fimc_input_is_parallel(endpoint.port)) { + if (endpoint.bus_type == V4L2_MBUS_PARALLEL) + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601; + else + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656; + pd->flags = endpoint.bus.parallel.flags; + } else if (fimc_input_is_mipi_csi(endpoint.port)) { + /* + * MIPI CSI-2: only input mux selection and + * the sensor's clock frequency is needed. + */ + pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2; + } else { + v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n", + endpoint.port, rem->full_name); + } + /* + * For FIMC-IS handled sensors, that are placed under i2c-isp device + * node, FIMC is connected to the FIMC-IS through its ISP Writeback + * input. Sensors are attached to the FIMC-LITE hostdata interface + * directly or through MIPI-CSIS, depending on the external media bus + * used. This needs to be handled in a more reliable way, not by just + * checking parent's node name. + */ + np = of_get_parent(rem); + + if (np && !of_node_cmp(np->name, "i2c-isp")) + pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; + else + pd->fimc_bus_type = pd->sensor_bus_type; + + ret = fimc_md_of_add_sensor(fmd, rem, index); + of_node_put(rem); + + return ret; +} + +/* Register all SoC external sub-devices */ +static int fimc_md_of_sensors_register(struct fimc_md *fmd, + struct device_node *np) +{ + struct device_node *parent = fmd->pdev->dev.of_node; + struct device_node *node, *ports; + int index = 0; + int ret; + + /* Attach sensors linked to MIPI CSI-2 receivers */ + for_each_available_child_of_node(parent, node) { + struct device_node *port; + + if (of_node_cmp(node->name, "csis")) + continue; + /* The csis node can have only port subnode. */ + port = of_get_next_child(node, NULL); + if (!port) + continue; + + ret = fimc_md_parse_port_node(fmd, port, index); + if (ret < 0) + return ret; + index++; + } + + /* Attach sensors listed in the parallel-ports node */ + ports = of_get_child_by_name(parent, "parallel-ports"); + if (!ports) + return 0; + + for_each_child_of_node(ports, node) { + ret = fimc_md_parse_port_node(fmd, node, index); + if (ret < 0) + break; + index++; + } + + return 0; +} + +static int __of_get_csis_id(struct device_node *np) +{ + u32 reg = 0; + + np = of_get_child_by_name(np, "port"); + if (!np) + return -EINVAL; + of_property_read_u32(np, "reg", ®); + return reg - FIMC_INPUT_MIPI_CSI2_0; +} +#else +#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) +#define __of_get_csis_id(np) (-ENOSYS) +#endif + static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct fimc_dev *fd = NULL; - int num_clients, ret, i; + struct device_node *of_node = fmd->pdev->dev.of_node; + int num_clients = 0; + int ret, i; /* * Runtime resume one of the FIMC entities to make sure * the sclk_cam clocks are not globally disabled. */ - for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++) - if (fmd->fimc[i]) - fd = fmd->fimc[i]; - if (!fd) + if (!fmd->pmf) return -ENXIO; - ret = pm_runtime_get_sync(&fd->pdev->dev); + + ret = pm_runtime_get_sync(fmd->pmf); if (ret < 0) return ret; - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); - - fmd->num_sensors = num_clients; - for (i = 0; i < num_clients; i++) { - struct v4l2_subdev *sd; - - fmd->sensor[i].pdata = pdata->source_info[i]; - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); - - if (!IS_ERR(sd)) { - fmd->sensor[i].subdev = sd; - } else { - fmd->sensor[i].subdev = NULL; - ret = PTR_ERR(sd); - break; + if (of_node) { + fmd->num_sensors = 0; + ret = fimc_md_of_sensors_register(fmd, of_node); + } else if (pdata) { + WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, + ARRAY_SIZE(fmd->sensor)); + fmd->num_sensors = num_clients; + + for (i = 0; i < num_clients; i++) { + struct fimc_sensor_info *si = &fmd->sensor[i]; + struct v4l2_subdev *sd; + + si->pdata = pdata->source_info[i]; + ret = __fimc_md_set_camclk(fmd, &si->pdata, true); + if (ret) + break; + sd = fimc_md_register_sensor(fmd, &si->pdata); + ret = __fimc_md_set_camclk(fmd, &si->pdata, false); + + if (IS_ERR(sd)) { + si->subdev = NULL; + ret = PTR_ERR(sd); + break; + } + si->subdev = sd; + if (ret) + break; } - if (ret) - break; } - pm_runtime_put(&fd->pdev->dev); + + pm_runtime_put(fmd->pmf); return ret; } @@ -352,6 +625,8 @@ static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) { + if (!fmd->pmf && fimc->pdev) + fmd->pmf = &fimc->pdev->dev; fmd->fimc[fimc->id] = fimc; fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; } else { @@ -368,13 +643,13 @@ static int register_csis_entity(struct fimc_md *fmd, struct device_node *node = pdev->dev.of_node; int id, ret; - id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id); + id = node ? __of_get_csis_id(node) : max(0, pdev->id); - if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd)) - return -EBUSY; + if (WARN_ON(id < 0 || id >= CSIS_MAX_ENTITIES)) + return -ENOENT; - if (WARN_ON(id >= CSIS_MAX_ENTITIES)) - return 0; + if (WARN_ON(fmd->csis[id].sd)) + return -EBUSY; sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -386,6 +661,22 @@ static int register_csis_entity(struct fimc_md *fmd, return ret; } +static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) +{ + struct v4l2_subdev *sd = &is->isp.subdev; + int ret; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, + "Failed to register FIMC-ISP (%d)\n", ret); + return ret; + } + + fmd->fimc_is = is; + return 0; +} + static int fimc_md_register_platform_entity(struct fimc_md *fmd, struct platform_device *pdev, int plat_entity) @@ -413,6 +704,9 @@ static int fimc_md_register_platform_entity(struct fimc_md *fmd, case IDX_CSIS: ret = register_csis_entity(fmd, pdev, drvdata); break; + case IDX_IS_ISP: + ret = register_fimc_is_entity(fmd, drvdata); + break; default: ret = -ENODEV; } @@ -457,6 +751,47 @@ static int fimc_md_pdev_match(struct device *dev, void *data) return 0; } +/* Register FIMC, FIMC-LITE and CSIS media entities */ +#ifdef CONFIG_OF +static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, + struct device_node *parent) +{ + struct device_node *node; + int ret = 0; + + for_each_available_child_of_node(parent, node) { + struct platform_device *pdev; + int plat_entity = -1; + + pdev = of_find_device_by_node(node); + if (!pdev) + continue; + + /* If driver of any entity isn't ready try all again later. */ + if (!strcmp(node->name, CSIS_OF_NODE_NAME)) + plat_entity = IDX_CSIS; + else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME)) + plat_entity = IDX_IS_ISP; + else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) + plat_entity = IDX_FLITE; + else if (!strcmp(node->name, FIMC_OF_NODE_NAME) && + !of_property_read_bool(node, "samsung,lcd-wb")) + plat_entity = IDX_FIMC; + + if (plat_entity >= 0) + ret = fimc_md_register_platform_entity(fmd, pdev, + plat_entity); + put_device(&pdev->dev); + if (ret < 0) + break; + } + + return ret; +} +#else +#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) +#endif + static void fimc_md_unregister_entities(struct fimc_md *fmd) { int i; @@ -479,7 +814,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); - module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -488,6 +822,10 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) fimc_md_unregister_sensor(fmd->sensor[i].subdev); fmd->sensor[i].subdev = NULL; } + + if (fmd->fimc_is) + v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); + v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); } @@ -504,12 +842,19 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct v4l2_subdev *sensor, int pad, int link_mask) { - struct fimc_sensor_info *s_info = NULL; + struct fimc_source_info *si = NULL; struct media_entity *sink; unsigned int flags = 0; - int ret, i; + int i, ret = 0; - for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (sensor) { + si = v4l2_get_subdev_hostdata(sensor); + /* Skip direct FIMC links in the logical FIMC-IS sensor path */ + if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) + ret = 1; + } + + for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; /* @@ -523,7 +868,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, - FIMC_SD_PAD_SINK, flags); + FIMC_SD_PAD_SINK_CAM, flags); if (ret) return ret; @@ -538,11 +883,13 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (flags == 0 || sensor == NULL) continue; - s_info = v4l2_get_subdev_hostdata(sensor); - if (!WARN_ON(s_info == NULL)) { + + if (!WARN_ON(si == NULL)) { unsigned long irq_flags; + struct fimc_sensor_info *inf = source_to_sensor_info(si); + spin_lock_irqsave(&fmd->slock, irq_flags); - s_info->host = fmd->fimc[i]; + inf->host = fmd->fimc[i]; spin_unlock_irqrestore(&fmd->slock, irq_flags); } } @@ -551,25 +898,20 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (!fmd->fimc_lite[i]) continue; - if (link_mask & (1 << (i + FIMC_MAX_DEVS))) - flags = MEDIA_LNK_FL_ENABLED; - else - flags = 0; - sink = &fmd->fimc_lite[i]->subdev.entity; ret = media_entity_create_link(source, pad, sink, - FLITE_SD_PAD_SINK, flags); + FLITE_SD_PAD_SINK, 0); if (ret) return ret; /* Notify FIMC-LITE subdev entity */ ret = media_entity_call(sink, link_setup, &sink->pads[0], - &source->pads[pad], flags); + &source->pads[pad], 0); if (ret) break; - v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", - source->name, flags ? '=' : '-', sink->name); + v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n", + source->name, sink->name); } return 0; } @@ -578,21 +920,50 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) { struct media_entity *source, *sink; - unsigned int flags = MEDIA_LNK_FL_ENABLED; int i, ret = 0; for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { struct fimc_lite *fimc = fmd->fimc_lite[i]; + if (fimc == NULL) continue; + source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, - sink, 0, flags); + sink, 0, 0); + if (ret) + break; + /* Link from FIMC-LITE to IS-ISP subdev */ + sink = &fmd->fimc_is->isp.subdev.entity; + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP, + sink, 0, 0); if (ret) break; - /* TODO: create links to other entities */ + } + + return ret; +} + +/* Create FIMC-IS links */ +static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) +{ + struct media_entity *source, *sink; + int i, ret; + + source = &fmd->fimc_is->isp.subdev.entity; + + for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (fmd->fimc[i] == NULL) + continue; + + /* Link from IS-ISP subdev to FIMC */ + sink = &fmd->fimc[i]->vid_cap.subdev.entity; + ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, + sink, FIMC_SD_PAD_SINK_FIFO, 0); + if (ret) + return ret; } return ret; @@ -615,7 +986,6 @@ static int fimc_md_create_links(struct fimc_md *fmd) struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct fimc_source_info *pdata; - struct fimc_sensor_info *s_info; struct media_entity *source, *sink; int i, pad, fimc_id = 0, ret = 0; u32 flags, link_mask = 0; @@ -625,12 +995,11 @@ static int fimc_md_create_links(struct fimc_md *fmd) continue; sensor = fmd->sensor[i].subdev; - s_info = v4l2_get_subdev_hostdata(sensor); - if (!s_info) + pdata = v4l2_get_subdev_hostdata(sensor); + if (!pdata) continue; source = NULL; - pdata = &s_info->pdata; switch (pdata->sensor_bus_type) { case FIMC_BUS_TYPE_MIPI_CSI2: @@ -680,6 +1049,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; + source = &fmd->csis[i].sd->entity; pad = CSIS_PAD_SOURCE; sensor = csi_sensors[i]; @@ -694,19 +1064,28 @@ static int fimc_md_create_links(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; + source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.vfd.entity; + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); if (ret) break; } - return __fimc_md_create_flite_source_links(fmd); + ret = __fimc_md_create_flite_source_links(fmd); + if (ret < 0) + return ret; + + if (fmd->use_isp) + ret = __fimc_md_create_fimc_is_links(fmd); + + return ret; } /* - * The peripheral sensor clock management. + * The peripheral sensor and CAM_BLK (PIXELASYNCMx) clocks management. */ static void fimc_md_put_clocks(struct fimc_md *fmd) { @@ -719,6 +1098,14 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) clk_put(fmd->camclk[i].clock); fmd->camclk[i].clock = ERR_PTR(-EINVAL); } + + /* Writeback (PIXELASYNCMx) clocks */ + for (i = 0; i < FIMC_MAX_WBCLKS; i++) { + if (IS_ERR(fmd->wbclk[i])) + continue; + clk_put(fmd->wbclk[i]); + fmd->wbclk[i] = ERR_PTR(-EINVAL); + } } static int fimc_md_get_clocks(struct fimc_md *fmd) @@ -755,35 +1142,59 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) if (ret) fimc_md_put_clocks(fmd); + if (!fmd->use_isp) + return 0; + /* + * For now get only PIXELASYNCM1 clock (Writeback B/ISP), + * leave PIXELASYNCM0 out for the LCD Writeback driver. + */ + fmd->wbclk[CLK_IDX_WB_A] = ERR_PTR(-EINVAL); + + for (i = CLK_IDX_WB_B; i < FIMC_MAX_WBCLKS; i++) { + snprintf(clk_name, sizeof(clk_name), "pxl_async%u", i); + clock = clk_get(dev, clk_name); + if (IS_ERR(clock)) { + v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s\n", + clk_name); + ret = PTR_ERR(clock); + break; + } + fmd->wbclk[i] = clock; + } + if (ret) + fimc_md_put_clocks(fmd); + return ret; } static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, + struct fimc_source_info *si, bool on) { - struct fimc_source_info *pdata = &s_info->pdata; struct fimc_camclk_info *camclk; int ret = 0; - if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL) + if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) return -EINVAL; - camclk = &fmd->camclk[pdata->clk_id]; + camclk = &fmd->camclk[si->clk_id]; dbg("camclk %d, f: %lu, use_count: %d, on: %d", - pdata->clk_id, pdata->clk_frequency, camclk->use_count, on); + si->clk_id, si->clk_frequency, camclk->use_count, on); if (on) { if (camclk->use_count > 0 && - camclk->frequency != pdata->clk_frequency) + camclk->frequency != si->clk_frequency) return -EINVAL; if (camclk->use_count++ == 0) { - clk_set_rate(camclk->clock, pdata->clk_frequency); - camclk->frequency = pdata->clk_frequency; + clk_set_rate(camclk->clock, si->clk_frequency); + camclk->frequency = si->clk_frequency; + ret = pm_runtime_get_sync(fmd->pmf); + if (ret < 0) + return ret; ret = clk_enable(camclk->clock); - dbg("Enabled camclk %d: f: %lu", pdata->clk_id, + dbg("Enabled camclk %d: f: %lu", si->clk_id, clk_get_rate(camclk->clock)); } return ret; @@ -794,7 +1205,8 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (--camclk->use_count == 0) { clk_disable(camclk->clock); - dbg("Disabled camclk %d", pdata->clk_id); + pm_runtime_put(fmd->pmf); + dbg("Disabled camclk %d", si->clk_id); } return ret; } @@ -813,10 +1225,10 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, */ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) { - struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd); + struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); - return __fimc_md_set_camclk(fmd, s_info, on); + return __fimc_md_set_camclk(fmd, si, on); } static int fimc_md_link_notify(struct media_pad *source, @@ -827,7 +1239,7 @@ static int fimc_md_link_notify(struct media_pad *source, struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; struct mutex *lock; - int ret = 0; + int i, ret = 0; int ref_count; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -854,29 +1266,28 @@ static int fimc_md_link_notify(struct media_pad *source, return 0; } + mutex_lock(lock); + ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; + if (!(flags & MEDIA_LNK_FL_ENABLED)) { - int i; - mutex_lock(lock); - ret = __fimc_pipeline_close(pipeline); + if (ref_count > 0) { + ret = __fimc_pipeline_close(pipeline); + if (!ret && fimc) + fimc_ctrls_delete(fimc->vid_cap.ctx); + } for (i = 0; i < IDX_MAX; i++) pipeline->subdevs[i] = NULL; - if (fimc) - fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(lock); - return ret; + } else if (ref_count > 0) { + /* + * Link activation. Enable power of pipeline elements only if + * the pipeline is already in use, i.e. its video node is open. + * Recreate the controls destroyed during the link deactivation. + */ + ret = __fimc_pipeline_open(pipeline, + source->entity, true); + if (!ret && fimc) + ret = fimc_capture_ctrls_create(fimc); } - /* - * Link activation. Enable power of pipeline elements only if the - * pipeline is already in use, i.e. its video node is opened. - * Recreate the controls destroyed during the link deactivation. - */ - mutex_lock(lock); - - ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; - if (ref_count > 0) - ret = __fimc_pipeline_open(pipeline, source->entity, true); - if (!ret && fimc) - ret = fimc_capture_ctrls_create(fimc); mutex_unlock(lock); return ret ? -EPIPE : ret; @@ -927,13 +1338,33 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); +static int fimc_md_get_pinctrl(struct fimc_md *fmd) +{ + struct device *dev = &fmd->pdev->dev; + struct fimc_pinctrl *pctl = &fmd->pinctl; + + pctl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pctl->pinctrl)) + return PTR_ERR(pctl->pinctrl); + + pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(pctl->state_default)) + return PTR_ERR(pctl->state_default); + + pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_IDLE); + return 0; +} + static int fimc_md_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct v4l2_device *v4l2_dev; struct fimc_md *fmd; int ret; - fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL); + fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL); if (!fmd) return -ENOMEM; @@ -943,15 +1374,16 @@ static int fimc_md_probe(struct platform_device *pdev) strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); fmd->media_dev.link_notify = fimc_md_link_notify; - fmd->media_dev.dev = &pdev->dev; + fmd->media_dev.dev = dev; v4l2_dev = &fmd->v4l2_dev; v4l2_dev->mdev = &fmd->media_dev; v4l2_dev->notify = fimc_sensor_notify; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s", - dev_name(&pdev->dev)); + strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); + + fmd->use_isp = fimc_md_is_isp_available(dev->of_node); - ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev); + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; @@ -965,21 +1397,32 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_clk; - fmd->user_subdev_api = false; + fmd->user_subdev_api = (dev->of_node != NULL); /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, - fimc_md_pdev_match); + ret = fimc_md_get_pinctrl(fmd); + if (ret < 0) { + if (ret != EPROBE_DEFER) + dev_err(dev, "Failed to get pinctrl: %d\n", ret); + goto err_unlock; + } + + if (dev->of_node) + ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); + else + ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, + fimc_md_pdev_match); if (ret) goto err_unlock; - if (pdev->dev.platform_data) { + if (dev->platform_data || dev->of_node) { ret = fimc_md_register_sensor_entities(fmd); if (ret) goto err_unlock; } + ret = fimc_md_create_links(fmd); if (ret) goto err_unlock; @@ -1019,12 +1462,25 @@ static int fimc_md_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id fimc_driver_ids[] __always_unused = { + { .name = "s5p-fimc-md" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, fimc_driver_ids); + +static const struct of_device_id fimc_md_of_match[] = { + { .compatible = "samsung,fimc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, fimc_md_of_match); + static struct platform_driver fimc_md_driver = { .probe = fimc_md_probe, .remove = fimc_md_remove, .driver = { - .name = "s5p-fimc-md", - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fimc_md_of_match), + .name = "s5p-fimc-md", + .owner = THIS_MODULE, } }; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/exynos4-is/media-dev.h index 06b0d8276fd2..44d86b61d660 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -12,6 +12,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/mutex.h> +#include <linux/of.h> +#include <linux/pinctrl/consumer.h> #include <media/media-device.h> #include <media/media-entity.h> #include <media/v4l2-device.h> @@ -21,18 +23,23 @@ #include "fimc-lite.h" #include "mipi-csis.h" -/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define GRP_ID_SENSOR (1 << 8) -#define GRP_ID_FIMC_IS_SENSOR (1 << 9) -#define GRP_ID_WRITEBACK (1 << 10) -#define GRP_ID_CSIS (1 << 11) -#define GRP_ID_FIMC (1 << 12) -#define GRP_ID_FLITE (1 << 13) -#define GRP_ID_FIMC_IS (1 << 14) +#define FIMC_OF_NODE_NAME "fimc" +#define FIMC_LITE_OF_NODE_NAME "fimc-lite" +#define FIMC_IS_OF_NODE_NAME "fimc-is" +#define CSIS_OF_NODE_NAME "csis" + +#define PINCTRL_STATE_IDLE "idle" #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 +/* LCD/ISP Writeback clocks (PIXELASYNCMx) */ +enum { + CLK_IDX_WB_A, + CLK_IDX_WB_B, + FIMC_MAX_WBCLKS +}; + struct fimc_csis_info { struct v4l2_subdev *sd; int id; @@ -65,9 +72,15 @@ struct fimc_sensor_info { * @num_sensors: actual number of registered sensors * @camclk: external sensor clock information * @fimc: array of registered fimc devices + * @fimc_is: fimc-is data structure + * @use_isp: set to true when FIMC-IS subsystem is used + * @pmf: handle to the CAMCLK clock control FIMC helper device * @media_dev: top level media device * @v4l2_dev: top level v4l2_device holding up the subdevs * @pdev: platform device this media device is hooked up into + * @pinctrl: camera port pinctrl handle + * @state_default: pinctrl default state handle + * @state_idle: pinctrl idle state handle * @user_subdev_api: true if subdevs are not configured by the host driver * @slock: spinlock protecting @sensor array */ @@ -76,11 +89,20 @@ struct fimc_md { struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; int num_sensors; struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; + struct clk *wbclk[FIMC_MAX_WBCLKS]; struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS]; struct fimc_dev *fimc[FIMC_MAX_DEVS]; + struct fimc_is *fimc_is; + bool use_isp; + struct device *pmf; struct media_device media_dev; struct v4l2_device v4l2_dev; struct platform_device *pdev; + struct fimc_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *state_default; + struct pinctrl_state *state_idle; + } pinctl; bool user_subdev_api; spinlock_t slock; }; @@ -93,6 +115,12 @@ struct fimc_md { #define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) +static inline +struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) +{ + return container_of(si, struct fimc_sensor_info, pdata); +} + static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) { return me->parent == NULL ? NULL : @@ -111,4 +139,14 @@ static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); +#ifdef CONFIG_OF +static inline bool fimc_md_is_isp_available(struct device_node *node) +{ + node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); + return node ? of_device_is_available(node) : false; +} +#else +#define fimc_md_is_isp_available(node) (false) +#endif /* CONFIG_OF */ + #endif diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 981863d05aaa..a2eda9d5ac87 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -19,14 +19,18 @@ #include <linux/kernel.h> #include <linux/memory.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_data/mipi-csis.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/videodev2.h> +#include <media/s5p_fimc.h> +#include <media/v4l2-of.h> #include <media/v4l2-subdev.h> -#include <linux/platform_data/mipi-csis.h> + #include "mipi-csis.h" static int debug; @@ -113,6 +117,7 @@ static char *csi_clock_name[] = { [CSIS_CLK_GATE] = "csis", }; #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) +#define DEFAULT_SCLK_CSIS_FREQ 166000000UL static const char * const csis_supply_name[] = { "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ @@ -167,6 +172,11 @@ struct csis_pktbuf { * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number * @flags: the state variable for power and streaming control + * @clock_frequency: device bus clock frequency + * @hs_settle: HS-RX settle time + * @num_lanes: number of MIPI-CSI data lanes used + * @max_num_lanes: maximum number of MIPI-CSI data lanes supported + * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM * @csis_fmt: current CSIS pixel format * @format: common media bus format for the source and sink pad * @slock: spinlock protecting structure members below @@ -184,6 +194,13 @@ struct csis_state { struct clk *clock[NUM_CSIS_CLOCKS]; int irq; u32 flags; + + u32 clk_frequency; + u32 hs_settle; + u32 num_lanes; + u32 max_num_lanes; + u8 wclk_ext; + const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; @@ -273,7 +290,6 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); @@ -286,7 +302,7 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); val &= ~S5PCSIS_DPHYCTRL_ENABLE; if (on) { - mask = (1 << (pdata->lanes + 1)) - 1; + mask = (1 << (state->num_lanes + 1)) - 1; val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); @@ -321,15 +337,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) static void s5pcsis_set_params(struct csis_state *state) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val; val = s5pcsis_read(state, S5PCSIS_CONFIG); - val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1); + val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); s5pcsis_write(state, S5PCSIS_CONFIG, val); __s5pcsis_set_format(state); - s5pcsis_set_hsync_settle(state, pdata->hs_settle); + s5pcsis_set_hsync_settle(state, state->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); if (state->csis_fmt->data_alignment == 32) @@ -338,7 +353,7 @@ static void s5pcsis_set_params(struct csis_state *state) val &= ~S5PCSIS_CTRL_ALIGN_32BIT; val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; - if (pdata->wclk_source) + if (state->wclk_ext) val |= S5PCSIS_CTRL_WCLK_EXTCLK; s5pcsis_write(state, S5PCSIS_CTRL, val); @@ -534,10 +549,10 @@ static struct csis_pix_format const *s5pcsis_try_format( static struct v4l2_mbus_framefmt *__s5pcsis_get_format( struct csis_state *state, struct v4l2_subdev_fh *fh, - u32 pad, enum v4l2_subdev_format_whence which) + enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; return &state->format; } @@ -549,10 +564,7 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) - return -EINVAL; - - mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + mf = __s5pcsis_get_format(state, fh, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -579,10 +591,7 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) - return -EINVAL; - - mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); + mf = __s5pcsis_get_format(state, fh, fmt->which); if (!mf) return -EINVAL; @@ -701,52 +710,111 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int s5pcsis_get_platform_data(struct platform_device *pdev, + struct csis_state *state) +{ + struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data not specified\n"); + return -EINVAL; + } + + state->clk_frequency = pdata->clk_rate; + state->num_lanes = pdata->lanes; + state->hs_settle = pdata->hs_settle; + state->index = max(0, pdev->id); + state->max_num_lanes = state->index ? CSIS1_MAX_LANES : + CSIS0_MAX_LANES; + return 0; +} + +#ifdef CONFIG_OF +static int s5pcsis_parse_dt(struct platform_device *pdev, + struct csis_state *state) +{ + struct device_node *node = pdev->dev.of_node; + struct v4l2_of_endpoint endpoint; + + if (of_property_read_u32(node, "clock-frequency", + &state->clk_frequency)) + state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; + if (of_property_read_u32(node, "bus-width", + &state->max_num_lanes)) + return -EINVAL; + + node = v4l2_of_get_next_endpoint(node, NULL); + if (!node) { + dev_err(&pdev->dev, "No port node at %s\n", + node->full_name); + return -EINVAL; + } + /* Get port node and validate MIPI-CSI channel id. */ + v4l2_of_parse_endpoint(node, &endpoint); + + state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0; + if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES) + return -ENXIO; + + /* Get MIPI CSI-2 bus configration from the endpoint node. */ + of_property_read_u32(node, "samsung,csis-hs-settle", + &state->hs_settle); + state->wclk_ext = of_property_read_bool(node, + "samsung,csis-wclk"); + + state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; + + of_node_put(node); + return 0; +} +#else +#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) +#endif + static int s5pcsis_probe(struct platform_device *pdev) { - struct s5p_platform_mipi_csis *pdata; + struct device *dev = &pdev->dev; struct resource *mem_res; struct csis_state *state; int ret = -ENOMEM; int i; - state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; mutex_init(&state->lock); spin_lock_init(&state->slock); - state->pdev = pdev; - state->index = max(0, pdev->id); - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform data not fully specified\n"); - return -EINVAL; - } + if (dev->of_node) + ret = s5pcsis_parse_dt(pdev, state); + else + ret = s5pcsis_get_platform_data(pdev, state); + if (ret < 0) + return ret; - if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || - pdata->lanes > CSIS0_MAX_LANES) { - dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", - pdata->lanes); + if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { + dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n", + state->num_lanes, state->max_num_lanes); return -EINVAL; } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_ioremap_resource(&pdev->dev, mem_res); + state->regs = devm_ioremap_resource(dev, mem_res); if (IS_ERR(state->regs)) return PTR_ERR(state->regs); state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { - dev_err(&pdev->dev, "Failed to get irq\n"); + dev_err(dev, "Failed to get irq\n"); return state->irq; } for (i = 0; i < CSIS_NUM_SUPPLIES; i++) state->supplies[i].supply = csis_supply_name[i]; - ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) return ret; @@ -755,11 +823,11 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (pdata->clk_rate) + if (state->clk_frequency) ret = clk_set_rate(state->clock[CSIS_CLK_MUX], - pdata->clk_rate); + state->clk_frequency); else - dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + dev_WARN(dev, "No clock frequency specified!\n"); if (ret < 0) goto e_clkput; @@ -767,16 +835,17 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) goto e_clkput; - ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, - 0, dev_name(&pdev->dev), state); + ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, + 0, dev_name(dev), state); if (ret) { - dev_err(&pdev->dev, "Interrupt request failed\n"); + dev_err(dev, "Interrupt request failed\n"); goto e_clkdis; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); state->sd.owner = THIS_MODULE; - strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name)); + snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", + CSIS_SUBDEV_NAME, state->index); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->csis_fmt = &s5pcsis_formats[0]; @@ -796,10 +865,12 @@ static int s5pcsis_probe(struct platform_device *pdev) /* .. and a pointer to the subdev. */ platform_set_drvdata(pdev, &state->sd); - memcpy(state->events, s5pcsis_events, sizeof(state->events)); + pm_runtime_enable(dev); - pm_runtime_enable(&pdev->dev); + dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", + state->num_lanes, state->hs_settle, state->wclk_ext, + state->clk_frequency); return 0; e_clkdis: @@ -923,13 +994,21 @@ static const struct dev_pm_ops s5pcsis_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) }; +static const struct of_device_id s5pcsis_of_match[] = { + { .compatible = "samsung,s5pv210-csis" }, + { .compatible = "samsung,exynos4210-csis" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s5pcsis_of_match); + static struct platform_driver s5pcsis_driver = { .probe = s5pcsis_probe, .remove = s5pcsis_remove, .driver = { - .name = CSIS_DRIVER_NAME, - .owner = THIS_MODULE, - .pm = &s5pcsis_pm_ops, + .of_match_table = s5pcsis_of_match, + .name = CSIS_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &s5pcsis_pm_ops, }, }; diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.h b/drivers/media/platform/exynos4-is/mipi-csis.h index 2709286396e1..28c11c4085d8 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.h +++ b/drivers/media/platform/exynos4-is/mipi-csis.h @@ -11,6 +11,7 @@ #define S5P_MIPI_CSIS_H_ #define CSIS_DRIVER_NAME "s5p-mipi-csis" +#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME #define CSIS_MAX_ENTITIES 2 #define CSIS0_MAX_LANES 4 #define CSIS1_MAX_LANES 2 diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 5f7db3f1f6f5..3a6a0dcdc3e4 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -957,12 +957,12 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { struct viu_fh *fh = priv; - fh->dev->std = *id; - decoder_call(fh->dev, core, s_std, *id); + fh->dev->std = id; + decoder_call(fh->dev, core, s_std, id); return 0; } diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 6c4db9b98989..758564649589 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -207,6 +207,9 @@ static void dma_callback(void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -866,6 +869,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -882,6 +886,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 92a33f081852..64ab91edfb81 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1357,7 +1357,7 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i) } /* from vivi.c */ -static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a) { return 0; } @@ -1445,7 +1445,7 @@ static int mcam_vidioc_g_register(struct file *file, void *priv, } static int mcam_vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct mcam_camera *cam = priv; diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 7487d7208dea..4cc7f65d7d76 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -38,6 +38,10 @@ MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1.1"); +static unsigned debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -67,7 +71,7 @@ MODULE_VERSION("0.1.1"); #define MEM2MEM_VFLIP (1 << 1) #define dprintk(dev, fmt, arg...) \ - v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) static void m2mtest_dev_release(struct device *dev) @@ -234,6 +238,10 @@ static int device_process(struct m2mtest_ctx *ctx, bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; + memcpy(&out_vb->v4l2_buf.timestamp, + &in_vb->v4l2_buf.timestamp, + sizeof(struct timeval)); + switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: p_out += bytesperline * height - bytes_left; @@ -844,6 +852,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &m2mtest_qops; src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -855,6 +864,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &m2mtest_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 4b9e0a28616a..f7440e585b6b 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -377,6 +377,9 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; + src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; + spin_lock_irqsave(&pcdev->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -763,6 +766,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -774,6 +778,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 96c4a17e4280..477268a2415f 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -648,9 +648,12 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) /* First save the configuration in ovelray structure */ ret = omapvid_init(vout, addr); - if (ret) + if (ret) { printk(KERN_ERR VOUT_NAME "failed to set overlay info\n"); + goto vout_isr_err; + } + /* Enable the pipeline and set the Go bit */ ret = omapvid_apply_changes(vout); if (ret) @@ -1660,13 +1663,16 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2; - omap_dispc_register_isr(omap_vout_isr, vout, mask); - /* First save the configuration in ovelray structure */ ret = omapvid_init(vout, addr); - if (ret) + if (ret) { v4l2_err(&vout->vid_dev->v4l2_dev, "failed to set overlay info\n"); + goto streamon_err1; + } + + omap_dispc_register_isr(omap_vout_isr, vout, mask); + /* Enable the pipeline and set the Go bit */ ret = omapvid_apply_changes(vout); if (ret) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 6e5ad8ec0a22..1d7dbd5c0fba 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -55,6 +55,7 @@ #include <asm/cacheflush.h> #include <linux/clk.h> +#include <linux/clkdev.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> @@ -148,6 +149,201 @@ void omap3isp_flush(struct isp_device *isp) isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); } +/* ----------------------------------------------------------------------------- + * XCLK + */ + +#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw) + +static void isp_xclk_update(struct isp_xclk *xclk, u32 divider) +{ + switch (xclk->id) { + case ISP_XCLK_A: + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ISPTCTRL_CTRL_DIVA_MASK, + divider << ISPTCTRL_CTRL_DIVA_SHIFT); + break; + case ISP_XCLK_B: + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ISPTCTRL_CTRL_DIVB_MASK, + divider << ISPTCTRL_CTRL_DIVB_SHIFT); + break; + } +} + +static int isp_xclk_prepare(struct clk_hw *hw) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + + omap3isp_get(xclk->isp); + + return 0; +} + +static void isp_xclk_unprepare(struct clk_hw *hw) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + + omap3isp_put(xclk->isp); +} + +static int isp_xclk_enable(struct clk_hw *hw) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + unsigned long flags; + + spin_lock_irqsave(&xclk->lock, flags); + isp_xclk_update(xclk, xclk->divider); + xclk->enabled = true; + spin_unlock_irqrestore(&xclk->lock, flags); + + return 0; +} + +static void isp_xclk_disable(struct clk_hw *hw) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + unsigned long flags; + + spin_lock_irqsave(&xclk->lock, flags); + isp_xclk_update(xclk, 0); + xclk->enabled = false; + spin_unlock_irqrestore(&xclk->lock, flags); +} + +static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + + return parent_rate / xclk->divider; +} + +static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) +{ + u32 divider; + + if (*rate >= parent_rate) { + *rate = parent_rate; + return ISPTCTRL_CTRL_DIV_BYPASS; + } + + divider = DIV_ROUND_CLOSEST(parent_rate, *rate); + if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) + divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; + + *rate = parent_rate / divider; + return divider; +} + +static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + isp_xclk_calc_divider(&rate, *parent_rate); + return rate; +} + +static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct isp_xclk *xclk = to_isp_xclk(hw); + unsigned long flags; + u32 divider; + + divider = isp_xclk_calc_divider(&rate, parent_rate); + + spin_lock_irqsave(&xclk->lock, flags); + + xclk->divider = divider; + if (xclk->enabled) + isp_xclk_update(xclk, divider); + + spin_unlock_irqrestore(&xclk->lock, flags); + + dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n", + __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider); + return 0; +} + +static const struct clk_ops isp_xclk_ops = { + .prepare = isp_xclk_prepare, + .unprepare = isp_xclk_unprepare, + .enable = isp_xclk_enable, + .disable = isp_xclk_disable, + .recalc_rate = isp_xclk_recalc_rate, + .round_rate = isp_xclk_round_rate, + .set_rate = isp_xclk_set_rate, +}; + +static const char *isp_xclk_parent_name = "cam_mclk"; + +static const struct clk_init_data isp_xclk_init_data = { + .name = "cam_xclk", + .ops = &isp_xclk_ops, + .parent_names = &isp_xclk_parent_name, + .num_parents = 1, +}; + +static int isp_xclk_init(struct isp_device *isp) +{ + struct isp_platform_data *pdata = isp->pdata; + struct clk_init_data init; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { + struct isp_xclk *xclk = &isp->xclks[i]; + struct clk *clk; + + xclk->isp = isp; + xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; + xclk->divider = 1; + spin_lock_init(&xclk->lock); + + init.name = i == 0 ? "cam_xclka" : "cam_xclkb"; + init.ops = &isp_xclk_ops; + init.parent_names = &isp_xclk_parent_name; + init.num_parents = 1; + + xclk->hw.init = &init; + + clk = devm_clk_register(isp->dev, &xclk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (pdata->xclks[i].con_id == NULL && + pdata->xclks[i].dev_id == NULL) + continue; + + xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL); + if (xclk->lookup == NULL) + return -ENOMEM; + + xclk->lookup->con_id = pdata->xclks[i].con_id; + xclk->lookup->dev_id = pdata->xclks[i].dev_id; + xclk->lookup->clk = clk; + + clkdev_add(xclk->lookup); + } + + return 0; +} + +static void isp_xclk_cleanup(struct isp_device *isp) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { + struct isp_xclk *xclk = &isp->xclks[i]; + + if (xclk->lookup) + clkdev_drop(xclk->lookup); + } +} + +/* ----------------------------------------------------------------------------- + * Interrupts + */ + /* * isp_enable_interrupts - Enable ISP interrupts. * @isp: OMAP3 ISP device @@ -180,80 +376,6 @@ static void isp_disable_interrupts(struct isp_device *isp) isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); } -/** - * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. - * @isp: OMAP3 ISP device - * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high - * @xclksel: XCLK to configure (0 = A, 1 = B). - * - * Configures the specified MCLK divisor in the ISP timing control register - * (TCTRL_CTRL) to generate the desired xclk clock value. - * - * Divisor = cam_mclk_hz / xclk - * - * Returns the final frequency that is actually being generated - **/ -static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) -{ - u32 divisor; - u32 currentxclk; - unsigned long mclk_hz; - - if (!omap3isp_get(isp)) - return 0; - - mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); - - if (xclk >= mclk_hz) { - divisor = ISPTCTRL_CTRL_DIV_BYPASS; - currentxclk = mclk_hz; - } else if (xclk >= 2) { - divisor = mclk_hz / xclk; - if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) - divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; - currentxclk = mclk_hz / divisor; - } else { - divisor = xclk; - currentxclk = 0; - } - - switch (xclksel) { - case ISP_XCLK_A: - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, - ISPTCTRL_CTRL_DIVA_MASK, - divisor << ISPTCTRL_CTRL_DIVA_SHIFT); - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", - currentxclk); - break; - case ISP_XCLK_B: - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, - ISPTCTRL_CTRL_DIVB_MASK, - divisor << ISPTCTRL_CTRL_DIVB_SHIFT); - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", - currentxclk); - break; - case ISP_XCLK_NONE: - default: - omap3isp_put(isp); - dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " - "xclk. Must be 0 (A) or 1 (B).\n"); - return -EINVAL; - } - - /* Do we go from stable whatever to clock? */ - if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2) - omap3isp_get(isp); - /* Stopping the clock. */ - else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2) - omap3isp_put(isp); - - isp->xclk_divisor[xclksel - 1] = divisor; - - omap3isp_put(isp); - - return currentxclk; -} - /* * isp_core_init - ISP core settings * @isp: OMAP3 ISP device @@ -1969,6 +2091,7 @@ static int isp_remove(struct platform_device *pdev) isp_unregister_entities(isp); isp_cleanup_modules(isp); + isp_xclk_cleanup(isp); __omap3isp_get(isp, false); iommu_detach_device(isp->domain, &pdev->dev); @@ -2042,7 +2165,6 @@ static int isp_probe(struct platform_device *pdev) } isp->autoidle = autoidle; - isp->platform_cb.set_xclk = isp_set_xclk; mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); @@ -2093,6 +2215,10 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_isp; + ret = isp_xclk_init(isp); + if (ret < 0) + goto error_isp; + /* Memory resources */ for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) if (isp->revision == isp_res_maps[m].isp_rev) @@ -2162,6 +2288,7 @@ detach_dev: free_domain: iommu_domain_free(isp->domain); error_isp: + isp_xclk_cleanup(isp); omap3isp_put(isp); error: platform_set_drvdata(pdev, NULL); diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index c77e1f2ae5ca..cd3eff45ae7d 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -29,6 +29,7 @@ #include <media/omap3isp.h> #include <media/v4l2-device.h> +#include <linux/clk-provider.h> #include <linux/device.h> #include <linux/io.h> #include <linux/iommu.h> @@ -125,8 +126,20 @@ struct isp_reg { u32 val; }; -struct isp_platform_callback { - u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); +enum isp_xclk_id { + ISP_XCLK_A, + ISP_XCLK_B, +}; + +struct isp_xclk { + struct isp_device *isp; + struct clk_hw hw; + struct clk_lookup *lookup; + enum isp_xclk_id id; + + spinlock_t lock; /* Protects enabled and divider */ + bool enabled; + unsigned int divider; }; /* @@ -149,6 +162,7 @@ struct isp_platform_callback { * @cam_mclk: Pointer to camera functional clock structure. * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. * @l3_ick: Pointer to OMAP3 L3 bus interface clock. + * @xclks: External clocks provided by the ISP * @irq: Currently attached ISP ISR callbacks information structure. * @isp_af: Pointer to current settings for ISP AutoFocus SCM. * @isp_hist: Pointer to current settings for ISP Histogram SCM. @@ -185,12 +199,12 @@ struct isp_device { int has_context; int ref_count; unsigned int autoidle; - u32 xclk_divisor[2]; /* Two clocks, a and b. */ #define ISP_CLK_CAM_ICK 0 #define ISP_CLK_CAM_MCLK 1 #define ISP_CLK_CSI2_FCK 2 #define ISP_CLK_L3_ICK 3 struct clk *clock[4]; + struct isp_xclk xclks[2]; /* ISP modules */ struct ispstat isp_af; @@ -209,8 +223,6 @@ struct isp_device { unsigned int subclk_resources; struct iommu_domain *domain; - - struct isp_platform_callback platform_cb; }; #define v4l2_dev_to_isp_device(dev) \ diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index a55793c3d811..70438a0f62ae 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -934,12 +934,19 @@ static int s3c_camif_reqbufs(struct file *file, void *priv, 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; + if (ret < 0) + return ret; + + if (rb->count && rb->count < CAMIF_REQ_BUFS_MIN) { + rb->count = 0; + vb2_reqbufs(&vp->vb_queue, rb); + ret = -ENOMEM; } + vp->reqbufs_count = rb->count; + if (vp->owner == NULL && rb->count > 0) + vp->owner = priv; + return ret; } @@ -1153,6 +1160,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct camif_buffer); q->drv_priv = vp; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index aaaf276a5a6c..553d87e5ceab 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <media/v4l2-mem2mem.h> @@ -157,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &g2d_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -168,6 +170,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &g2d_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -634,6 +637,9 @@ static irqreturn_t g2d_isr(int irq, void *prv) BUG_ON(src == NULL); BUG_ON(dst == NULL); + dst->v4l2_buf.timecode = src->v4l2_buf.timecode; + dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); @@ -695,11 +701,14 @@ static struct v4l2_m2m_ops g2d_m2m_ops = { .unlock = g2d_unlock, }; +static const struct of_device_id exynos_g2d_match[]; + static int g2d_probe(struct platform_device *pdev) { struct g2d_dev *dev; struct video_device *vfd; struct resource *res; + const struct of_device_id *of_id; int ret = 0; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -794,7 +803,17 @@ static int g2d_probe(struct platform_device *pdev) } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - dev->variant = g2d_get_drv_data(pdev); + + if (!pdev->dev.of_node) { + dev->variant = g2d_get_drv_data(pdev); + } else { + of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); + if (!of_id) { + ret = -ENODEV; + goto unreg_video_dev; + } + dev->variant = (struct g2d_variant *)of_id->data; + } return 0; @@ -835,13 +854,25 @@ static int g2d_remove(struct platform_device *pdev) } static struct g2d_variant g2d_drvdata_v3x = { - .hw_rev = TYPE_G2D_3X, + .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ }; static struct g2d_variant g2d_drvdata_v4x = { .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ }; +static const struct of_device_id exynos_g2d_match[] = { + { + .compatible = "samsung,s5pv210-g2d", + .data = &g2d_drvdata_v3x, + }, { + .compatible = "samsung,exynos4212-g2d", + .data = &g2d_drvdata_v4x, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_g2d_match); + static struct platform_device_id g2d_driver_ids[] = { { .name = "s5p-g2d", @@ -861,6 +892,7 @@ static struct platform_driver g2d_pdrv = { .driver = { .name = G2D_NAME, .owner = THIS_MODULE, + .of_match_table = exynos_g2d_match, }, }; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3b023752bcb4..15d23968d1de 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1229,6 +1229,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &s5p_jpeg_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1240,6 +1241,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &s5p_jpeg_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1287,6 +1289,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) payload_size = jpeg_compressed_size(jpeg->regs); } + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + v4l2_m2m_buf_done(src_buf, state); if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(dst_buf, 0, payload_size); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e84703c314ce..01f9ae0dadb0 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -243,12 +243,10 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { - memcpy(&dst_buf->b->v4l2_buf.timecode, - &src_buf->b->v4l2_buf.timecode, - sizeof(struct v4l2_timecode)); - memcpy(&dst_buf->b->v4l2_buf.timestamp, - &src_buf->b->v4l2_buf.timestamp, - sizeof(struct timeval)); + dst_buf->b->v4l2_buf.timecode = + src_buf->b->v4l2_buf.timecode; + dst_buf->b->v4l2_buf.timestamp = + src_buf->b->v4l2_buf.timestamp; switch (frame_type) { case S5P_FIMV_DECODE_FRAME_I_FRAME: dst_buf->b->v4l2_buf.flags |= @@ -276,7 +274,7 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) unsigned int frame_type; dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); - frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); + frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx); /* If frame is same as previous then skip and do not dequeue */ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) { @@ -386,6 +384,8 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, } else { mfc_debug(2, "MFC needs next buffer\n"); ctx->consumed_stream = 0; + if (src_buf->flags & MFC_BUF_FLAG_EOS) + ctx->state = MFCINST_FINISHING; list_del(&src_buf->list); ctx->src_queue_cnt--; if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0) @@ -804,6 +804,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(capture)\n"); @@ -825,6 +826,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(output)\n"); @@ -1016,7 +1018,7 @@ static void *mfc_get_drv_data(struct platform_device *pdev); static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) { - unsigned int mem_info[2]; + unsigned int mem_info[2] = { }; dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, sizeof(struct device), GFP_KERNEL); @@ -1106,7 +1108,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) } if (pdev->dev.of_node) { - if (s5p_mfc_alloc_memdevs(dev) < 0) + ret = s5p_mfc_alloc_memdevs(dev); + if (ret < 0) goto err_res; } else { dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c index 138778083c63..ad4f1df0a18e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c @@ -16,7 +16,7 @@ #include "s5p_mfc_debug.h" /* This function is used to send a command to the MFC */ -int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, +static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, struct s5p_mfc_cmd_args *args) { int cur_cmd; @@ -41,7 +41,7 @@ int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd, } /* Initialize the MFC */ -int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -52,7 +52,7 @@ int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev) } /* Suspend the MFC hardware */ -int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -61,7 +61,7 @@ int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev) } /* Wake up the MFC hardware */ -int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -71,7 +71,7 @@ int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev) } -int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; @@ -124,7 +124,7 @@ int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx) return ret; } -int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index 754bfbcb1c43..5708fc3d9b4d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -17,7 +17,7 @@ #include "s5p_mfc_intr.h" #include "s5p_mfc_opr.h" -int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, +static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, struct s5p_mfc_cmd_args *args) { mfc_debug(2, "Issue the command: %d\n", cmd); @@ -32,7 +32,7 @@ int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd, return 0; } -int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; @@ -44,7 +44,7 @@ int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) &h2r_args); } -int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -53,7 +53,7 @@ int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev) &h2r_args); } -int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; @@ -63,7 +63,7 @@ int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev) } /* Open a new instance and get its number */ -int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; @@ -121,7 +121,7 @@ int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) } /* Close instance */ -int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_cmd_args h2r_args; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 4582473978ca..4af53bd2f182 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -22,6 +22,7 @@ #include <linux/videodev2.h> #include <linux/workqueue.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/videobuf2-core.h> #include "s5p_mfc_common.h" #include "s5p_mfc_debug.h" @@ -623,17 +624,27 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) /* Dequeue a buffer */ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { + const struct v4l2_event ev = { + .type = V4L2_EVENT_EOS + }; struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + int ret; if (ctx->state == MFCINST_ERROR) { mfc_err("Call on DQBUF after unrecoverable error\n"); return -EIO; } if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - return -EINVAL; + ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + if (ret == 0 && ctx->state == MFCINST_FINISHED && + list_empty(&ctx->vq_dst.done_list)) + v4l2_event_queue_fh(&ctx->fh, &ev); + } else { + ret = -EINVAL; + } + return ret; } /* Export DMA buffer */ @@ -809,6 +820,59 @@ static int vidioc_g_crop(struct file *file, void *priv, return 0; } +int vidioc_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + struct s5p_mfc_dev *dev = ctx->dev; + struct s5p_mfc_buf *buf; + unsigned long flags; + + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + if (cmd->flags != 0) + return -EINVAL; + + if (!ctx->vq_src.streaming) + return -EINVAL; + + spin_lock_irqsave(&dev->irqlock, flags); + if (list_empty(&ctx->src_queue)) { + mfc_err("EOS: empty src queue, entering finishing state"); + ctx->state = MFCINST_FINISHING; + if (s5p_mfc_ctx_ready(ctx)) + set_work_bit_irqsave(ctx); + spin_unlock_irqrestore(&dev->irqlock, flags); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + } else { + mfc_err("EOS: marking last buffer of stream"); + buf = list_entry(ctx->src_queue.prev, + struct s5p_mfc_buf, list); + if (buf->flags & MFC_BUF_FLAG_USED) + ctx->state = MFCINST_FINISHING; + else + buf->flags |= MFC_BUF_FLAG_EOS; + spin_unlock_irqrestore(&dev->irqlock, flags); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); + default: + return -EINVAL; + } +} + + /* v4l2_ioctl_ops */ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -830,6 +894,9 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_crop = vidioc_g_crop, + .vidioc_decoder_cmd = vidioc_decoder_cmd, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static int s5p_mfc_queue_setup(struct vb2_queue *vq, @@ -1147,3 +1214,4 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n", (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt); } + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2356fd52a169..4f6b553c4b2d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -232,6 +232,7 @@ static struct mfc_control controls[] = { .minimum = 0, .maximum = 1, .default_value = 0, + .step = 1, .menu_skip_mask = 0, }, { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index f61dba837899..0af05a2d1cd4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -34,7 +34,7 @@ #define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT) /* Allocate temporary buffers for decoding */ -int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; @@ -55,13 +55,13 @@ int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) /* Release temporary buffers for decoding */ -void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc); } /* Allocate codec buffers */ -int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int enc_ref_y_size = 0; @@ -193,14 +193,14 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) } /* Release buffers allocated for codec */ -void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2); } /* Allocate memory for instance data buffer */ -int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; @@ -241,20 +241,20 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) } /* Release instance buffer */ -void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm); } -int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) { /* NOP */ return 0; } -void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) { /* NOP */ } @@ -273,7 +273,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, return readl(ctx->shm.virt + ofs); } -void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) { unsigned int guard_width, guard_height; @@ -315,7 +315,7 @@ void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) } } -void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx) { if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) { ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN); @@ -361,8 +361,9 @@ static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) } /* Set registers for decoding stream buffer */ -int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr, - unsigned int start_num_byte, unsigned int buf_size) +static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, + int buf_addr, unsigned int start_num_byte, + unsigned int buf_size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -374,7 +375,7 @@ int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr, } /* Set decoding frame buffer */ -int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) { unsigned int frame_size, i; unsigned int frame_size_ch, frame_size_mv; @@ -506,7 +507,7 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) } /* Set registers for encoding stream buffer */ -int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -516,7 +517,7 @@ int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx, return 0; } -void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long y_addr, unsigned long c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -525,7 +526,7 @@ void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR); } -void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, unsigned long *y_addr, unsigned long *c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -537,7 +538,7 @@ void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx, } /* Set encoding ref & codec buffer */ -int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; size_t buf_addr1, buf_addr2; @@ -1041,7 +1042,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) } /* Initialize decoding */ -int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1077,7 +1078,7 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) } /* Decode a single frame */ -int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, enum s5p_mfc_decode_arg last_frame) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1106,7 +1107,7 @@ int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx, return 0; } -int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1128,7 +1129,7 @@ int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx) } /* Encode a single frame */ -int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; int cmd; @@ -1187,6 +1188,15 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) unsigned long flags; unsigned int index; + if (ctx->state == MFCINST_FINISHING) { + last_frame = MFC_DEC_LAST_FRAME; + s5p_mfc_set_dec_stream_buffer_v5(ctx, 0, 0, 0); + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_decode_one_frame_v5(ctx, last_frame); + return 0; + } + spin_lock_irqsave(&dev->irqlock, flags); /* Frames are being decoded */ if (list_empty(&ctx->src_queue)) { @@ -1353,7 +1363,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) } /* Try running an operation on hardware */ -void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) { struct s5p_mfc_ctx *ctx; int new_ctx; @@ -1469,7 +1479,7 @@ void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) } -void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) +static void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) { struct s5p_mfc_buf *b; int i; @@ -1483,52 +1493,52 @@ void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) } } -void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev) +static void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev) { mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT); mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD); mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID); } -int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DISPLAY_Y_ADR) << MFC_OFFSET_SHIFT; } -int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DECODE_Y_ADR) << MFC_OFFSET_SHIFT; } -int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DISPLAY_STATUS); } -int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_DECODE_STATUS); } -int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_DECODE_FRAME_TYPE) & S5P_FIMV_DECODE_FRAME_MASK; } -int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx) { return (s5p_mfc_read_info_v5(ctx, DISP_PIC_FRAME_TYPE) >> S5P_FIMV_SHARED_DISP_FRAME_TYPE_SHIFT) & S5P_FIMV_DECODE_FRAME_MASK; } -int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_CONSUMED_BYTES); } -int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) { int reason; reason = mfc_read(dev, S5P_FIMV_RISC2HOST_CMD) & @@ -1576,98 +1586,98 @@ int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev) return reason; } -int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG2); } -int s5p_mfc_err_dec_v5(unsigned int err) +static int s5p_mfc_err_dec_v5(unsigned int err) { return (err & S5P_FIMV_ERR_DEC_MASK) >> S5P_FIMV_ERR_DEC_SHIFT; } -int s5p_mfc_err_dspl_v5(unsigned int err) +static int s5p_mfc_err_dspl_v5(unsigned int err) { return (err & S5P_FIMV_ERR_DSPL_MASK) >> S5P_FIMV_ERR_DSPL_SHIFT; } -int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_HRESOL); } -int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_VRESOL); } -int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_SI_BUF_NUMBER); } -int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev) { /* NOP */ return -1; } -int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG1); } -int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_STRM_SIZE); } -int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_SLICE_TYPE); } -int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev) { return -1; } -int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT); } -int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, FRAME_PACK_SEI_AVAIL); } -int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev) { return -1; } -int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev) { return -1; } -unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, PIC_TIME_TOP); } -unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, PIC_TIME_BOT); } -unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, CROP_INFO_H); } -unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v5(ctx, CROP_INFO_V); } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index beb6dbacebd9..7e76fce2e524 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -49,7 +49,7 @@ #define OFFSETB(x) (((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET) /* Allocate temporary buffers for decoding */ -int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) { /* NOP */ @@ -57,19 +57,19 @@ int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) } /* Release temproary buffers for decoding */ -void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx) { /* NOP */ } -int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) { /* NOP */ return -1; } /* Allocate codec buffers */ -int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; @@ -203,13 +203,13 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) } /* Release buffers allocated for codec */ -void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); } /* Allocate memory for instance data buffer */ -int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; @@ -258,13 +258,13 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) } /* Release instance buffer */ -void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); } /* Allocate context buffers for SYS_INIT */ -int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; int ret; @@ -287,7 +287,7 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) } /* Release context buffers for SYS_INIT */ -void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf); } @@ -306,7 +306,7 @@ static int calc_plane(int width, int height) (mbY * S5P_FIMV_NUM_PIXELS_IN_MB_ROW_V6); } -void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) { ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6); ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6); @@ -326,7 +326,7 @@ void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) } } -void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) { unsigned int mb_width, mb_height; @@ -339,8 +339,9 @@ void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) } /* Set registers for decoding stream buffer */ -int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, int buf_addr, - unsigned int start_num_byte, unsigned int strm_size) +static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, + int buf_addr, unsigned int start_num_byte, + unsigned int strm_size) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size; @@ -359,7 +360,7 @@ int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, int buf_addr, } /* Set decoding frame buffer */ -int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) { unsigned int frame_size, i; unsigned int frame_size_ch, frame_size_mv; @@ -440,7 +441,7 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) } /* Set registers for encoding stream buffer */ -int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size) { struct s5p_mfc_dev *dev = ctx->dev; @@ -454,7 +455,7 @@ int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, return 0; } -void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long y_addr, unsigned long c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -466,7 +467,7 @@ void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, mfc_debug(2, "enc src c buf addr: 0x%08lx", c_addr); } -void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, +static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long *y_addr, unsigned long *c_addr) { struct s5p_mfc_dev *dev = ctx->dev; @@ -483,7 +484,7 @@ void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, } /* Set encoding ref & codec buffer */ -int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; size_t buf_addr1; @@ -1147,7 +1148,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) } /* Initialize decoding */ -int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int reg = 0; @@ -1215,7 +1216,7 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) } /* Decode a single frame */ -int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, +static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, enum s5p_mfc_decode_arg last_frame) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1244,7 +1245,7 @@ int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, return 0; } -int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1267,7 +1268,7 @@ int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) return 0; } -int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_enc_params *p = &ctx->enc_params; @@ -1283,7 +1284,7 @@ int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) } /* Encode a single frame */ -int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1312,7 +1313,7 @@ static inline int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev) int cnt; spin_lock_irqsave(&dev->condlock, flags); - mfc_debug(2, "Previos context: %d (bits %08lx)\n", dev->curr_ctx, + mfc_debug(2, "Previous context: %d (bits %08lx)\n", dev->curr_ctx, dev->ctx_work_bits); new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS; cnt = 0; @@ -1362,8 +1363,16 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) unsigned long flags; int last_frame = 0; - spin_lock_irqsave(&dev->irqlock, flags); + if (ctx->state == MFCINST_FINISHING) { + last_frame = MFC_DEC_LAST_FRAME; + s5p_mfc_set_dec_stream_buffer_v6(ctx, 0, 0, 0); + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_decode_one_frame_v6(ctx, last_frame); + return 0; + } + spin_lock_irqsave(&dev->irqlock, flags); /* Frames are being decoded */ if (list_empty(&ctx->src_queue)) { mfc_debug(2, "No src buffers.\n"); @@ -1540,7 +1549,7 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx) } /* Try running an operation on hardware */ -void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_ctx *ctx; int new_ctx; @@ -1663,7 +1672,7 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) } -void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) +static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) { struct s5p_mfc_buf *b; int i; @@ -1677,13 +1686,13 @@ void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) } } -void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) +static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) { mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6); mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6); } -void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, +static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { struct s5p_mfc_dev *dev = ctx->dev; @@ -1693,7 +1702,8 @@ void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, s5p_mfc_clock_off(); } -unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) +static unsigned int +s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) { struct s5p_mfc_dev *dev = ctx->dev; int ret; @@ -1705,140 +1715,140 @@ unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) return ret; } -int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); } -int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); } -int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6); } -int s5p_mfc_get_decoded_status_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_decoded_status_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6); } -int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) & S5P_FIMV_DECODE_FRAME_MASK_V6; } -int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) { return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) & S5P_FIMV_DECODE_FRAME_MASK_V6; } -int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6); } -int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) & S5P_FIMV_RISC2HOST_CMD_MASK; } -int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6); } -int s5p_mfc_err_dec_v6(unsigned int err) +static int s5p_mfc_err_dec_v6(unsigned int err) { return (err & S5P_FIMV_ERR_DEC_MASK_V6) >> S5P_FIMV_ERR_DEC_SHIFT_V6; } -int s5p_mfc_err_dspl_v6(unsigned int err) +static int s5p_mfc_err_dspl_v6(unsigned int err) { return (err & S5P_FIMV_ERR_DSPL_MASK_V6) >> S5P_FIMV_ERR_DSPL_SHIFT_V6; } -int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); } -int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); } -int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6); } -int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6); } -int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6); } -int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6); } -int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6); } -int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6); } -int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6); } -int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) +static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) { return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); } -int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6); } -int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) +static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) { return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6); } -unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6); } -unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6); } -unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6); } -unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) +static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) { return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6); } diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8de1b3dce459..4e86626dad4b 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -31,6 +31,7 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <linux/v4l2-dv-timings.h> #include <media/s5p_hdmi.h> #include <media/v4l2-common.h> @@ -43,9 +44,6 @@ MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); -/* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 - struct hdmi_pulse { u32 beg; u32 end; @@ -91,8 +89,8 @@ struct hdmi_device { const struct hdmi_timings *cur_conf; /** flag indicating that timings are dirty */ int cur_conf_dirty; - /** current preset */ - u32 cur_preset; + /** current timings */ + struct v4l2_dv_timings cur_timings; /** other resources */ struct hdmi_resources res; }; @@ -252,7 +250,6 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) { struct device *dev = hdmi_dev->dev; const struct hdmi_timings *conf = hdmi_dev->cur_conf; - struct v4l2_dv_preset preset; int ret; dev_dbg(dev, "%s\n", __func__); @@ -267,11 +264,11 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); - /* configure presets */ - preset.preset = hdmi_dev->cur_preset; - ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset); + /* configure timings */ + ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_timings, + &hdmi_dev->cur_timings); if (ret) { - dev_err(dev, "failed to set preset (%u)\n", preset.preset); + dev_err(dev, "failed to set timings\n"); return ret; } @@ -475,33 +472,26 @@ static const struct hdmi_timings hdmi_timings_1080p50 = { .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; +/* default hdmi_timings index of the timings configured on probe */ +#define HDMI_DEFAULT_TIMINGS_IDX (0) + static const struct { - u32 preset; - const struct hdmi_timings *timings; + bool reduced_fps; + const struct v4l2_dv_timings dv_timings; + const struct hdmi_timings *hdmi_timings; } hdmi_timings[] = { - { V4L2_DV_480P59_94, &hdmi_timings_480p }, - { V4L2_DV_576P50, &hdmi_timings_576p50 }, - { V4L2_DV_720P50, &hdmi_timings_720p50 }, - { V4L2_DV_720P59_94, &hdmi_timings_720p60 }, - { V4L2_DV_720P60, &hdmi_timings_720p60 }, - { V4L2_DV_1080P24, &hdmi_timings_1080p24 }, - { V4L2_DV_1080P30, &hdmi_timings_1080p60 }, - { V4L2_DV_1080P50, &hdmi_timings_1080p50 }, - { V4L2_DV_1080I50, &hdmi_timings_1080i50 }, - { V4L2_DV_1080I60, &hdmi_timings_1080i60 }, - { V4L2_DV_1080P60, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_720X480P59_94, &hdmi_timings_480p }, + { false, V4L2_DV_BT_CEA_720X576P50, &hdmi_timings_576p50 }, + { false, V4L2_DV_BT_CEA_1280X720P50, &hdmi_timings_720p50 }, + { true, V4L2_DV_BT_CEA_1280X720P60, &hdmi_timings_720p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P24, &hdmi_timings_1080p24 }, + { false, V4L2_DV_BT_CEA_1920X1080P30, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P50, &hdmi_timings_1080p50 }, + { false, V4L2_DV_BT_CEA_1920X1080I50, &hdmi_timings_1080i50 }, + { false, V4L2_DV_BT_CEA_1920X1080I60, &hdmi_timings_1080i60 }, + { false, V4L2_DV_BT_CEA_1920X1080P60, &hdmi_timings_1080p60 }, }; -static const struct hdmi_timings *hdmi_preset2timings(u32 preset) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i) - if (hdmi_timings[i].preset == preset) - return hdmi_timings[i].timings; - return NULL; -} - static int hdmi_streamon(struct hdmi_device *hdev) { struct device *dev = hdev->dev; @@ -621,29 +611,33 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on) return IS_ERR_VALUE(ret) ? ret : 0; } -static int hdmi_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); struct device *dev = hdev->dev; - const struct hdmi_timings *conf; + int i; - conf = hdmi_preset2timings(preset->preset); - if (conf == NULL) { - dev_err(dev, "preset (%u) not supported\n", preset->preset); + for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) + if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings, + timings, 0)) + break; + if (i == ARRAY_SIZE(hdmi_timings)) { + dev_err(dev, "timings not supported\n"); return -EINVAL; } - hdev->cur_conf = conf; + hdev->cur_conf = hdmi_timings[i].hdmi_timings; hdev->cur_conf_dirty = 1; - hdev->cur_preset = preset->preset; + hdev->cur_timings = *timings; + if (!hdmi_timings[i].reduced_fps) + hdev->cur_timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; return 0; } -static int hdmi_g_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { - memset(preset, 0, sizeof(*preset)); - preset->preset = sd_to_hdmi_dev(sd)->cur_preset; + *timings = sd_to_hdmi_dev(sd)->cur_timings; return 0; } @@ -670,13 +664,33 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) +static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) { - if (preset->index >= ARRAY_SIZE(hdmi_timings)) + if (timings->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; - return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset, - preset); + timings->timings = hdmi_timings[timings->index].dv_timings; + if (!hdmi_timings[timings->index].reduced_fps) + timings->timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; + return 0; +} + +static int hdmi_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + struct hdmi_device *hdev = sd_to_hdmi_dev(sd); + + /* Let the phy fill in the pixelclock range */ + v4l2_subdev_call(hdev->phy_sd, video, dv_timings_cap, cap); + cap->type = V4L2_DV_BT_656_1120; + cap->bt.min_width = 720; + cap->bt.max_width = 1920; + cap->bt.min_height = 480; + cap->bt.max_height = 1080; + cap->bt.standards = V4L2_DV_BT_STD_CEA861; + cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | + V4L2_DV_BT_CAP_PROGRESSIVE; + return 0; } static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { @@ -684,9 +698,10 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { }; static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { - .s_dv_preset = hdmi_s_dv_preset, - .g_dv_preset = hdmi_g_dv_preset, - .enum_dv_presets = hdmi_enum_dv_presets, + .s_dv_timings = hdmi_s_dv_timings, + .g_dv_timings = hdmi_g_dv_timings, + .enum_dv_timings = hdmi_enum_dv_timings, + .dv_timings_cap = hdmi_dv_timings_cap, .g_mbus_fmt = hdmi_g_mbus_fmt, .s_stream = hdmi_s_stream, }; @@ -956,9 +971,11 @@ static int hdmi_probe(struct platform_device *pdev) sd->owner = THIS_MODULE; strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); - hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; - /* FIXME: missing fail preset is not supported */ - hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); + hdmi_dev->cur_timings = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].dv_timings; + /* FIXME: missing fail timings is not supported */ + hdmi_dev->cur_conf = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].hdmi_timings; hdmi_dev->cur_conf_dirty = 1; /* storing subdev for call that have only access to struct device */ diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index 80717cec76ae..e19a0af1ea4f 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -176,35 +176,9 @@ static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) return container_of(sd, struct hdmiphy_ctx, sd); } -static unsigned long hdmiphy_preset_to_pixclk(u32 preset) +static const u8 *hdmiphy_find_conf(unsigned long pixclk, + const struct hdmiphy_conf *conf) { - static const unsigned long pixclk[] = { - [V4L2_DV_480P59_94] = 27000000, - [V4L2_DV_576P50] = 27000000, - [V4L2_DV_720P59_94] = 74176000, - [V4L2_DV_720P50] = 74250000, - [V4L2_DV_720P60] = 74250000, - [V4L2_DV_1080P24] = 74250000, - [V4L2_DV_1080P30] = 74250000, - [V4L2_DV_1080I50] = 74250000, - [V4L2_DV_1080I60] = 74250000, - [V4L2_DV_1080P50] = 148500000, - [V4L2_DV_1080P60] = 148500000, - }; - if (preset < ARRAY_SIZE(pixclk)) - return pixclk[preset]; - else - return 0; -} - -static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf) -{ - unsigned long pixclk; - - pixclk = hdmiphy_preset_to_pixclk(preset); - if (!pixclk) - return NULL; - for (; conf->pixclk; ++conf) if (conf->pixclk == pixclk) return conf->data; @@ -217,8 +191,8 @@ static int hdmiphy_s_power(struct v4l2_subdev *sd, int on) return 0; } -static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { const u8 *data; u8 buffer[32]; @@ -226,9 +200,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, struct hdmiphy_ctx *ctx = sd_to_ctx(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; + unsigned long pixclk = timings->bt.pixelclock; - dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset); - data = hdmiphy_find_conf(preset->preset, ctx->conf_tab); + dev_info(dev, "s_dv_timings\n"); + if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000) + pixclk = 74176000; + data = hdmiphy_find_conf(pixclk, ctx->conf_tab); if (!data) { dev_err(dev, "format not supported\n"); return -EINVAL; @@ -245,6 +222,17 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, return 0; } +static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + cap->type = V4L2_DV_BT_656_1120; + /* The phy only determines the pixelclock, leave the other values + * at 0 to signify that we have no information for them. */ + cap->bt.min_pixelclock = 27000000; + cap->bt.max_pixelclock = 148500000; + return 0; +} + static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -270,7 +258,8 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { }; static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { - .s_dv_preset = hdmiphy_s_dv_preset, + .s_dv_timings = hdmiphy_s_dv_timings, + .dv_timings_cap = hdmiphy_dv_timings_cap, .s_stream = hdmiphy_s_stream, }; diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 82142a2d6d93..ef0efdf422fe 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -501,8 +501,8 @@ fail: return -ERANGE; } -static int mxr_enum_dv_presets(struct file *file, void *fh, - struct v4l2_dv_enum_preset *preset) +static int mxr_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -510,14 +510,14 @@ static int mxr_enum_dv_presets(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_timings, timings); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; } -static int mxr_s_dv_preset(struct file *file, void *fh, - struct v4l2_dv_preset *preset) +static int mxr_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -526,7 +526,7 @@ static int mxr_s_dv_preset(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - /* preset change cannot be done while there is an entity + /* timings change cannot be done while there is an entity * dependant on output configuration */ if (mdev->n_output > 0) { @@ -534,7 +534,7 @@ static int mxr_s_dv_preset(struct file *file, void *fh, return -EBUSY; } - ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_timings, timings); mutex_unlock(&mdev->mutex); @@ -544,8 +544,8 @@ static int mxr_s_dv_preset(struct file *file, void *fh, return ret ? -EINVAL : 0; } -static int mxr_g_dv_preset(struct file *file, void *fh, - struct v4l2_dv_preset *preset) +static int mxr_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -553,13 +553,28 @@ static int mxr_g_dv_preset(struct file *file, void *fh, /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset); + ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_timings, timings); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; } -static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int mxr_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + struct mxr_layer *layer = video_drvdata(file); + struct mxr_device *mdev = layer->mdev; + int ret; + + /* lock protects from changing sd_out */ + mutex_lock(&mdev->mutex); + ret = v4l2_subdev_call(to_outsd(mdev), video, dv_timings_cap, cap); + mutex_unlock(&mdev->mutex); + + return ret ? -EINVAL : 0; +} + +static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; @@ -576,7 +591,7 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) return -EBUSY; } - ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm); + ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, norm); mutex_unlock(&mdev->mutex); @@ -616,8 +631,8 @@ static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) /* try to obtain supported tv norms */ v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); a->capabilities = 0; - if (sd->ops->video && sd->ops->video->s_dv_preset) - a->capabilities |= V4L2_OUT_CAP_PRESETS; + if (sd->ops->video && sd->ops->video->s_dv_timings) + a->capabilities |= V4L2_OUT_CAP_DV_TIMINGS; if (sd->ops->video && sd->ops->video->s_std_output) a->capabilities |= V4L2_OUT_CAP_STD; a->type = V4L2_OUTPUT_TYPE_ANALOG; @@ -738,10 +753,11 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = { /* Streaming control */ .vidioc_streamon = mxr_streamon, .vidioc_streamoff = mxr_streamoff, - /* Preset functions */ - .vidioc_enum_dv_presets = mxr_enum_dv_presets, - .vidioc_s_dv_preset = mxr_s_dv_preset, - .vidioc_g_dv_preset = mxr_g_dv_preset, + /* DV Timings functions */ + .vidioc_enum_dv_timings = mxr_enum_dv_timings, + .vidioc_s_dv_timings = mxr_s_dv_timings, + .vidioc_g_dv_timings = mxr_g_dv_timings, + .vidioc_dv_timings_cap = mxr_dv_timings_cap, /* analog TV standard functions */ .vidioc_s_std = mxr_s_std, .vidioc_g_std = mxr_g_std, diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index d90d2286090b..39b77d24b9c2 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -23,9 +23,6 @@ #include <linux/regulator/machine.h> #include <linux/slab.h> -#include <mach/gpio.h> -#include <plat/gpio-cfg.h> - #include <media/sii9234.h> #include <media/v4l2-subdev.h> diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index cb54c69d5748..0b32cc3f6a47 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation */ +#include <linux/err.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/module.h> @@ -1164,9 +1165,9 @@ static int sh_veu_probe(struct platform_device *pdev) veu->is_2h = resource_size(reg_res) == 0x22c; - veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); - if (!veu->base) - return -ENOMEM; + veu->base = devm_ioremap_resource(&pdev->dev, reg_res); + if (IS_ERR(veu->base)) + return PTR_ERR(veu->base); ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, 0, "veu", veu); @@ -1248,18 +1249,7 @@ static struct platform_driver __refdata sh_veu_pdrv = { }, }; -static int __init sh_veu_init(void) -{ - return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); -} - -static void __exit sh_veu_exit(void) -{ - platform_driver_unregister(&sh_veu_pdrv); -} - -module_init(sh_veu_init); -module_exit(sh_veu_exit); +module_platform_driver_probe(sh_veu_pdrv, sh_veu_probe); MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 66c8da18df84..7d0235069c87 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -881,29 +881,29 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) } } -static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct sh_vou_device *vou_dev = video_drvdata(file); int ret; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); - if (*std_id & ~vou_dev->vdev->tvnorms) + if (std_id & ~vou_dev->vdev->tvnorms) return -EINVAL; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_std_output, *std_id); + s_std_output, std_id); /* Shall we continue, if the subdev doesn't support .s_std_output()? */ if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - if (*std_id & V4L2_STD_525_60) + if (std_id & V4L2_STD_525_60) sh_vou_reg_ab_set(vou_dev, VOUCR, sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); else sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); - vou_dev->std = *std_id; + vou_dev->std = std_id; return 0; } @@ -1266,7 +1266,7 @@ static int sh_vou_g_register(struct file *file, void *fh, } static int sh_vou_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct sh_vou_device *vou_dev = video_drvdata(file); @@ -1485,18 +1485,7 @@ static struct platform_driver __refdata sh_vou = { }, }; -static int __init sh_vou_init(void) -{ - return platform_driver_probe(&sh_vou, sh_vou_probe); -} - -static void __exit sh_vou_exit(void) -{ - platform_driver_unregister(&sh_vou); -} - -module_init(sh_vou_init); -module_exit(sh_vou_exit); +module_platform_driver_probe(sh_vou, sh_vou_probe); MODULE_DESCRIPTION("SuperH VOU driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 82dbf99d347c..1abbb36d0755 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -514,6 +514,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, q->buf_struct_size = sizeof(struct frame_buffer); q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1020,7 +1021,7 @@ static int atmel_isi_probe(struct platform_device *pdev) isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); irq = platform_get_irq(pdev, 0); - if (irq < 0) { + if (IS_ERR_VALUE(irq)) { ret = irq; goto err_req_irq; } @@ -1073,7 +1074,6 @@ err_clk_prepare_pclk: } static struct platform_driver atmel_isi_driver = { - .probe = atmel_isi_probe, .remove = atmel_isi_remove, .driver = { .name = "atmel_isi", @@ -1081,17 +1081,7 @@ static struct platform_driver atmel_isi_driver = { }, }; -static int __init atmel_isi_init_module(void) -{ - return platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe); -} - -static void __exit atmel_isi_exit(void) -{ - platform_driver_unregister(&atmel_isi_driver); -} -module_init(atmel_isi_init_module); -module_exit(atmel_isi_exit); +module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe); MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 25b2a285dc86..a3fd8d63546c 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -776,7 +776,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev) /* request irq */ err = claim_fiq(&fh); if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed\n"); goto exit_free_dma; } @@ -853,24 +853,13 @@ static int __exit mx1_camera_remove(struct platform_device *pdev) } static struct platform_driver mx1_camera_driver = { - .driver = { + .driver = { .name = DRIVER_NAME, }, .remove = __exit_p(mx1_camera_remove), }; -static int __init mx1_camera_init(void) -{ - return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe); -} - -static void __exit mx1_camera_exit(void) -{ - return platform_driver_unregister(&mx1_camera_driver); -} - -module_init(mx1_camera_init); -module_exit(mx1_camera_exit); +module_platform_driver_probe(mx1_camera_driver, mx1_camera_probe); MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver"); MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>"); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index ffba7d91f413..5bbeb43e4531 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -797,6 +797,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx2_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx2_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1453,7 +1454,7 @@ static int mx27_camera_emma_init(struct platform_device *pdev) err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0, MX2_CAM_DRV_NAME, pcdev); if (err) { - dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n"); + dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n"); goto out; } @@ -1614,15 +1615,14 @@ static int mx2_camera_remove(struct platform_device *pdev) } static struct platform_driver mx2_camera_driver = { - .driver = { + .driver = { .name = MX2_CAM_DRV_NAME, }, .id_table = mx2_camera_devtype, .remove = mx2_camera_remove, - .probe = mx2_camera_probe, }; -module_platform_driver(mx2_camera_driver); +module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe); MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index f5cbb92db545..5da337736cd8 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -455,6 +455,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx3_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx3_camera_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -1275,7 +1276,7 @@ static int mx3_camera_remove(struct platform_device *pdev) } static struct platform_driver mx3_camera_driver = { - .driver = { + .driver = { .name = MX3_CAM_DRV_NAME, }, .probe = mx3_camera_probe, diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 2547bf88f79f..9689a6e89b7f 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1546,7 +1546,7 @@ static struct soc_camera_host_ops omap1_host_ops = { .poll = omap1_cam_poll, }; -static int __init omap1_cam_probe(struct platform_device *pdev) +static int omap1_cam_probe(struct platform_device *pdev) { struct omap1_cam_dev *pcdev; struct resource *res; @@ -1677,7 +1677,7 @@ exit: return err; } -static int __exit omap1_cam_remove(struct platform_device *pdev) +static int omap1_cam_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct omap1_cam_dev *pcdev = container_of(soc_host, @@ -1709,7 +1709,7 @@ static struct platform_driver omap1_cam_driver = { .name = DRIVER_NAME, }, .probe = omap1_cam_probe, - .remove = __exit_p(omap1_cam_remove), + .remove = omap1_cam_remove, }; module_platform_driver(omap1_cam_driver); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 395e2e043615..d665242e8207 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -1710,9 +1711,10 @@ static int pxa_camera_probe(struct platform_device *pdev) /* * Request the regions. */ - base = devm_request_and_ioremap(&pdev->dev, res); - if (!base) - return -ENOMEM; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + pcdev->irq = irq; pcdev->base = base; @@ -1794,13 +1796,13 @@ static int pxa_camera_remove(struct platform_device *pdev) return 0; } -static struct dev_pm_ops pxa_camera_pm = { +static const struct dev_pm_ops pxa_camera_pm = { .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, }; static struct platform_driver pxa_camera_driver = { - .driver = { + .driver = { .name = PXA_CAM_DRV_NAME, .pm = &pxa_camera_pm, }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index bb08a46432f4..143d29fe0137 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -20,6 +20,7 @@ #include <linux/completion.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -2026,6 +2027,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, q->ops = &sh_mobile_ceu_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } @@ -2110,11 +2112,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = devm_request_and_ioremap(&pdev->dev, res); - if (!base) { - dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - return -ENXIO; - } + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); pcdev->irq = irq; pcdev->base = base; @@ -2288,7 +2288,7 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { }; static struct platform_driver sh_mobile_ceu_driver = { - .driver = { + .driver = { .name = "sh_mobile_ceu", .pm = &sh_mobile_ceu_dev_pm_ops, }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 42c559eb4937..09cb4fc88f34 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -9,6 +9,7 @@ */ #include <linux/delay.h> +#include <linux/err.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/platform_device.h> @@ -324,11 +325,9 @@ static int sh_csi2_probe(struct platform_device *pdev) priv->irq = irq; - priv->base = devm_request_and_ioremap(&pdev->dev, res); - if (!priv->base) { - dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - return -ENXIO; - } + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); priv->pdev = pdev; platform_set_drvdata(pdev, priv); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 8ec98051ea73..eea832c5fd01 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -256,12 +256,12 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, s_std, *a); + return v4l2_subdev_call(sd, core, s_std, a); } static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -508,36 +508,49 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_device *icd; struct soc_camera_host *ici; int ret; - if (!to_soc_camera_control(icd)) - /* No device driver attached */ - return -ENODEV; - /* * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes + * scan_add_host() completes. Also protect against a race with + * soc_camera_host_unregister(). */ if (mutex_lock_interruptible(&list_lock)) return -ERESTARTSYS; + + if (!vdev || !video_is_registered(vdev)) { + mutex_unlock(&list_lock); + return -ENODEV; + } + + icd = dev_get_drvdata(vdev->parent); ici = to_soc_camera_host(icd->parent); + + ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; mutex_unlock(&list_lock); - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - if (!try_module_get(ici->ops->owner)) { + if (ret < 0) { dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - ret = -EINVAL; - goto emodule; + return ret; } + if (!to_soc_camera_control(icd)) { + /* No device driver attached */ + ret = -ENODEV; + goto econtrol; + } + + if (mutex_lock_interruptible(&ici->host_lock)) { + ret = -ERESTARTSYS; + goto elockhost; + } icd->use_count++; /* Now we really have to activate the camera */ if (icd->use_count == 1) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); /* Restore parameters before the last close() per V4L2 API */ struct v4l2_format f = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, @@ -609,9 +622,10 @@ epower: ici->ops->remove(icd); eiciadd: icd->use_count--; - module_put(ici->ops->owner); -emodule: mutex_unlock(&ici->host_lock); +elockhost: +econtrol: + module_put(ici->ops->owner); return ret; } @@ -1042,7 +1056,7 @@ static int soc_camera_g_register(struct file *file, void *fh, } static int soc_camera_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index ce3b1d6a4734..1b7a88ca195b 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -188,7 +188,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev) } static struct platform_driver soc_camera_platform_driver = { - .driver = { + .driver = { .name = "soc_camera_platform", .owner = THIS_MODULE, }, diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index 89dce097a827..dc02deca7563 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -73,7 +73,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .name = "RGB555X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, + .order = SOC_MBUS_ORDER_BE, .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { @@ -93,10 +93,46 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .name = "RGB565X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, + .order = SOC_MBUS_ORDER_BE, .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { + .code = V4L2_MBUS_FMT_RGB666_1X18, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB666/32bpp", + .bits_per_sample = 18, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_1X24, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 24, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_2X12_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_BE, + }, +}, { + .code = V4L2_MBUS_FMT_RGB888_2X12_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { .code = V4L2_MBUS_FMT_SBGGR8_1X8, .fmt = { .fourcc = V4L2_PIX_FMT_SBGGR8, @@ -358,6 +394,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, *numerator = 1; *denominator = 1; return 0; + case SOC_MBUS_PACKING_EXTEND32: + *numerator = 1; + *denominator = 1; + return 0; case SOC_MBUS_PACKING_2X8_PADHI: case SOC_MBUS_PACKING_2X8_PADLO: *numerator = 2; @@ -392,6 +432,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) return width * 3 / 2; case SOC_MBUS_PACKING_VARIABLE: return 0; + case SOC_MBUS_PACKING_EXTEND32: + return width * 4; } return -EINVAL; } diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index c3a2a4484401..a2f7bdd5104f 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -78,7 +78,7 @@ struct timblogiw_buffer { struct timblogiw_fh *fh; }; -const struct timblogiw_tvnorm timblogiw_tvnorms[] = { +static const struct timblogiw_tvnorm timblogiw_tvnorms[] = { { .std = V4L2_STD_PAL, .width = 720, @@ -336,7 +336,7 @@ static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) { struct video_device *vdev = video_devdata(file); struct timblogiw *lw = video_get_drvdata(vdev); @@ -348,10 +348,10 @@ static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) mutex_lock(&lw->lock); if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std); + err = v4l2_subdev_call(lw->sd_enc, core, s_std, std); if (!err) - fh->cur_norm = timblogiw_get_norm(*std); + fh->cur_norm = timblogiw_get_norm(std); mutex_unlock(&lw->lock); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index b051c4a28554..a794cd6c4441 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -847,7 +847,7 @@ static int viacam_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id *std) +static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id std) { return 0; } diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index eb5d6f955709..c6af974c5b45 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3042,7 +3042,7 @@ static int vino_g_std(struct file *file, void *__fh, } static int vino_s_std(struct file *file, void *__fh, - v4l2_std_id *std) + v4l2_std_id std) { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; @@ -3056,7 +3056,7 @@ static int vino_s_std(struct file *file, void *__fh, } /* check if the standard is valid for the current input */ - if ((*std) & vino_inputs[vcs->input].std) { + if (std & vino_inputs[vcs->input].std) { dprintk("standard accepted\n"); /* change the video norm for SAA7191 @@ -3065,13 +3065,13 @@ static int vino_s_std(struct file *file, void *__fh, if (vcs->input == VINO_INPUT_D1) goto out; - if ((*std) & V4L2_STD_PAL) { + if (std & V4L2_STD_PAL) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, &flags); - } else if ((*std) & V4L2_STD_NTSC) { + } else if (std & V4L2_STD_NTSC) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC, &flags); - } else if ((*std) & V4L2_STD_SECAM) { + } else if (std & V4L2_STD_SECAM) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM, &flags); } else { diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 8a33a712f480..85bc314382d3 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1093,6 +1093,15 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; dev->input = i; + /* + * Modify the brightness range depending on the input. + * This makes it easy to use vivi to test if applications can + * handle control range modifications and is also how this is + * typically used in practice as different inputs may be hooked + * up to different receivers with different control ranges. + */ + v4l2_ctrl_modify_range(dev->brightness, + 128 * i, 255 + 128 * i, 1, 127 + 128 * i); precalculate_bars(dev); precalculate_line(dev); return 0; @@ -1429,6 +1438,7 @@ static int __init vivi_create_instance(int inst) q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) |