summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig5
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c13
-rw-r--r--drivers/media/platform/coda.c50
-rw-r--r--drivers/media/platform/davinci/Kconfig103
-rw-r--r--drivers/media/platform/davinci/Makefile17
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c49
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc_regs.h2
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c57
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc_regs.h2
-rw-r--r--drivers/media/platform/davinci/isif.c30
-rw-r--r--drivers/media/platform/davinci/isif_regs.h4
-rw-r--r--drivers/media/platform/davinci/vpbe.c16
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c17
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c3
-rw-r--r--drivers/media/platform/davinci/vpbe_venc.c36
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c62
-rw-r--r--drivers/media/platform/davinci/vpif.c32
-rw-r--r--drivers/media/platform/davinci/vpif.h2
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c12
-rw-r--r--drivers/media/platform/davinci/vpif_display.c16
-rw-r--r--drivers/media/platform/davinci/vpss.c36
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c8
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c5
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-regs.c1
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig (renamed from drivers/media/platform/s5p-fimc/Kconfig)21
-rw-r--r--drivers/media/platform/exynos4-is/Makefile (renamed from drivers/media/platform/s5p-fimc/Makefile)5
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c (renamed from drivers/media/platform/s5p-fimc/fimc-capture.c)411
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c (renamed from drivers/media/platform/s5p-fimc/fimc-core.c)318
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h (renamed from drivers/media/platform/s5p-fimc/fimc-core.h)86
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-command.h137
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.c272
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.h248
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.c126
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.h15
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c900
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.h1020
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c243
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.h164
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.c305
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.h89
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c1007
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h345
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c703
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h181
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c (renamed from drivers/media/platform/s5p-fimc/fimc-lite-reg.c)12
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.h (renamed from drivers/media/platform/s5p-fimc/fimc-lite-reg.h)8
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c (renamed from drivers/media/platform/s5p-fimc/fimc-lite.c)430
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.h (renamed from drivers/media/platform/s5p-fimc/fimc-lite.h)20
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c (renamed from drivers/media/platform/s5p-fimc/fimc-m2m.c)40
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.c (renamed from drivers/media/platform/s5p-fimc/fimc-reg.c)87
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.h (renamed from drivers/media/platform/s5p-fimc/fimc-reg.h)27
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c (renamed from drivers/media/platform/s5p-fimc/fimc-mdevice.c)764
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h (renamed from drivers/media/platform/s5p-fimc/fimc-mdevice.h)54
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c (renamed from drivers/media/platform/s5p-fimc/mipi-csis.c)169
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.h (renamed from drivers/media/platform/s5p-fimc/mipi-csis.h)1
-rw-r--r--drivers/media/platform/fsl-viu.c6
-rw-r--r--drivers/media/platform/m2m-deinterlace.c5
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c4
-rw-r--r--drivers/media/platform/mem2mem_testdev.c12
-rw-r--r--drivers/media/platform/mx2_emmaprp.c5
-rw-r--r--drivers/media/platform/omap/omap_vout.c14
-rw-r--r--drivers/media/platform/omap3isp/isp.c277
-rw-r--r--drivers/media/platform/omap3isp/isp.h22
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c16
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c36
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c5
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c21
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c12
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c12
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c76
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c1
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c112
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c124
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c129
-rw-r--r--drivers/media/platform/s5p-tv/hdmiphy_drv.c55
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c52
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c3
-rw-r--r--drivers/media/platform/sh_veu.c20
-rw-r--r--drivers/media/platform/sh_vou.c27
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c16
-rw-r--r--drivers/media/platform/soc_camera/mx1_camera.c17
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c8
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c3
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c6
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c12
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c12
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c9
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c48
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c2
-rw-r--r--drivers/media/platform/soc_camera/soc_mediabus.c46
-rw-r--r--drivers/media/platform/timblogiw.c8
-rw-r--r--drivers/media/platform/via-camera.c2
-rw-r--r--drivers/media/platform/vino.c10
-rw-r--r--drivers/media/platform/vivi.c10
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", &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)