summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Kumar K <arun.kk@samsung.com>2013-11-05 17:43:19 +0530
committerSylwester Nawrocki <s.nawrocki@samsung.com>2014-11-27 03:35:09 -0800
commitc28f5dd0b9b73130a4c93f33a7d1fd6296b2d8be (patch)
tree7a6915e2ac266af746558462d7f6bba41d81aeb8
parent16c9bfd617dace172315cb0d50cfe8c075824586 (diff)
downloadlinux-3.10-c28f5dd0b9b73130a4c93f33a7d1fd6296b2d8be.tar.gz
linux-3.10-c28f5dd0b9b73130a4c93f33a7d1fd6296b2d8be.tar.bz2
linux-3.10-c28f5dd0b9b73130a4c93f33a7d1fd6296b2d8be.zip
[media] Add driver for exynos5 FIMC-IS subsystem
This driver is for the FIMC-IS IP available in Samsung Exynos5 SoC onwards. exynos5-fimc-is: Add common driver header files This patch adds all the common header files used by the fimc-is driver. It includes the commands for interfacing with the firmware and error codes from IS firmware, metadata and command parameter definitions. exynos5-fimc-is: Add register definition and context header This patch adds the register definition file for the fimc-is driver and also the header file containing the main context for the driver. exynos5-fimc-is: Add isp subdev fimc-is driver takes video data input from the ISP video node which is added in this patch. This node accepts Bayer input buffers which is given from the IS sensors. exynos5-fimc-is: Add scaler subdev FIMC-IS has two hardware scalers named as scaler-codec and scaler-preview. This patch adds the common code handling the video nodes and subdevs of both the scalers. exynos5-fimc-is: Add sensor interface Some sensors to be used with fimc-is are exclusively controlled by the fimc-is firmware. This minimal sensor driver provides the required info for the firmware to configure the sensors sitting on I2C bus. exynos5-fimc-is: Add the hardware pipeline control This patch adds the crucial hardware pipeline control for the fimc-is driver. All the subdev nodes will call this pipeline interfaces to reach the hardware. Responsibilities of this module involves configuring and maintaining the hardware pipeline involving multiple sub-ips like ISP, DRC, Scalers, ODC, 3DNR, FD etc. exynos5-fimc-is: Add the hardware interface module The hardware interface module finally sends the commands to the FIMC-IS firmware and runs the interrupt handler for getting the responses. Signed-off-by: Arun Kumar K <arun.kk@samsung.com> Signed-off-by: Kilyeon Im <kilyeon.im@samsung.com> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> exynos5-is: Add Kconfig and Makefile Adds Kconfig and Makefile for exynos5-is driver files. [b.michalska@samsung.com: Implementation adjusted to current version of V4L2 framework: - updated prototype for stop_streaming v4l2 capture ops - vb2_queue struct's field tiemstamp_type updated to timestamp_flags] Change-Id: I066a86151048b4b5871bd3732773b87219f04fd1 Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com> Signed-off-by: Arun Kumar K <arun.kk@samsung.com> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/exynos5-is/Kconfig20
-rw-r--r--drivers/media/platform/exynos5-is/Makefile7
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-cmd.h187
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.c387
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.h117
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-err.h257
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-interface.c810
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-interface.h124
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.c533
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.h90
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-metadata.h767
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-param.h1159
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.c1699
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.h129
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-regs.h105
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-scaler.c474
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-scaler.h106
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-sensor.c45
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-sensor.h65
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is.h160
22 files changed, 7243 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ab331dd75ea..3fe8c9b982a 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -117,6 +117,7 @@ config VIDEO_S3C_CAMIF
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
+source "drivers/media/platform/exynos5-is/Kconfig"
source "drivers/media/platform/s5p-tv/Kconfig"
endif # V4L_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index e5269da9190..d757065247a 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS5_CAMERA) += exynos5-is/
obj-$(CONFIG_BLACKFIN) += blackfin/
diff --git a/drivers/media/platform/exynos5-is/Kconfig b/drivers/media/platform/exynos5-is/Kconfig
new file mode 100644
index 00000000000..ca46b588634
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/Kconfig
@@ -0,0 +1,20 @@
+config VIDEO_SAMSUNG_EXYNOS5_CAMERA
+ bool "Samsung Exynos5 SoC series Camera Subsystem driver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PM_RUNTIME
+ depends on VIDEO_SAMSUNG_EXYNOS4_IS
+ help
+ This is a V4L2 media device driver for Exynos5 SoC series
+ camera subsystem.
+
+if VIDEO_SAMSUNG_EXYNOS5_CAMERA
+
+config VIDEO_SAMSUNG_EXYNOS5_FIMC_IS
+ tristate "Samsung Exynos5 SoC FIMC-IS driver"
+ depends on I2C && OF
+ depends on VIDEO_EXYNOS4_FIMC_IS
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a V4L2 driver for Samsung Exynos5 SoC series Imaging
+ Subsystem known as FIMC-IS.
+
+endif #VIDEO_SAMSUNG_EXYNOS5_MDEV
diff --git a/drivers/media/platform/exynos5-is/Makefile b/drivers/media/platform/exynos5-is/Makefile
new file mode 100644
index 00000000000..6cdb0374f54
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/Makefile
@@ -0,0 +1,7 @@
+ccflags-y += -Idrivers/media/platform/exynos4-is
+exynos5-fimc-is-objs := fimc-is-core.o fimc-is-isp.o fimc-is-scaler.o
+exynos5-fimc-is-objs += fimc-is-pipeline.o fimc-is-interface.o fimc-is-sensor.o
+exynos-mdevice-objs := exynos5-mdev.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS5_FIMC_IS) += exynos5-fimc-is.o
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS5_CAMERA) += exynos-mdevice.o
diff --git a/drivers/media/platform/exynos5-is/fimc-is-cmd.h b/drivers/media/platform/exynos5-is/fimc-is-cmd.h
new file mode 100644
index 00000000000..62502803935
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-cmd.h
@@ -0,0 +1,187 @@
+/*
+ * Samsung Exynos5 SoC series FIMC-IS driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd
+ * Kil-yeon Lim <kilyeon.im@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 IS_COMMAND_VER 122 /* IS COMMAND VERSION 1.22 */
+
+enum is_cmd {
+ /* HOST -> IS */
+ HIC_PREVIEW_STILL = 0x1,
+ HIC_PREVIEW_VIDEO,
+ HIC_CAPTURE_STILL,
+ HIC_CAPTURE_VIDEO,
+ HIC_PROCESS_START,
+ HIC_PROCESS_STOP,
+ HIC_STREAM_ON,
+ HIC_STREAM_OFF,
+ HIC_SHOT,
+ HIC_GET_STATIC_METADATA,
+ HIC_SET_CAM_CONTROL,
+ HIC_GET_CAM_CONTROL,
+ HIC_SET_PARAMETER,
+ HIC_GET_PARAMETER,
+ HIC_SET_A5_MEM_ACCESS,
+ RESERVED2,
+ HIC_GET_STATUS,
+ /* SENSOR PART*/
+ HIC_OPEN_SENSOR,
+ HIC_CLOSE_SENSOR,
+ HIC_SIMMIAN_INIT,
+ HIC_SIMMIAN_WRITE,
+ HIC_SIMMIAN_READ,
+ HIC_POWER_DOWN,
+ HIC_GET_SET_FILE_ADDR,
+ HIC_LOAD_SET_FILE,
+ HIC_MSG_CONFIG,
+ HIC_MSG_TEST,
+ /* IS -> HOST */
+ IHC_GET_SENSOR_NUMBER = 0x1000,
+ /* Parameter1 : Address of space to copy a setfile */
+ /* Parameter2 : Space szie */
+ IHC_SET_SHOT_MARK,
+ /* PARAM1 : a frame number */
+ /* PARAM2 : confidence level(smile 0~100) */
+ /* PARMA3 : confidence level(blink 0~100) */
+ IHC_SET_FACE_MARK,
+ /* PARAM1 : coordinate count */
+ /* PARAM2 : coordinate buffer address */
+ IHC_FRAME_DONE,
+ /* PARAM1 : frame start number */
+ /* PARAM2 : frame count */
+ IHC_AA_DONE,
+ IHC_NOT_READY,
+ IHC_FLASH_READY
+};
+
+enum is_reply {
+ ISR_DONE = 0x2000,
+ ISR_NDONE
+};
+
+enum is_scenario_id {
+ ISS_PREVIEW_STILL,
+ ISS_PREVIEW_VIDEO,
+ ISS_CAPTURE_STILL,
+ ISS_CAPTURE_VIDEO,
+ ISS_END
+};
+
+enum is_subscenario_id {
+ ISS_SUB_SCENARIO_STILL,
+ ISS_SUB_SCENARIO_VIDEO,
+ ISS_SUB_SCENARIO_SCENE1,
+ ISS_SUB_SCENARIO_SCENE2,
+ ISS_SUB_SCENARIO_SCENE3,
+ ISS_SUB_END
+};
+
+struct is_setfile_header_element {
+ u32 binary_addr;
+ u32 binary_size;
+};
+
+struct is_setfile_header {
+ struct is_setfile_header_element isp[ISS_END];
+ struct is_setfile_header_element drc[ISS_END];
+ struct is_setfile_header_element fd[ISS_END];
+};
+
+struct is_common_reg {
+ u32 hicmd;
+ u32 hic_sensorid;
+ u32 hic_param[4];
+
+ u32 reserved1[3];
+
+ u32 ihcmd_iflag;
+ u32 ihcmd;
+ u32 ihc_sensorid;
+ u32 ihc_param[4];
+
+ u32 reserved2[3];
+
+ u32 isp_bayer_iflag;
+ u32 isp_bayer_sensor_id;
+ u32 isp_bayer_param[2];
+
+ u32 reserved3[4];
+
+ u32 scc_iflag;
+ u32 scc_sensor_id;
+ u32 scc_param[3];
+
+ u32 reserved4[3];
+
+ u32 dnr_iflag;
+ u32 dnr_sensor_id;
+ u32 dnr_param[2];
+
+ u32 reserved5[4];
+
+ u32 scp_iflag;
+ u32 scp_sensor_id;
+ u32 scp_param[3];
+
+ u32 reserved6[1];
+
+ u32 isp_yuv_iflag;
+ u32 isp_yuv_sensor_id;
+ u32 isp_yuv_param[2];
+
+ u32 reserved7[1];
+
+ u32 shot_iflag;
+ u32 shot_sensor_id;
+ u32 shot_param[2];
+
+ u32 reserved8[1];
+
+ u32 meta_iflag;
+ u32 meta_sensor_id;
+ u32 meta_param1;
+
+ u32 reserved9[1];
+
+ u32 fcount;
+};
+
+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 pad[0xD];
+
+ struct is_common_reg common_reg;
+};
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.c b/drivers/media/platform/exynos5-is/fimc-is-core.c
new file mode 100644
index 00000000000..0b391f21d6e
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-core.c
@@ -0,0 +1,387 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-of.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-is.h"
+#include "fimc-is-i2c.h"
+
+#define CLK_MCU_ISP_DIV0_FREQ (200 * 1000000)
+#define CLK_MCU_ISP_DIV1_FREQ (100 * 1000000)
+#define CLK_ISP_DIV0_FREQ (134 * 1000000)
+#define CLK_ISP_DIV1_FREQ (68 * 1000000)
+#define CLK_ISP_DIVMPWM_FREQ (34 * 1000000)
+
+static const char * const fimc_is_clock_name[] = {
+ [IS_CLK_ISP] = "isp",
+ [IS_CLK_MCU_ISP] = "mcu_isp",
+ [IS_CLK_ISP_DIV0] = "isp_div0",
+ [IS_CLK_ISP_DIV1] = "isp_div1",
+ [IS_CLK_ISP_DIVMPWM] = "isp_divmpwm",
+ [IS_CLK_MCU_ISP_DIV0] = "mcu_isp_div0",
+ [IS_CLK_MCU_ISP_DIV1] = "mcu_isp_div1",
+};
+
+static void fimc_is_put_clocks(struct fimc_is *is)
+{
+ int i;
+
+ for (i = 0; i < IS_CLK_MAX_NUM; i++) {
+ if (IS_ERR(is->clock[i]))
+ continue;
+ clk_unprepare(is->clock[i]);
+ clk_put(is->clock[i]);
+ is->clock[i] = ERR_PTR(-EINVAL);
+ }
+}
+
+static int fimc_is_get_clocks(struct fimc_is *is)
+{
+ struct device *dev = &is->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < IS_CLK_MAX_NUM; i++) {
+ is->clock[i] = clk_get(dev, fimc_is_clock_name[i]);
+ if (IS_ERR(is->clock[i]))
+ goto err;
+ ret = clk_prepare(is->clock[i]);
+ if (ret < 0) {
+ clk_put(is->clock[i]);
+ is->clock[i] = ERR_PTR(-EINVAL);
+ goto err;
+ }
+ }
+ return 0;
+err:
+ fimc_is_put_clocks(is);
+ dev_err(dev, "Failed to get clock: %s\n", fimc_is_clock_name[i]);
+ return -ENXIO;
+}
+
+static int fimc_is_configure_clocks(struct fimc_is *is)
+{
+ int i, ret;
+
+ for (i = 0; i < IS_CLK_MAX_NUM; i++)
+ is->clock[i] = ERR_PTR(-EINVAL);
+
+ ret = fimc_is_get_clocks(is);
+ if (ret)
+ return ret;
+
+ /* Set rates */
+ ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV0],
+ CLK_MCU_ISP_DIV0_FREQ);
+ if (ret)
+ return ret;
+ ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV1],
+ CLK_MCU_ISP_DIV1_FREQ);
+ if (ret)
+ return ret;
+ ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV0], CLK_ISP_DIV0_FREQ);
+ if (ret)
+ return ret;
+ ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV1], CLK_ISP_DIV1_FREQ);
+ if (ret)
+ return ret;
+ return clk_set_rate(is->clock[IS_CLK_ISP_DIVMPWM],
+ CLK_ISP_DIVMPWM_FREQ);
+}
+
+static void fimc_is_pipelines_destroy(struct fimc_is *is)
+{
+ int i;
+
+ for (i = 0; i < is->drvdata->num_instances; i++)
+ fimc_is_pipeline_destroy(&is->pipeline[i]);
+}
+
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+ struct device_node *node)
+{
+ struct fimc_is_sensor *sensor = &is->sensor[index];
+ u32 tmp = 0;
+ int ret;
+
+ sensor->drvdata = exynos5_is_sensor_get_drvdata(node);
+ if (!sensor->drvdata) {
+ dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ node = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node)
+ return -ENXIO;
+
+ node = v4l2_of_get_remote_port(node);
+ if (!node)
+ return -ENXIO;
+
+ /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
+
+ sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ return 0;
+}
+
+static int fimc_is_parse_sensor(struct fimc_is *is)
+{
+ struct device_node *i2c_bus, *child;
+ int ret, index = 0;
+
+ for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_available_child_of_node(i2c_bus, child) {
+ ret = fimc_is_parse_sensor_config(is, index, child);
+
+ if (ret < 0 || index >= FIMC_IS_NUM_SENSORS) {
+ of_node_put(child);
+ return ret;
+ }
+ index++;
+ }
+ }
+ return 0;
+}
+
+static struct fimc_is_drvdata exynos5250_drvdata = {
+ .num_instances = 1,
+ .fw_name = "exynos5_fimc_is_fw.bin",
+};
+
+static const struct of_device_id exynos5_fimc_is_match[] = {
+ {
+ .compatible = "samsung,exynos5250-fimc-is",
+ .data = &exynos5250_drvdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos5_fimc_is_match);
+
+static void *fimc_is_get_drvdata(struct platform_device *pdev)
+{
+ struct fimc_is_drvdata *driver_data = NULL;
+ const struct of_device_id *match;
+
+ match = of_match_node(exynos5_fimc_is_match,
+ pdev->dev.of_node);
+ if (match)
+ driver_data = (struct fimc_is_drvdata *)match->data;
+ return driver_data;
+}
+
+static int fimc_is_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct fimc_is *is;
+ void __iomem *regs;
+ struct device_node *node;
+ int irq, ret;
+ int i;
+
+ dev_dbg(dev, "FIMC-IS Probe Enter\n");
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL);
+ if (!is)
+ return -ENOMEM;
+
+ is->pdev = pdev;
+
+ is->drvdata = fimc_is_get_drvdata(pdev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /* Get the PMU base */
+ node = of_parse_phandle(dev->of_node, "samsung,pmu", 0);
+ if (!node)
+ return -ENODEV;
+ is->pmu_regs = of_iomap(node, 0);
+ if (!is->pmu_regs)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (!irq) {
+ dev_err(dev, "Failed to get IRQ\n");
+ return irq;
+ }
+
+ ret = fimc_is_configure_clocks(is);
+ if (ret < 0) {
+ dev_err(dev, "clocks configuration failed\n");
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, is);
+ pm_runtime_enable(dev);
+
+ is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(is->alloc_ctx)) {
+ ret = PTR_ERR(is->alloc_ctx);
+ goto err_vb;
+ }
+
+ /* Get IS-sensor contexts */
+ ret = fimc_is_parse_sensor(is);
+ if (ret < 0)
+ goto err_vb;
+
+ /* Initialize FIMC Pipeline */
+ for (i = 0; i < is->drvdata->num_instances; i++) {
+ ret = fimc_is_pipeline_init(&is->pipeline[i], i, is);
+ if (ret < 0)
+ goto err_sd;
+ }
+
+ /* Initialize FIMC Interface */
+ ret = fimc_is_interface_init(&is->interface, regs, irq);
+ if (ret < 0)
+ goto err_sd;
+
+ /* Probe the peripheral devices */
+ ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (ret < 0)
+ goto err_sd;
+
+ dev_dbg(dev, "FIMC-IS registered successfully\n");
+
+ return 0;
+
+err_sd:
+ fimc_is_pipelines_destroy(is);
+err_vb:
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+err_clk:
+ fimc_is_put_clocks(is);
+
+ return ret;
+}
+
+int fimc_is_clk_enable(struct fimc_is *is)
+{
+ int ret;
+
+ ret = clk_enable(is->clock[IS_CLK_ISP]);
+ if (ret)
+ return ret;
+ ret = clk_enable(is->clock[IS_CLK_MCU_ISP]);
+ if (ret)
+ clk_disable(is->clock[IS_CLK_ISP]);
+ return ret;
+}
+
+void fimc_is_clk_disable(struct fimc_is *is)
+{
+ clk_disable(is->clock[IS_CLK_ISP]);
+ clk_disable(is->clock[IS_CLK_MCU_ISP]);
+}
+
+static int fimc_is_pm_resume(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+ int ret;
+
+ ret = fimc_is_clk_enable(is);
+ if (ret < 0) {
+ dev_err(dev, "Could not enable clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int fimc_is_pm_suspend(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+
+ fimc_is_clk_disable(is);
+ return 0;
+}
+
+static int fimc_is_runtime_resume(struct device *dev)
+{
+ return fimc_is_pm_resume(dev);
+}
+
+static int fimc_is_runtime_suspend(struct device *dev)
+{
+ return fimc_is_pm_suspend(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_resume(struct device *dev)
+{
+ /* TODO */
+ return 0;
+}
+
+static int fimc_is_suspend(struct device *dev)
+{
+ /* TODO */
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int fimc_is_remove(struct platform_device *pdev)
+{
+ struct fimc_is *is = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ fimc_is_pipelines_destroy(is);
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+ fimc_is_put_clocks(is);
+ return 0;
+}
+
+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 = {
+ .name = FIMC_IS_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_is_pm_ops,
+ .of_match_table = exynos5_fimc_is_match,
+ }
+};
+module_platform_driver(fimc_is_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arun Kumar K <arun.kk@samsung.com>");
+MODULE_DESCRIPTION("Samsung Exynos5 (FIMC-IS) Imaging Subsystem driver");
diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.h b/drivers/media/platform/exynos5-is/fimc-is-core.h
new file mode 100644
index 00000000000..ef80ed9d7ce
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-core.h
@@ -0,0 +1,117 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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_CORE_H_
+#define FIMC_IS_CORE_H_
+
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/s5p_fimc.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define FIMC_IS_DRV_NAME "exynos5-fimc-is"
+
+#define FIMC_IS_COMMAND_TIMEOUT (10 * HZ)
+#define FIMC_IS_STARTUP_TIMEOUT (3 * HZ)
+#define FIMC_IS_SHUTDOWN_TIMEOUT (10 * HZ)
+
+#define FW_SHARED_OFFSET (0x8c0000)
+#define DEBUG_CNT (500 * 1024)
+#define DEBUG_OFFSET (0x840000)
+#define DEBUGCTL_OFFSET (0x8bd000)
+#define DEBUG_FCOUNT (0x8c64c0)
+
+#define FIMC_IS_MAX_INSTANCES 1
+
+#define FIMC_IS_NUM_SENSORS 2
+#define FIMC_IS_NUM_PIPELINES 1
+
+#define FIMC_IS_MAX_PLANES 3
+#define FIMC_IS_NUM_SCALERS 2
+
+enum fimc_is_clks {
+ IS_CLK_ISP,
+ IS_CLK_MCU_ISP,
+ IS_CLK_ISP_DIV0,
+ IS_CLK_ISP_DIV1,
+ IS_CLK_ISP_DIVMPWM,
+ IS_CLK_MCU_ISP_DIV0,
+ IS_CLK_MCU_ISP_DIV1,
+ IS_CLK_MAX_NUM
+};
+
+/* Video capture states */
+enum fimc_is_video_state {
+ STATE_INIT,
+ STATE_BUFS_ALLOCATED,
+ STATE_RUNNING,
+};
+
+enum fimc_is_scaler_id {
+ SCALER_SCC,
+ SCALER_SCP
+};
+
+enum fimc_is_sensor_pos {
+ SENSOR_CAM0,
+ SENSOR_CAM1
+};
+
+struct fimc_is_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+ unsigned int paddr[FIMC_IS_MAX_PLANES];
+};
+
+struct fimc_is_memory {
+ /* physical base address */
+ dma_addr_t paddr;
+ /* virtual base address */
+ void *vaddr;
+ /* total length */
+ unsigned int size;
+};
+
+struct fimc_is_meminfo {
+ struct fimc_is_memory fw;
+ struct fimc_is_memory shot;
+ struct fimc_is_memory region;
+ struct fimc_is_memory shared;
+};
+
+struct fimc_is_drvdata {
+ unsigned int num_instances;
+ char *fw_name;
+};
+
+/**
+ * struct fimc_is_fmt - the driver's internal color format data
+ * @name: format description
+ * @fourcc: the fourcc code for this format
+ * @depth: number of bytes per pixel
+ * @num_planes: number of planes for this color format
+ */
+struct fimc_is_fmt {
+ char *name;
+ unsigned int fourcc;
+ unsigned int depth[FIMC_IS_MAX_PLANES];
+ unsigned int num_planes;
+};
+
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-err.h b/drivers/media/platform/exynos5-is/fimc-is-err.h
new file mode 100644
index 00000000000..e19eced0722
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-err.h
@@ -0,0 +1,257 @@
+/*
+ * Samsung Exynos5 SoC series FIMC-IS driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd
+ * Arun Kumar K <arun.kk@samsung.com>
+ * Kil-yeon Lim <kilyeon.im@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 012 /* IS ERROR VERSION 0.07 */
+
+/* IS error enum */
+enum is_error {
+
+ IS_ERROR_SUCCESS = 0,
+
+ /* General 1 ~ 100 */
+ IS_ERROR_INVALID_COMMAND = 1,
+ 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_ISP_SETFILE_VERSION_MISMATCH,
+ IS_ERROR_ISP_SETFILE_REVISION_MISMATCH,
+ 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_INTERNAL_STOP,
+ IS_ERROR_UNKNOWN,
+ IS_ERROR_TIME_OUT_FLAG,
+
+ /* Sensor 100 ~ 200 */
+ IS_ERROR_SENSOR_PWRDN_FAIL = 100,
+ IS_ERROR_SENSOR_STREAM_ON_FAIL,
+ IS_ERROR_SENSOR_STREAM_OFF_FAIL,
+
+ /* ISP 200 ~ 300 */
+ 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 ~ 400 */
+ IS_ERROR_DRC_PWRDN_FAIL = 300,
+ IS_ERROR_DRC_MULTIPLE_INPUT,
+ IS_ERROR_DRC_ABSENT_INPUT,
+ IS_ERROR_DRC_NONADJACENT_INTPUT,
+ 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~500)*/
+ IS_ERROR_SCALERC_PWRDN_FAIL = 400,
+
+ /*ODC(500~600)*/
+ IS_ERROR_ODC_PWRDN_FAIL = 500,
+
+ /*DIS(600~700)*/
+ IS_ERROR_DIS_PWRDN_FAIL = 600,
+
+ /*TDNR(700~800)*/
+ IS_ERROR_TDNR_PWRDN_FAIL = 700,
+
+ /*SCALERP(800~900)*/
+ IS_ERROR_SCALERP_PWRDN_FAIL = 800,
+
+ /*FD(900~1000)*/
+ 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,
+};
+
+/* Set parameter error enum */
+enum 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. */
+ 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_CONTROL_BUF = 12, /* invalid buffer info */
+
+ ERROR_OTF_INPUT_NONE = ERROR_COMMON_NONE,
+ /* invalid command */
+ 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 frame time for ISP */
+ ERROR_OTF_INPUT_USER_FRAMETILE = 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,
+ /* invalid crop size (ODC: left>2, right>10) */
+ ERROR_OTF_OUTPUT_CROP = 45,
+
+ 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_DMA_OUTPUT_BUF = 57, /* invalid buffer info */
+
+ 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_ACTURATOR_INIT_FAIL,
+ ERROR_SENSOR_INVALID_AF_POS,
+ ERROR_SENSOR_UNSUPPORT_FUNC,
+ ERROR_SENSOR_UNSUPPORT_PERI,
+ ERROR_SENSOR_UNSUPPORT_AF,
+ ERROR_SENSOR_FLASH_FAIL,
+ ERROR_SENSOR_START_FAIL,
+ ERROR_SENSOR_STOP_FAIL,
+
+ /* 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_IMAGE_EFFECT_INVALID = 231,
+ 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 ERR(500~599)*/
+ ERROR_SCALER_NONE = ERROR_COMMON_NONE,
+ ERROR_SCALER_DMA_OUTSEL = 501,
+ ERROR_SCALER_H_RATIO = 502,
+ ERROR_SCALER_V_RATIO = 503,
+ ERROR_SCALER_FRAME_BUFFER_SEQ = 504,
+
+ ERROR_SCALER_IMAGE_EFFECT = 510,
+
+ ERROR_SCALER_ROTATE = 520,
+ ERROR_SCALER_FLIP = 521,
+
+};
+
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-interface.c b/drivers/media/platform/exynos5-is/fimc-is-interface.c
new file mode 100644
index 00000000000..c5da6ff8154
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-interface.c
@@ -0,0 +1,810 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Kil-yeon Lim <kilyeon.im@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/debugfs.h>
+#include <linux/seq_file.h>
+#include "fimc-is.h"
+#include "fimc-is-cmd.h"
+#include "fimc-is-regs.h"
+
+#define init_request_barrier(itf) mutex_init(&itf->request_barrier)
+#define enter_request_barrier(itf) mutex_lock(&itf->request_barrier)
+#define exit_request_barrier(itf) mutex_unlock(&itf->request_barrier)
+
+static inline void itf_get_cmd(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg, unsigned int index)
+{
+ struct is_common_reg __iomem *com_regs = itf->com_regs;
+
+ memset(msg, 0, sizeof(*msg));
+
+ switch (index) {
+ case INTR_GENERAL:
+ msg->command = com_regs->ihcmd;
+ msg->instance = com_regs->ihc_sensorid;
+ memcpy(msg->param, com_regs->ihc_param,
+ 4 * sizeof(msg->param[0]));
+ break;
+ case INTR_SCC_FDONE:
+ msg->command = IHC_FRAME_DONE;
+ msg->instance = com_regs->scc_sensor_id;
+ memcpy(msg->param, com_regs->scc_param,
+ 3 * sizeof(msg->param[0]));
+ break;
+ case INTR_SCP_FDONE:
+ msg->command = IHC_FRAME_DONE;
+ msg->instance = com_regs->scp_sensor_id;
+ memcpy(msg->param, com_regs->scp_param,
+ 3 * sizeof(msg->param[0]));
+ break;
+ case INTR_META_DONE:
+ msg->command = IHC_FRAME_DONE;
+ msg->instance = com_regs->meta_sensor_id;
+ msg->param[0] = com_regs->meta_param1;
+ break;
+ case INTR_SHOT_DONE:
+ msg->command = IHC_FRAME_DONE;
+ msg->instance = com_regs->shot_sensor_id;
+ memcpy(msg->param, com_regs->shot_param,
+ 2 * sizeof(msg->param[0]));
+ break;
+ default:
+ dev_err(itf->dev, "%s Unknown command\n", __func__);
+ break;
+ }
+}
+
+static inline unsigned int itf_get_intr(struct fimc_is_interface *itf)
+{
+ unsigned int status;
+ struct is_common_reg __iomem *com_regs = itf->com_regs;
+
+ status = readl(itf->regs + INTMSR1) | com_regs->ihcmd_iflag |
+ com_regs->scc_iflag |
+ com_regs->scp_iflag |
+ com_regs->meta_iflag |
+ com_regs->shot_iflag;
+
+ return status;
+}
+
+static void itf_set_state(struct fimc_is_interface *itf,
+ unsigned long state)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&itf->slock_state, flags);
+ __set_bit(state, &itf->state);
+ spin_unlock_irqrestore(&itf->slock_state, flags);
+}
+
+static void itf_clr_state(struct fimc_is_interface *itf,
+ unsigned long state)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&itf->slock_state, flags);
+ __clear_bit(state, &itf->state);
+ spin_unlock_irqrestore(&itf->slock_state, flags);
+}
+
+static int itf_get_state(struct fimc_is_interface *itf,
+ unsigned long state)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&itf->slock_state, flags);
+ ret = test_bit(state, &itf->state);
+ spin_unlock_irqrestore(&itf->slock_state, flags);
+ return ret;
+}
+
+static void itf_init_wakeup(struct fimc_is_interface *itf)
+{
+ itf_set_state(itf, IS_IF_STATE_INIT);
+ wake_up(&itf->irq_queue);
+}
+
+void itf_busy_wakeup(struct fimc_is_interface *itf)
+{
+ itf_clr_state(itf, IS_IF_STATE_BUSY);
+ wake_up(&itf->irq_queue);
+}
+
+static int itf_wait_hw_ready(struct fimc_is_interface *itf)
+{
+ int t;
+ for (t = TRY_RECV_AWARE_COUNT; t >= 0; t--) {
+ unsigned int cfg = readl(itf->regs + INTMSR0);
+ if (INTMSR0_GET_INTMSD(0, cfg) == 0)
+ return 0;
+ }
+ dev_err(itf->dev, "INTMSR0's 0 bit is not cleared.\n");
+ return -EINVAL;
+}
+
+static int itf_wait_idlestate(struct fimc_is_interface *itf)
+{
+ int ret;
+
+ ret = wait_event_timeout(itf->irq_queue,
+ !itf_get_state(itf, IS_IF_STATE_BUSY),
+ FIMC_IS_COMMAND_TIMEOUT);
+ if (!ret) {
+ dev_err(itf->dev, "%s Timeout\n", __func__);
+ return -ETIME;
+ }
+ return 0;
+}
+
+int fimc_is_itf_wait_init_state(struct fimc_is_interface *itf)
+{
+ int ret;
+
+ ret = wait_event_timeout(itf->irq_queue,
+ itf_get_state(itf, IS_IF_STATE_INIT),
+ FIMC_IS_STARTUP_TIMEOUT);
+
+ if (!ret) {
+ dev_err(itf->dev, "%s Timeout\n", __func__);
+ return -ETIME;
+ }
+ return 0;
+}
+
+/* Send Host to IS command interrupt */
+static void itf_hic_interrupt(struct fimc_is_interface *itf)
+{
+ writel(INTGR0_INTGD(0), itf->regs + INTGR0);
+}
+
+static int itf_send_sensor_number(struct fimc_is_interface *itf)
+{
+ struct fimc_is_msg msg = {
+ .command = ISR_DONE,
+ .param = { IHC_GET_SENSOR_NUMBER, 1 },
+ };
+ unsigned long flags;
+
+ spin_lock_irqsave(&itf->slock, flags);
+ itf->com_regs->hicmd = msg.command;
+ itf->com_regs->hic_sensorid = msg.instance;
+ memcpy(itf->com_regs->hic_param, msg.param,
+ 4 * sizeof(itf->com_regs->hic_param[0]));
+ itf_hic_interrupt(itf);
+ spin_unlock_irqrestore(&itf->slock, flags);
+
+ return 0;
+}
+
+static int fimc_is_itf_set_cmd(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg)
+{
+ int ret = 0;
+ bool block_io = true;
+ unsigned long flags;
+
+ enter_request_barrier(itf);
+
+ switch (msg->command) {
+ case HIC_STREAM_ON:
+ if (itf->streaming == IS_IF_STREAMING_ON)
+ goto exit;
+ break;
+ case HIC_STREAM_OFF:
+ if (itf->streaming == IS_IF_STREAMING_OFF)
+ goto exit;
+ break;
+ case HIC_PROCESS_START:
+ if (itf->processing == IS_IF_PROCESSING_ON)
+ goto exit;
+ break;
+ case HIC_PROCESS_STOP:
+ if (itf->processing == IS_IF_PROCESSING_OFF)
+ goto exit;
+ break;
+ case HIC_POWER_DOWN:
+ if (itf->pdown_ready == IS_IF_POWER_DOWN_READY)
+ goto exit;
+ break;
+ case HIC_SHOT:
+ case ISR_DONE:
+ block_io = false;
+ break;
+ }
+
+ ret = itf_wait_hw_ready(itf);
+ if (ret) {
+ dev_err(itf->dev, "%s: itf_wait_hw_ready() failed", __func__);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ spin_lock_irqsave(&itf->slock, flags);
+ itf_set_state(itf, IS_IF_STATE_BUSY);
+ itf->com_regs->hicmd = msg->command;
+ itf->com_regs->hic_sensorid = msg->instance;
+ memcpy(itf->com_regs->hic_param, msg->param,
+ 4 * sizeof(itf->com_regs->hic_param[0]));
+ itf_hic_interrupt(itf);
+ spin_unlock_irqrestore(&itf->slock, flags);
+
+ if (!block_io)
+ goto exit;
+
+ ret = itf_wait_idlestate(itf);
+ if (ret) {
+ dev_err(itf->dev, "%d command is timeout\n", msg->command);
+ itf_clr_state(itf, IS_IF_STATE_BUSY);
+ ret = -ETIME;
+ goto exit;
+ }
+
+ if (itf->reply.command == ISR_DONE) {
+ switch (msg->command) {
+ case HIC_STREAM_ON:
+ itf->streaming = IS_IF_STREAMING_ON;
+ break;
+ case HIC_STREAM_OFF:
+ itf->streaming = IS_IF_STREAMING_OFF;
+ break;
+ case HIC_PROCESS_START:
+ itf->processing = IS_IF_PROCESSING_ON;
+ break;
+ case HIC_PROCESS_STOP:
+ itf->processing = IS_IF_PROCESSING_OFF;
+ break;
+ case HIC_POWER_DOWN:
+ itf->pdown_ready = IS_IF_POWER_DOWN_READY;
+ break;
+ case HIC_OPEN_SENSOR:
+ if (itf->reply.param[0] == HIC_POWER_DOWN) {
+ dev_err(itf->dev, "firmware power down");
+ itf->pdown_ready = IS_IF_POWER_DOWN_READY;
+ ret = -ECANCELED;
+ goto exit;
+ } else
+ itf->pdown_ready = IS_IF_POWER_DOWN_NREADY;
+ break;
+ default:
+ break;
+ }
+ } else {
+ dev_err(itf->dev, "ISR_NDONE occured");
+ ret = -EINVAL;
+ }
+exit:
+ exit_request_barrier(itf);
+
+ if (ret)
+ dev_err(itf->dev, "Error returned from FW. See debugfs for error log\n");
+
+ return ret;
+}
+
+static int fimc_is_itf_set_cmd_shot(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&itf->slock, flags);
+ itf->com_regs->hicmd = msg->command;
+ itf->com_regs->hic_sensorid = msg->instance;
+ memcpy(itf->com_regs->hic_param, msg->param,
+ 4 * sizeof(itf->com_regs->hic_param[0]));
+ itf->com_regs->fcount = msg->param[2];
+ itf_hic_interrupt(itf);
+ spin_unlock_irqrestore(&itf->slock, flags);
+
+ return 0;
+}
+
+static void itf_handle_general(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg)
+{
+ bool is_blocking = true;
+
+ switch (msg->command) {
+
+ case IHC_GET_SENSOR_NUMBER:
+ pr_debug("IS version : %d.%d\n",
+ ISDRV_VERSION, msg->param[0]);
+ /* Respond with sensor number */
+ itf_send_sensor_number(itf);
+ itf_init_wakeup(itf);
+ break;
+ case ISR_DONE:
+ switch (msg->param[0]) {
+ case HIC_OPEN_SENSOR:
+ pr_debug("open done\n");
+ break;
+ case HIC_GET_SET_FILE_ADDR:
+ pr_debug("saddr(%p) done\n",
+ (void *)msg->param[1]);
+ break;
+ case HIC_LOAD_SET_FILE:
+ pr_debug("setfile done\n");
+ break;
+ case HIC_SET_A5_MEM_ACCESS:
+ pr_debug("cfgmem done\n");
+ break;
+ case HIC_PROCESS_START:
+ pr_debug("process_on done\n");
+ break;
+ case HIC_PROCESS_STOP:
+ pr_debug("process_off done\n");
+ break;
+ case HIC_STREAM_ON:
+ pr_debug("stream_on done\n");
+ break;
+ case HIC_STREAM_OFF:
+ pr_debug("stream_off done\n");
+ break;
+ case HIC_SET_PARAMETER:
+ pr_debug("s_param done\n");
+ break;
+ case HIC_GET_STATIC_METADATA:
+ pr_debug("g_capability done\n");
+ break;
+ case HIC_PREVIEW_STILL:
+ pr_debug("a_param(%dx%d) done\n",
+ msg->param[1], msg->param[2]);
+ break;
+ case HIC_POWER_DOWN:
+ pr_debug("powerdown done\n");
+ break;
+ /* Non-blocking command */
+ case HIC_SHOT:
+ is_blocking = false;
+ dev_err(itf->dev, "shot done is not acceptable\n");
+ break;
+ case HIC_SET_CAM_CONTROL:
+ is_blocking = false;
+ dev_err(itf->dev, "camctrl is not acceptable\n");
+ break;
+ default:
+ is_blocking = false;
+ dev_err(itf->dev, "unknown done is invokded\n");
+ break;
+ }
+ break;
+ case ISR_NDONE:
+ switch (msg->param[0]) {
+ case HIC_SHOT:
+ is_blocking = false;
+ dev_err(itf->dev, "shot NOT done is not acceptable\n");
+ break;
+ case HIC_SET_CAM_CONTROL:
+ is_blocking = false;
+ pr_debug("camctrl NOT done\n");
+ break;
+ case HIC_SET_PARAMETER:
+ dev_err(itf->dev, "s_param NOT done\n");
+ dev_err(itf->dev, "param2 : 0x%08X\n", msg->param[1]);
+ dev_err(itf->dev, "param3 : 0x%08X\n", msg->param[2]);
+ dev_err(itf->dev, "param4 : 0x%08X\n", msg->param[3]);
+ break;
+ default:
+ dev_err(itf->dev, "command(%d) not done",
+ msg->param[0]);
+ break;
+ }
+ break;
+ case IHC_SET_FACE_MARK:
+ is_blocking = false;
+ dev_err(itf->dev, "FACE_MARK(%d,%d,%d) is not acceptable\n",
+ msg->param[0], msg->param[1], msg->param[2]);
+ break;
+ case IHC_AA_DONE:
+ is_blocking = false;
+ dev_err(itf->dev, "AA_DONE(%d,%d,%d) is not acceptable\n",
+ msg->param[0], msg->param[1], msg->param[2]);
+ break;
+ case IHC_FLASH_READY:
+ is_blocking = false;
+ dev_err(itf->dev, "IHC_FLASH_READY is not acceptable");
+ break;
+ case IHC_NOT_READY:
+ is_blocking = false;
+ dev_err(itf->dev, "IHC_NOT_READY is occured, need reset");
+ break;
+ default:
+ is_blocking = false;
+ dev_err(itf->dev, "%s: unknown (#%08X) command\n",
+ __func__, msg->command);
+ break;
+ }
+
+ if (is_blocking) {
+ memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg));
+ itf_busy_wakeup(itf);
+ }
+}
+
+static void itf_handle_scaler_done(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg)
+{
+ struct fimc_is *is = fimc_interface_to_is(itf);
+ struct fimc_is_pipeline *pipeline = &is->pipeline[msg->instance];
+ struct fimc_is_buf *buf;
+ struct fimc_is_scaler *scl;
+ const struct fimc_is_fmt *fmt;
+ struct timeval *tv;
+ struct timespec ts;
+ unsigned int wh, i;
+ unsigned int fcount = msg->param[0];
+ unsigned long *comp_state;
+
+ if (msg->param[3] == SCALER_SCC) {
+ scl = &pipeline->scaler[SCALER_SCC];
+ comp_state = &pipeline->comp_state[IS_SCC];
+ } else {
+ scl = &pipeline->scaler[SCALER_SCP];
+ comp_state = &pipeline->comp_state[IS_SCP];
+ }
+
+ fmt = scl->fmt;
+
+ fimc_is_pipeline_buf_lock(pipeline);
+ if (!list_empty(&scl->run_queue)) {
+
+ wh = scl->width * scl->height;
+ buf = fimc_is_scaler_run_queue_get(scl);
+ for (i = 0; i < fmt->num_planes; i++)
+ vb2_set_plane_payload(&buf->vb, i,
+ (wh * fmt->depth[i]) / 8);
+
+ /* Set timestamp */
+ ktime_get_ts(&ts);
+ tv = &buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ buf->vb.v4l2_buf.sequence = fcount;
+
+ pr_debug("SCP buffer done %d/%d\n",
+ msg->param[0], msg->param[2]);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ }
+ fimc_is_pipeline_buf_unlock(pipeline);
+ clear_bit(COMP_RUN, comp_state);
+ wake_up(&scl->event_q);
+}
+
+static void itf_handle_shot_done(struct fimc_is_interface *itf,
+ struct fimc_is_msg *msg)
+{
+ struct fimc_is *is = fimc_interface_to_is(itf);
+ struct fimc_is_pipeline *pipeline = &is->pipeline[msg->instance];
+ unsigned int status = msg->param[1];
+ struct fimc_is_buf *bayer_buf;
+ int ret;
+
+ if (status != ISR_DONE)
+ dev_err(itf->dev, "Shot done is invalid(0x%08X)\n", status);
+
+ /* DQ the bayer input buffer */
+ fimc_is_pipeline_buf_lock(pipeline);
+ bayer_buf = fimc_is_isp_run_queue_get(&pipeline->isp);
+ if (bayer_buf) {
+ vb2_buffer_done(&bayer_buf->vb, VB2_BUF_STATE_DONE);
+ pr_debug("Bayer buffer done.\n");
+ }
+ fimc_is_pipeline_buf_unlock(pipeline);
+
+ /* Clear state & call shot again */
+ clear_bit(PIPELINE_RUN, &pipeline->state);
+
+ ret = fimc_is_pipeline_shot(pipeline);
+ if (ret)
+ dev_err(itf->dev, "Shot failed\n");
+}
+
+/* Main FIMC-IS interrupt handler */
+static irqreturn_t itf_irq_handler(int irq, void *data)
+{
+ struct fimc_is_interface *itf = data;
+ struct fimc_is_msg msg;
+ unsigned int status, intr;
+ struct is_common_reg __iomem *com_regs;
+
+ com_regs = itf->com_regs;
+ status = itf_get_intr(itf);
+
+ for (intr = INTR_GENERAL; intr < INTR_MAX_MAP; intr++) {
+
+ if (status & BIT(intr)) {
+ itf_get_cmd(itf, &msg, intr);
+
+ switch (intr) {
+ case INTR_GENERAL:
+ itf_handle_general(itf, &msg);
+ com_regs->ihcmd_iflag = 0;
+ break;
+ case INTR_SHOT_DONE:
+ itf_handle_shot_done(itf, &msg);
+ com_regs->shot_iflag = 0;
+ break;
+ case INTR_SCC_FDONE:
+ msg.param[3] = SCALER_SCC;
+ itf_handle_scaler_done(itf, &msg);
+ com_regs->scc_iflag = 0;
+ break;
+ case INTR_SCP_FDONE:
+ msg.param[3] = SCALER_SCP;
+ itf_handle_scaler_done(itf, &msg);
+ com_regs->scp_iflag = 0;
+ break;
+ case INTR_META_DONE:
+ com_regs->meta_iflag = 0;
+ break;
+ }
+ status &= ~BIT(intr);
+ writel(BIT(intr), itf->regs + INTCR1);
+ }
+ }
+
+ if (status != 0)
+ dev_err(itf->dev, "status is NOT all clear(0x%08X)", status);
+
+ return IRQ_HANDLED;
+}
+
+int fimc_is_itf_open_sensor(struct fimc_is_interface *itf,
+ unsigned int instance,
+ unsigned int sensor_id,
+ unsigned int i2c_channel,
+ unsigned int sensor_ext)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_OPEN_SENSOR,
+ .instance = instance,
+ .param = { sensor_id, i2c_channel, sensor_ext },
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_get_setfile_addr(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int *setfile_addr)
+{
+ int ret;
+ struct fimc_is_msg msg = {
+ .command = HIC_GET_SET_FILE_ADDR,
+ .instance = instance,
+ };
+
+ ret = fimc_is_itf_set_cmd(itf, &msg);
+ *setfile_addr = itf->reply.param[1];
+
+ return ret;
+}
+
+int fimc_is_itf_load_setfile(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_LOAD_SET_FILE,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_stream_on(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_STREAM_ON,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_stream_off(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_STREAM_OFF,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_process_on(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_PROCESS_START,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_process_off(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_PROCESS_STOP,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_set_param(struct fimc_is_interface *itf,
+ unsigned int instance,
+ unsigned int lindex,
+ unsigned int hindex)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_SET_PARAMETER,
+ .instance = instance,
+ .param = { ISS_PREVIEW_STILL, 0, lindex, hindex },
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_preview_still(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_PREVIEW_STILL,
+ .instance = instance,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_get_capability(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int address)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_GET_STATIC_METADATA,
+ .instance = instance,
+ .param[0] = address,
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_cfg_mem(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int address,
+ unsigned int size)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_SET_A5_MEM_ACCESS,
+ .instance = instance,
+ .param = { address, size },
+ };
+
+ return fimc_is_itf_set_cmd(itf, &msg);
+}
+
+int fimc_is_itf_shot_nblk(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int bayer,
+ unsigned int shot, unsigned int fcount, unsigned int rcount)
+{
+ struct fimc_is_msg msg = {
+ .command = HIC_SHOT,
+ .instance = instance,
+ .param = { bayer, shot, fcount, rcount },
+ };
+
+ return fimc_is_itf_set_cmd_shot(itf, &msg);
+}
+
+int fimc_is_itf_power_down(struct fimc_is_interface *itf,
+ unsigned int instance)
+{
+ int ret;
+ struct fimc_is_msg msg = {
+ .command = HIC_POWER_DOWN,
+ .instance = instance,
+ };
+
+ ret = fimc_is_itf_set_cmd(itf, &msg);
+ itf_clr_state(itf, IS_IF_STATE_INIT);
+
+ return ret;
+}
+
+/* Debugfs for showing FW debug messages */
+static int fimc_is_log_show(struct seq_file *s, void *data)
+{
+ struct fimc_is_interface *itf = s->private;
+ struct fimc_is *is = fimc_interface_to_is(itf);
+
+ const u8 *buf = (u8 *) (is->minfo.fw.vaddr + DEBUG_OFFSET);
+
+ if (is->minfo.fw.vaddr == 0) {
+ dev_err(itf->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_interface *itf)
+{
+ debugfs_remove(itf->debugfs_entry);
+ itf->debugfs_entry = NULL;
+}
+
+static int fimc_is_debugfs_create(struct fimc_is_interface *itf)
+{
+ struct dentry *dentry;
+
+ itf->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
+
+ dentry = debugfs_create_file("fw_log", S_IRUGO, itf->debugfs_entry,
+ itf, &fimc_is_debugfs_fops);
+ if (!dentry)
+ fimc_is_debugfs_remove(itf);
+
+ return itf->debugfs_entry == NULL ? -EIO : 0;
+}
+
+int fimc_is_interface_init(struct fimc_is_interface *itf,
+ void __iomem *regs, int irq)
+{
+ struct fimc_is *is = fimc_interface_to_is(itf);
+ struct device *dev = &is->pdev->dev;
+ int ret;
+
+ if (!regs || !irq) {
+ dev_err(itf->dev, "Invalid args\n");
+ return -EINVAL;
+ }
+
+ itf->regs = regs;
+ itf->com_regs = (struct is_common_reg *)(regs + ISSR(0));
+ itf->dev = &is->pdev->dev;
+
+ init_waitqueue_head(&itf->irq_queue);
+ spin_lock_init(&itf->slock_state);
+ spin_lock_init(&itf->slock);
+
+ /* Register interrupt handler */
+ ret = devm_request_irq(dev, irq, itf_irq_handler,
+ 0, dev_name(dev), itf);
+ if (ret) {
+ dev_err(dev, "Failed to install irq (%d)\n", ret);
+ return ret;
+ }
+
+ /* Initialize context vars */
+ itf->streaming = IS_IF_STREAMING_INIT;
+ itf->processing = IS_IF_PROCESSING_INIT;
+ itf->pdown_ready = IS_IF_POWER_DOWN_READY;
+ itf->debug_cnt = 0;
+ init_request_barrier(itf);
+
+ /* Debugfs for FW debug log */
+ fimc_is_debugfs_create(itf);
+
+ return 0;
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-interface.h b/drivers/media/platform/exynos5-is/fimc-is-interface.h
new file mode 100644
index 00000000000..44b641b26d3
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-interface.h
@@ -0,0 +1,124 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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_INTERFACE_H_
+#define FIMC_IS_INTERFACE_H_
+
+#include "fimc-is-core.h"
+
+#define TRY_RECV_AWARE_COUNT 100
+
+#define ISDRV_VERSION 111
+
+enum interrupt_map {
+ INTR_GENERAL = 0,
+ INTR_ISP_FDONE = 1,
+ INTR_SCC_FDONE = 2,
+ INTR_DNR_FDONE = 3,
+ INTR_SCP_FDONE = 4,
+ INTR_ISP_YUV_DONE = 5,
+ INTR_META_DONE = 6,
+ INTR_SHOT_DONE = 7,
+ INTR_MAX_MAP
+};
+
+enum fimc_is_interface_state {
+ IS_IF_STATE_INIT,
+ IS_IF_STATE_OPEN,
+ IS_IF_STATE_START,
+ IS_IF_STATE_BUSY
+};
+
+enum streaming_state {
+ IS_IF_STREAMING_INIT,
+ IS_IF_STREAMING_OFF,
+ IS_IF_STREAMING_ON
+};
+
+enum processing_state {
+ IS_IF_PROCESSING_INIT,
+ IS_IF_PROCESSING_OFF,
+ IS_IF_PROCESSING_ON
+};
+
+enum pdown_ready_state {
+ IS_IF_POWER_DOWN_READY,
+ IS_IF_POWER_DOWN_NREADY
+};
+
+struct fimc_is_msg {
+ u32 id;
+ u32 command;
+ u32 instance;
+ u32 param[4];
+};
+
+struct fimc_is_interface {
+ unsigned long state;
+
+ void __iomem *regs;
+ struct is_common_reg __iomem *com_regs;
+ /* Lock for writing into MCUCTL registers */
+ spinlock_t slock;
+ /* Lock for context state variable */
+ spinlock_t slock_state;
+ wait_queue_head_t irq_queue;
+ struct device *dev;
+ /* Held while sending commands to FW */
+ struct mutex request_barrier;
+
+ enum streaming_state streaming;
+ enum processing_state processing;
+ enum pdown_ready_state pdown_ready;
+
+ struct fimc_is_msg reply;
+
+ int debug_cnt;
+ struct dentry *debugfs_entry;
+
+};
+
+int fimc_is_interface_init(struct fimc_is_interface *itf,
+ void __iomem *regs, int irq);
+int fimc_is_itf_wait_init_state(struct fimc_is_interface *itf);
+int fimc_is_itf_open_sensor(struct fimc_is_interface *itf,
+ unsigned int instance,
+ unsigned int sensor_id,
+ unsigned int i2c_channel,
+ unsigned int sensor_ext);
+int fimc_is_itf_get_setfile_addr(struct fimc_is_interface *this,
+ unsigned int instance, unsigned int *setfile_addr);
+int fimc_is_itf_load_setfile(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_stream_on(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_stream_off(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_process_on(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_process_off(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_set_param(struct fimc_is_interface *this,
+ unsigned int instance,
+ unsigned int lindex,
+ unsigned int hindex);
+int fimc_is_itf_preview_still(struct fimc_is_interface *itf,
+ unsigned int instance);
+int fimc_is_itf_get_capability(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int address);
+int fimc_is_itf_cfg_mem(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int address,
+ unsigned int size);
+int fimc_is_itf_shot_nblk(struct fimc_is_interface *itf,
+ unsigned int instance, unsigned int bayer,
+ unsigned int shot, unsigned int fcount, unsigned int rcount);
+int fimc_is_itf_power_down(struct fimc_is_interface *itf,
+ unsigned int instance);
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.c b/drivers/media/platform/exynos5-is/fimc-is-isp.c
new file mode 100644
index 00000000000..baa54fb49ae
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-isp.c
@@ -0,0 +1,533 @@
+/*
+ * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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 <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-is.h"
+
+#define ISP_DRV_NAME "fimc-is-isp"
+
+static const struct fimc_is_fmt formats[] = {
+ {
+ .name = "Bayer GR-BG 8bits",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = { 8 },
+ .num_planes = 1,
+ },
+ {
+ .name = "Bayer GR-BG 10bits",
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .depth = { 16 },
+ .num_planes = 1,
+ },
+ {
+ .name = "Bayer GR-BG 12bits",
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .depth = { 16 },
+ .num_planes = 1,
+ },
+};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static const struct fimc_is_fmt *find_format(struct v4l2_format *f)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat)
+ return &formats[i];
+ return NULL;
+}
+
+static int isp_video_output_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+
+ set_bit(STATE_RUNNING, &isp->output_state);
+ return 0;
+}
+
+static void isp_video_output_stop_streaming(struct vb2_queue *vq)
+{
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *buf;
+
+ /* Release unused buffers */
+ while (!list_empty(&isp->wait_queue)) {
+ buf = fimc_is_isp_wait_queue_get(isp);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ while (!list_empty(&isp->run_queue)) {
+ buf = fimc_is_isp_run_queue_get(isp);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ clear_bit(STATE_RUNNING, &isp->output_state);
+ return;
+}
+
+static int isp_video_output_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+ const struct fimc_is_fmt *fmt = isp->fmt;
+ unsigned int wh, i;
+
+ if (!fmt)
+ return -EINVAL;
+
+ *num_planes = fmt->num_planes;
+ wh = isp->width * isp->height;
+
+ for (i = 0; i < *num_planes; i++) {
+ allocators[i] = isp->alloc_ctx;
+ sizes[i] = (wh * fmt->depth[i]) / 8;
+ }
+ return 0;
+}
+
+static int isp_video_output_buffer_init(struct vb2_buffer *vb)
+{
+ struct fimc_is_buf *buf = container_of(vb, struct fimc_is_buf, vb);
+
+ buf->paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0);
+ return 0;
+}
+
+static int isp_video_output_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = (isp->width * isp->height * isp->fmt->depth[0]) / 8;
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(&isp->subdev, "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, 0, size);
+
+ return 0;
+}
+
+static void isp_video_output_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *buf = container_of(vb, struct fimc_is_buf, vb);
+
+ fimc_is_pipeline_buf_lock(isp->pipeline);
+ fimc_is_isp_wait_queue_add(isp, buf);
+ fimc_is_pipeline_buf_unlock(isp->pipeline);
+
+ /* Call shot command */
+ fimc_is_pipeline_shot_safe(isp->pipeline);
+}
+
+static const struct vb2_ops isp_video_output_qops = {
+ .queue_setup = isp_video_output_queue_setup,
+ .buf_init = isp_video_output_buffer_init,
+ .buf_prepare = isp_video_output_buffer_prepare,
+ .buf_queue = isp_video_output_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = isp_video_output_start_streaming,
+ .stop_streaming = isp_video_output_stop_streaming,
+};
+
+static const struct v4l2_file_operations isp_video_output_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int isp_querycap_output(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, ISP_DRV_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, ISP_DRV_NAME, sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ ISP_DRV_NAME);
+ cap->device_caps = V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int isp_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_is_fmt *fmt;
+
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int isp_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_is_isp *isp = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
+ const struct fimc_is_fmt *fmt = isp->fmt;
+
+ plane_fmt->bytesperline = (isp->width * fmt->depth[0]) / 8;
+ plane_fmt->sizeimage = plane_fmt->bytesperline * isp->height;
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+
+ pixm->num_planes = fmt->num_planes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->width = isp->width;
+ pixm->height = isp->height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ return 0;
+}
+
+static int isp_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ const struct fimc_is_fmt *fmt;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
+
+ fmt = find_format(f);
+ if (!fmt)
+ fmt = (struct fimc_is_fmt *) &formats[0];
+
+ v4l_bound_align_image(&pixm->width,
+ ISP_MIN_WIDTH + SENSOR_WIDTH_PADDING,
+ ISP_MAX_WIDTH + SENSOR_WIDTH_PADDING, 0,
+ &pixm->height,
+ ISP_MIN_HEIGHT + SENSOR_HEIGHT_PADDING,
+ ISP_MAX_HEIGHT + SENSOR_HEIGHT_PADDING, 0,
+ 0);
+
+ plane_fmt->bytesperline = (pixm->width * fmt->depth[0]) / 8;
+ plane_fmt->sizeimage = (pixm->width * pixm->height *
+ fmt->depth[0]) / 8;
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+
+ pixm->num_planes = fmt->num_planes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->field = V4L2_FIELD_NONE;
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ return 0;
+}
+
+static int isp_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_is_isp *isp = video_drvdata(file);
+ const struct fimc_is_fmt *fmt;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ int ret;
+
+ ret = isp_try_fmt_mplane(file, priv, f);
+ if (ret)
+ return ret;
+
+ /* Get format type */
+ fmt = find_format(f);
+ if (!fmt) {
+ fmt = &formats[0];
+ pixm->pixelformat = fmt->fourcc;
+ pixm->num_planes = fmt->num_planes;
+ }
+
+ isp->fmt = fmt;
+ isp->width = pixm->width;
+ isp->height = pixm->height;
+ isp->size_image = pixm->plane_fmt[0].sizeimage;
+ set_bit(STATE_INIT, &isp->output_state);
+ return 0;
+}
+
+static int isp_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_is_isp *isp = video_drvdata(file);
+ int ret;
+
+ reqbufs->count = max_t(u32, FIMC_IS_ISP_REQ_BUFS_MIN, reqbufs->count);
+ ret = vb2_reqbufs(&isp->vbq, reqbufs);
+ if (ret) {
+ v4l2_err(&isp->subdev, "vb2 req buffers failed\n");
+ return ret;
+ }
+
+ if (reqbufs->count < FIMC_IS_ISP_REQ_BUFS_MIN) {
+ reqbufs->count = 0;
+ vb2_reqbufs(&isp->vbq, reqbufs);
+ return -ENOMEM;
+ }
+ set_bit(STATE_BUFS_ALLOCATED, &isp->output_state);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_video_output_ioctl_ops = {
+ .vidioc_querycap = isp_querycap_output,
+ .vidioc_enum_fmt_vid_out_mplane = isp_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = isp_try_fmt_mplane,
+ .vidioc_s_fmt_vid_out_mplane = isp_s_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = isp_g_fmt_mplane,
+ .vidioc_reqbufs = isp_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static int isp_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_is_isp *isp = v4l2_get_subdevdata(sd);
+ struct vb2_queue *q = &isp->vbq;
+ struct video_device *vfd = &isp->vfd;
+ int ret;
+
+ mutex_init(&isp->video_lock);
+
+ memset(vfd, 0, sizeof(*vfd));
+ snprintf(vfd->name, sizeof(vfd->name), "fimc-is-isp.output");
+
+ vfd->fops = &isp_video_output_fops;
+ vfd->ioctl_ops = &isp_video_output_ioctl_ops;
+ vfd->v4l2_dev = sd->v4l2_dev;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &isp->video_lock;
+ vfd->queue = q;
+ vfd->vfl_dir = VFL_DIR_TX;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->ops = &isp_video_output_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct fimc_is_buf);
+ q->drv_priv = isp;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ return ret;
+
+ isp->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &isp->vd_pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vfd, isp);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vfd->entity);
+ return ret;
+ }
+
+ v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+}
+
+static void isp_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_is_isp *isp = v4l2_get_subdevdata(sd);
+
+ if (isp && video_is_registered(&isp->vfd))
+ video_unregister_device(&isp->vfd);
+}
+
+static const struct v4l2_subdev_internal_ops isp_subdev_internal_ops = {
+ .registered = isp_subdev_registered,
+ .unregistered = isp_subdev_unregistered,
+};
+
+static struct fimc_is_sensor *fimc_is_get_sensor(struct fimc_is *is,
+ int sensor_id)
+{
+ int i;
+
+ for (i = 0; i < FIMC_IS_NUM_SENSORS; i++) {
+ if (is->sensor[i].drvdata->id == sensor_id)
+ return &is->sensor[i];
+ }
+ return NULL;
+}
+
+static int isp_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_is_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = isp->pipeline->is;
+ struct v4l2_subdev *sensor_sd = isp->sensor_sd;
+ struct fimc_is_sensor *sensor;
+ const struct sensor_drv_data *sdata;
+ int ret;
+
+ if (!sensor_sd)
+ return -EINVAL;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&is->pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ sdata = exynos5_is_sensor_get_drvdata(sensor_sd->dev->of_node);
+ sensor = fimc_is_get_sensor(is, sdata->id);
+
+ ret = fimc_is_pipeline_open(isp->pipeline, sensor);
+ if (ret)
+ v4l2_err(&isp->subdev, "Pipeline open failed\n");
+ } else {
+ ret = fimc_is_pipeline_close(isp->pipeline);
+ if (ret)
+ v4l2_err(&isp->subdev, "Pipeline close failed\n");
+ pm_runtime_put_sync(&is->pdev->dev);
+ }
+
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops isp_core_ops = {
+ .s_power = isp_s_power,
+};
+
+static int isp_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct fimc_is_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = isp->pipeline->is;
+ struct v4l2_subdev *sensor_sd = isp->sensor_sd;
+ struct v4l2_subdev_format fmt;
+ const struct sensor_drv_data *sdata;
+ struct fimc_is_sensor *sensor;
+ int ret;
+
+ if (!sensor_sd)
+ return -EINVAL;
+
+ if (enable) {
+ sdata = exynos5_is_sensor_get_drvdata(sensor_sd->dev->of_node);
+ sensor = fimc_is_get_sensor(is, sdata->id);
+ /* Retrieve the sensor format */
+ fmt.pad = 0;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, &fmt);
+ if (ret)
+ return ret;
+
+ sensor->width = fmt.format.width - SENSOR_WIDTH_PADDING;
+ sensor->height = fmt.format.height - SENSOR_HEIGHT_PADDING;
+ sensor->pixel_width = fmt.format.width;
+ sensor->pixel_height = fmt.format.height;
+
+ /* Check sensor resolution match */
+ if ((sensor->pixel_width != isp->width) ||
+ (sensor->pixel_height != isp->height)) {
+ v4l2_err(sd, "Resolution mismatch\n");
+ return -EPIPE;
+ }
+
+ ret = fimc_is_pipeline_start(isp->pipeline, 1);
+ if (ret)
+ v4l2_err(sd, "Pipeline start failed.\n");
+ } else {
+ ret = fimc_is_pipeline_stop(isp->pipeline, 1);
+ if (ret)
+ v4l2_err(sd, "Pipeline stop failed.\n");
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops isp_video_ops = {
+ .s_stream = isp_s_stream,
+};
+
+static struct v4l2_subdev_ops isp_subdev_ops = {
+ .core = &isp_core_ops,
+ .video = &isp_video_ops,
+};
+
+int fimc_is_isp_subdev_create(struct fimc_is_isp *isp,
+ struct vb2_alloc_ctx *alloc_ctx,
+ struct fimc_is_pipeline *pipeline)
+{
+ struct v4l2_ctrl_handler *handler = &isp->ctrl_handler;
+ struct v4l2_subdev *sd = &isp->subdev;
+ int ret;
+
+ isp->alloc_ctx = alloc_ctx;
+ isp->pipeline = pipeline;
+ isp->fmt = &formats[1];
+ INIT_LIST_HEAD(&isp->wait_queue);
+ INIT_LIST_HEAD(&isp->run_queue);
+ isp->width = ISP_DEF_WIDTH;
+ isp->height = ISP_DEF_HEIGHT;
+
+ v4l2_subdev_init(sd, &isp_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), ISP_DRV_NAME);
+
+ isp->subdev_pads[ISP_SD_PAD_SINK_DMA].flags = MEDIA_PAD_FL_SINK;
+ isp->subdev_pads[ISP_SD_PAD_SINK_OTF].flags = MEDIA_PAD_FL_SINK;
+ isp->subdev_pads[ISP_SD_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, ISP_SD_PADS_NUM,
+ isp->subdev_pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (handler->error)
+ goto err_ctrl;
+
+ sd->ctrl_handler = handler;
+ sd->internal_ops = &isp_subdev_internal_ops;
+ v4l2_set_subdevdata(sd, isp);
+
+ return 0;
+
+err_ctrl:
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(handler);
+ return ret;
+}
+
+void fimc_is_isp_subdev_destroy(struct fimc_is_isp *isp)
+{
+ struct v4l2_subdev *sd = &isp->subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&isp->ctrl_handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.h b/drivers/media/platform/exynos5-is/fimc-is-isp.h
new file mode 100644
index 00000000000..fdb6d86bd2a
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-isp.h
@@ -0,0 +1,90 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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_ISP_H_
+#define FIMC_IS_ISP_H_
+
+#include "fimc-is-core.h"
+#include "fimc-is-pipeline.h"
+
+#define FIMC_IS_ISP_REQ_BUFS_MIN 2
+
+#define ISP_SD_PAD_SINK_DMA 0
+#define ISP_SD_PAD_SINK_OTF 1
+#define ISP_SD_PAD_SRC 2
+#define ISP_SD_PADS_NUM 3
+
+#define ISP_DEF_WIDTH 1296
+#define ISP_DEF_HEIGHT 732
+
+#define ISP_MAX_WIDTH 4808
+#define ISP_MAX_HEIGHT 3356
+#define ISP_MIN_WIDTH 32
+#define ISP_MIN_HEIGHT 32
+
+#define ISP_MAX_BUFS 2
+
+/**
+ * struct fimc_is_isp - ISP context
+ * @vfd: video device node
+ * @fh: v4l2 file handle
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @subdev: fimc-is-isp subdev
+ * @vd_pad: media pad for the output video node
+ * @subdev_pads: the subdev media pads
+ * @ctrl_handler: v4l2 control handler
+ * @video_lock: video lock mutex
+ * @sensor_sd: sensor subdev used with this isp instance
+ * @pipeline: pipeline instance for this isp context
+ * @vbq: vb2 buffers queue for ISP output video node
+ * @wait_queue: list holding buffers waiting to be queued to HW
+ * @wait_queue_cnt: wait queue number of buffers
+ * @run_queue: list holding buffers queued to HW
+ * @run_queue_cnt: run queue number of buffers
+ * @output_bufs: isp output buffers array
+ * @out_buf_cnt: number of output buffers in use
+ * @fmt: output plane format for isp
+ * @width: user configured input width
+ * @height: user configured input height
+ * @size_image: image size in bytes
+ * @output_state: state of the output video node operations
+ */
+struct fimc_is_isp {
+ struct video_device vfd;
+ struct v4l2_fh fh;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_subdev subdev;
+ struct media_pad vd_pad;
+ struct media_pad subdev_pads[ISP_SD_PADS_NUM];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct mutex video_lock;
+
+ struct v4l2_subdev *sensor_sd;
+ struct fimc_is_pipeline *pipeline;
+
+ struct vb2_queue vbq;
+ struct list_head wait_queue;
+ unsigned int wait_queue_cnt;
+ struct list_head run_queue;
+ unsigned int run_queue_cnt;
+
+ const struct fimc_is_fmt *fmt;
+ unsigned int width;
+ unsigned int height;
+ unsigned int size_image;
+ unsigned long output_state;
+};
+
+int fimc_is_isp_subdev_create(struct fimc_is_isp *isp,
+ struct vb2_alloc_ctx *alloc_ctx,
+ struct fimc_is_pipeline *pipeline);
+void fimc_is_isp_subdev_destroy(struct fimc_is_isp *isp);
+
+#endif /* FIMC_IS_ISP_H_ */
diff --git a/drivers/media/platform/exynos5-is/fimc-is-metadata.h b/drivers/media/platform/exynos5-is/fimc-is-metadata.h
new file mode 100644
index 00000000000..02367c418d7
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-metadata.h
@@ -0,0 +1,767 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Kil-yeon Lim <kilyeon.im@samsung.com>
+ * Arun Kumar K <arun.kk@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_METADATA_H_
+#define FIMC_IS_METADATA_H_
+
+struct rational {
+ uint32_t num;
+ uint32_t den;
+};
+
+#define CAMERA2_MAX_AVAILABLE_MODE 21
+#define CAMERA2_MAX_FACES 16
+
+/*
+ * Controls/dynamic metadata
+ */
+
+enum metadata_mode {
+ METADATA_MODE_NONE,
+ METADATA_MODE_FULL
+};
+
+struct camera2_request_ctl {
+ uint32_t id;
+ enum metadata_mode metadatamode;
+ uint8_t outputstreams[16];
+ uint32_t framecount;
+};
+
+struct camera2_request_dm {
+ uint32_t id;
+ enum metadata_mode metadatamode;
+ uint32_t framecount;
+};
+
+
+
+enum optical_stabilization_mode {
+ OPTICAL_STABILIZATION_MODE_OFF,
+ OPTICAL_STABILIZATION_MODE_ON
+};
+
+enum lens_facing {
+ LENS_FACING_BACK,
+ LENS_FACING_FRONT
+};
+
+struct camera2_lens_ctl {
+ uint32_t focus_distance;
+ float aperture;
+ float focal_length;
+ float filter_density;
+ enum optical_stabilization_mode optical_stabilization_mode;
+};
+
+struct camera2_lens_dm {
+ uint32_t focus_distance;
+ float aperture;
+ float focal_length;
+ float filter_density;
+ enum optical_stabilization_mode optical_stabilization_mode;
+ float focus_range[2];
+};
+
+struct camera2_lens_sm {
+ float minimum_focus_distance;
+ float hyper_focal_distance;
+ float available_focal_length[2];
+ float available_apertures;
+ /* assuming 1 aperture */
+ float available_filter_densities;
+ /* assuming 1 ND filter value */
+ enum optical_stabilization_mode available_optical_stabilization;
+ /* assuming 1 */
+ uint32_t shading_map_size;
+ float shading_map[3][40][30];
+ uint32_t geometric_correction_map_size;
+ float geometric_correction_map[2][3][40][30];
+ enum lens_facing facing;
+ float position[2];
+};
+
+enum sensor_colorfilter_arrangement {
+ SENSOR_COLORFILTER_ARRANGEMENT_RGGB,
+ SENSOR_COLORFILTER_ARRANGEMENT_GRBG,
+ SENSOR_COLORFILTER_ARRANGEMENT_GBRG,
+ SENSOR_COLORFILTER_ARRANGEMENT_BGGR,
+ SENSOR_COLORFILTER_ARRANGEMENT_RGB
+};
+
+enum sensor_ref_illuminant {
+ SENSOR_ILLUMINANT_DAYLIGHT = 1,
+ SENSOR_ILLUMINANT_FLUORESCENT = 2,
+ SENSOR_ILLUMINANT_TUNGSTEN = 3,
+ SENSOR_ILLUMINANT_FLASH = 4,
+ SENSOR_ILLUMINANT_FINE_WEATHER = 9,
+ SENSOR_ILLUMINANT_CLOUDY_WEATHER = 10,
+ SENSOR_ILLUMINANT_SHADE = 11,
+ SENSOR_ILLUMINANT_DAYLIGHT_FLUORESCENT = 12,
+ SENSOR_ILLUMINANT_DAY_WHITE_FLUORESCENT = 13,
+ SENSOR_ILLUMINANT_COOL_WHITE_FLUORESCENT = 14,
+ SENSOR_ILLUMINANT_WHITE_FLUORESCENT = 15,
+ SENSOR_ILLUMINANT_STANDARD_A = 17,
+ SENSOR_ILLUMINANT_STANDARD_B = 18,
+ SENSOR_ILLUMINANT_STANDARD_C = 19,
+ SENSOR_ILLUMINANT_D55 = 20,
+ SENSOR_ILLUMINANT_D65 = 21,
+ SENSOR_ILLUMINANT_D75 = 22,
+ SENSOR_ILLUMINANT_D50 = 23,
+ SENSOR_ILLUMINANT_ISO_STUDIO_TUNGSTEN = 24
+};
+
+struct camera2_sensor_ctl {
+ /* unit : nano */
+ uint64_t exposure_time;
+ /* unit : nano(It's min frame duration */
+ uint64_t frame_duration;
+ /* unit : percent(need to change ISO value?) */
+ uint32_t sensitivity;
+};
+
+struct camera2_sensor_dm {
+ uint64_t exposure_time;
+ uint64_t frame_duration;
+ uint32_t sensitivity;
+ uint64_t timestamp;
+};
+
+struct camera2_sensor_sm {
+ uint32_t exposure_time_range[2];
+ uint32_t max_frame_duration;
+ /* list of available sensitivities. */
+ uint32_t available_sensitivities[10];
+ enum sensor_colorfilter_arrangement colorfilter_arrangement;
+ float physical_size[2];
+ uint32_t pixel_array_size[2];
+ uint32_t active_array_size[4];
+ uint32_t white_level;
+ uint32_t black_level_pattern[4];
+ struct rational color_transform1[9];
+ struct rational color_transform2[9];
+ enum sensor_ref_illuminant reference_illuminant1;
+ enum sensor_ref_illuminant reference_illuminant2;
+ struct rational forward_matrix1[9];
+ struct rational forward_matrix2[9];
+ struct rational calibration_transform1[9];
+ struct rational calibration_transform2[9];
+ struct rational base_gain_factor;
+ uint32_t max_analog_sensitivity;
+ float noise_model_coefficients[2];
+ uint32_t orientation;
+};
+
+
+
+enum flash_mode {
+ CAM2_FLASH_MODE_OFF = 1,
+ CAM2_FLASH_MODE_SINGLE,
+ CAM2_FLASH_MODE_TORCH,
+ CAM2_FLASH_MODE_BEST
+};
+
+struct camera2_flash_ctl {
+ enum flash_mode flash_mode;
+ uint32_t firing_power;
+ uint64_t firing_time;
+};
+
+struct camera2_flash_dm {
+ enum flash_mode flash_mode;
+ /* 10 is max power */
+ uint32_t firing_power;
+ /* unit : microseconds */
+ uint64_t firing_time;
+ /* 1 : stable, 0 : unstable */
+ uint32_t firing_stable;
+ /* 1 : success, 0 : fail */
+ uint32_t decision;
+};
+
+struct camera2_flash_sm {
+ uint32_t available;
+ uint64_t charge_duration;
+};
+
+enum processing_mode {
+ PROCESSING_MODE_OFF = 1,
+ PROCESSING_MODE_FAST,
+ PROCESSING_MODE_HIGH_QUALITY
+};
+
+
+struct camera2_hotpixel_ctl {
+ enum processing_mode mode;
+};
+
+struct camera2_hotpixel_dm {
+ enum processing_mode mode;
+};
+
+struct camera2_demosaic_ctl {
+ enum processing_mode mode;
+};
+
+struct camera2_demosaic_dm {
+ enum processing_mode mode;
+};
+
+struct camera2_noise_reduction_ctl {
+ enum processing_mode mode;
+ uint32_t strength;
+};
+
+struct camera2_noise_reduction_dm {
+ enum processing_mode mode;
+ uint32_t strength;
+};
+
+struct camera2_shading_ctl {
+ enum processing_mode mode;
+};
+
+struct camera2_shading_dm {
+ enum processing_mode mode;
+};
+
+struct camera2_geometric_ctl {
+ enum processing_mode mode;
+};
+
+struct camera2_geometric_dm {
+ enum processing_mode mode;
+};
+
+enum color_correction_mode {
+ COLOR_CORRECTION_MODE_FAST = 1,
+ COLOR_CORRECTION_MODE_HIGH_QUALITY,
+ COLOR_CORRECTION_MODE_TRANSFORM_MATRIX,
+ COLOR_CORRECTION_MODE_EFFECT_MONO,
+ COLOR_CORRECTION_MODE_EFFECT_NEGATIVE,
+ COLOR_CORRECTION_MODE_EFFECT_SOLARIZE,
+ COLOR_CORRECTION_MODE_EFFECT_SEPIA,
+ COLOR_CORRECTION_MODE_EFFECT_POSTERIZE,
+ COLOR_CORRECTION_MODE_EFFECT_WHITEBOARD,
+ COLOR_CORRECTION_MODE_EFFECT_BLACKBOARD,
+ COLOR_CORRECTION_MODE_EFFECT_AQUA
+};
+
+
+struct camera2_color_correction_ctl {
+ enum color_correction_mode mode;
+ float transform[9];
+ uint32_t hue;
+ uint32_t saturation;
+ uint32_t brightness;
+};
+
+struct camera2_color_correction_dm {
+ enum color_correction_mode mode;
+ float transform[9];
+ uint32_t hue;
+ uint32_t saturation;
+ uint32_t brightness;
+};
+
+struct camera2_color_correction_sm {
+ /* assuming 10 supported modes */
+ uint8_t available_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ uint32_t hue_range[2];
+ uint32_t saturation_range[2];
+ uint32_t brightness_range[2];
+};
+
+enum tonemap_mode {
+ TONEMAP_MODE_FAST = 1,
+ TONEMAP_MODE_HIGH_QUALITY,
+ TONEMAP_MODE_CONTRAST_CURVE
+};
+
+struct camera2_tonemap_ctl {
+ enum tonemap_mode mode;
+ /* assuming maxCurvePoints = 64 */
+ float curve_red[64];
+ float curve_green[64];
+ float curve_blue[64];
+};
+
+struct camera2_tonemap_dm {
+ enum tonemap_mode mode;
+ /* assuming maxCurvePoints = 64 */
+ float curve_red[64];
+ float curve_green[64];
+ float curve_blue[64];
+};
+
+struct camera2_tonemap_sm {
+ uint32_t max_curve_points;
+};
+
+struct camera2_edge_ctl {
+ enum processing_mode mode;
+ uint32_t strength;
+};
+
+struct camera2_edge_dm {
+ enum processing_mode mode;
+ uint32_t strength;
+};
+
+enum scaler_formats {
+ SCALER_FORMAT_BAYER_RAW,
+ SCALER_FORMAT_YV12,
+ SCALER_FORMAT_NV21,
+ SCALER_FORMAT_JPEG,
+ SCALER_FORMAT_UNKNOWN
+};
+
+struct camera2_scaler_ctl {
+ uint32_t crop_region[3];
+};
+
+struct camera2_scaler_dm {
+ uint32_t crop_region[3];
+};
+
+struct camera2_scaler_sm {
+ enum scaler_formats available_formats[4];
+ /* assuming # of availableFormats = 4 */
+ uint32_t available_raw_sizes;
+ uint64_t available_raw_min_durations;
+ /* needs check */
+ uint32_t available_processed_sizes[8];
+ uint64_t available_processed_min_durations[8];
+ uint32_t available_jpeg_sizes[8][2];
+ uint64_t available_jpeg_min_durations[8];
+ uint32_t available_max_digital_zoom[8];
+};
+
+struct camera2_jpeg_ctl {
+ uint32_t quality;
+ uint32_t thumbnail_size[2];
+ uint32_t thumbnail_quality;
+ double gps_coordinates[3];
+ uint32_t gps_processing_method;
+ uint64_t gps_timestamp;
+ uint32_t orientation;
+};
+
+struct camera2_jpeg_dm {
+ uint32_t quality;
+ uint32_t thumbnail_size[2];
+ uint32_t thumbnail_quality;
+ double gps_coordinates[3];
+ uint32_t gps_processing_method;
+ uint64_t gps_timestamp;
+ uint32_t orientation;
+};
+
+struct camera2_jpeg_sm {
+ uint32_t available_thumbnail_sizes[8][2];
+ uint32_t maxsize;
+ /* assuming supported size=8 */
+};
+
+enum face_detect_mode {
+ FACEDETECT_MODE_OFF = 1,
+ FACEDETECT_MODE_SIMPLE,
+ FACEDETECT_MODE_FULL
+};
+
+enum stats_mode {
+ STATS_MODE_OFF = 1,
+ STATS_MODE_ON
+};
+
+struct camera2_stats_ctl {
+ enum face_detect_mode face_detect_mode;
+ enum stats_mode histogram_mode;
+ enum stats_mode sharpness_map_mode;
+};
+
+
+struct camera2_stats_dm {
+ enum face_detect_mode face_detect_mode;
+ uint32_t face_rectangles[CAMERA2_MAX_FACES][4];
+ uint8_t face_scores[CAMERA2_MAX_FACES];
+ uint32_t face_landmarks[CAMERA2_MAX_FACES][6];
+ uint32_t face_ids[CAMERA2_MAX_FACES];
+ enum stats_mode histogram_mode;
+ uint32_t histogram[3 * 256];
+ enum stats_mode sharpness_map_mode;
+};
+
+
+struct camera2_stats_sm {
+ uint8_t available_face_detect_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ /* assuming supported modes = 3 */
+ uint32_t max_face_count;
+ uint32_t histogram_bucket_count;
+ uint32_t max_histogram_count;
+ uint32_t sharpness_map_size[2];
+ uint32_t max_sharpness_map_value;
+};
+
+enum aa_capture_intent {
+ AA_CAPTURE_INTENT_CUSTOM = 0,
+ AA_CAPTURE_INTENT_PREVIEW,
+ AA_CAPTURE_INTENT_STILL_CAPTURE,
+ AA_CAPTURE_INTENT_VIDEO_RECORD,
+ AA_CAPTURE_INTENT_VIDEO_SNAPSHOT,
+ AA_CAPTURE_INTENT_ZERO_SHUTTER_LAG
+};
+
+enum aa_mode {
+ AA_CONTROL_OFF = 1,
+ AA_CONTROL_AUTO,
+ AA_CONTROL_USE_SCENE_MODE
+};
+
+enum aa_scene_mode {
+ AA_SCENE_MODE_UNSUPPORTED = 1,
+ AA_SCENE_MODE_FACE_PRIORITY,
+ AA_SCENE_MODE_ACTION,
+ AA_SCENE_MODE_PORTRAIT,
+ AA_SCENE_MODE_LANDSCAPE,
+ AA_SCENE_MODE_NIGHT,
+ AA_SCENE_MODE_NIGHT_PORTRAIT,
+ AA_SCENE_MODE_THEATRE,
+ AA_SCENE_MODE_BEACH,
+ AA_SCENE_MODE_SNOW,
+ AA_SCENE_MODE_SUNSET,
+ AA_SCENE_MODE_STEADYPHOTO,
+ AA_SCENE_MODE_FIREWORKS,
+ AA_SCENE_MODE_SPORTS,
+ AA_SCENE_MODE_PARTY,
+ AA_SCENE_MODE_CANDLELIGHT,
+ AA_SCENE_MODE_BARCODE,
+ AA_SCENE_MODE_NIGHT_CAPTURE
+};
+
+enum aa_effect_mode {
+ AA_EFFECT_OFF = 1,
+ AA_EFFECT_MONO,
+ AA_EFFECT_NEGATIVE,
+ AA_EFFECT_SOLARIZE,
+ AA_EFFECT_SEPIA,
+ AA_EFFECT_POSTERIZE,
+ AA_EFFECT_WHITEBOARD,
+ AA_EFFECT_BLACKBOARD,
+ AA_EFFECT_AQUA
+};
+
+enum aa_aemode {
+ AA_AEMODE_OFF = 1,
+ AA_AEMODE_LOCKED,
+ AA_AEMODE_ON,
+ AA_AEMODE_ON_AUTO_FLASH,
+ AA_AEMODE_ON_ALWAYS_FLASH,
+ AA_AEMODE_ON_AUTO_FLASH_REDEYE
+};
+
+enum aa_ae_flashmode {
+ /* all flash control stop */
+ AA_FLASHMODE_OFF = 1,
+ /* internal 3A can control flash */
+ AA_FLASHMODE_ON,
+ /* internal 3A can do auto flash algorithm */
+ AA_FLASHMODE_AUTO,
+ /* internal 3A can fire flash by auto result */
+ AA_FLASHMODE_CAPTURE,
+ /* internal 3A can control flash forced */
+ AA_FLASHMODE_ON_ALWAYS
+
+};
+
+enum aa_ae_antibanding_mode {
+ AA_AE_ANTIBANDING_OFF = 1,
+ AA_AE_ANTIBANDING_50HZ,
+ AA_AE_ANTIBANDING_60HZ,
+ AA_AE_ANTIBANDING_AUTO
+};
+
+enum aa_awbmode {
+ AA_AWBMODE_OFF = 1,
+ AA_AWBMODE_LOCKED,
+ AA_AWBMODE_WB_AUTO,
+ AA_AWBMODE_WB_INCANDESCENT,
+ AA_AWBMODE_WB_FLUORESCENT,
+ AA_AWBMODE_WB_WARM_FLUORESCENT,
+ AA_AWBMODE_WB_DAYLIGHT,
+ AA_AWBMODE_WB_CLOUDY_DAYLIGHT,
+ AA_AWBMODE_WB_TWILIGHT,
+ AA_AWBMODE_WB_SHADE
+};
+
+enum aa_afmode {
+ AA_AFMODE_OFF = 1,
+ AA_AFMODE_AUTO,
+ AA_AFMODE_MACRO,
+ AA_AFMODE_CONTINUOUS_VIDEO,
+ AA_AFMODE_CONTINUOUS_PICTURE,
+ AA_AFMODE_EDOF
+};
+
+enum aa_afstate {
+ AA_AFSTATE_INACTIVE = 1,
+ AA_AFSTATE_PASSIVE_SCAN,
+ AA_AFSTATE_ACTIVE_SCAN,
+ AA_AFSTATE_AF_ACQUIRED_FOCUS,
+ AA_AFSTATE_AF_FAILED_FOCUS
+};
+
+enum ae_state {
+ AE_STATE_INACTIVE = 1,
+ AE_STATE_SEARCHING,
+ AE_STATE_CONVERGED,
+ AE_STATE_LOCKED,
+ AE_STATE_FLASH_REQUIRED,
+ AE_STATE_PRECAPTURE
+};
+
+enum awb_state {
+ AWB_STATE_INACTIVE = 1,
+ AWB_STATE_SEARCHING,
+ AWB_STATE_CONVERGED,
+ AWB_STATE_LOCKED
+};
+
+enum aa_isomode {
+ AA_ISOMODE_AUTO = 1,
+ AA_ISOMODE_MANUAL,
+};
+
+struct camera2_aa_ctl {
+ enum aa_capture_intent capture_intent;
+ enum aa_mode mode;
+ enum aa_scene_mode scene_mode;
+ uint32_t video_stabilization_mode;
+ enum aa_aemode ae_mode;
+ uint32_t ae_regions[5];
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region. */
+ int32_t ae_exp_compensation;
+ uint32_t ae_target_fps_range[2];
+ enum aa_ae_antibanding_mode ae_anti_banding_mode;
+ enum aa_ae_flashmode ae_flash_mode;
+ enum aa_awbmode awb_mode;
+ uint32_t awb_regions[5];
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region. */
+ enum aa_afmode af_mode;
+ uint32_t af_regions[5];
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region. */
+ uint32_t af_trigger;
+ enum aa_isomode iso_mode;
+ uint32_t iso_value;
+
+};
+
+struct camera2_aa_dm {
+ enum aa_mode mode;
+ enum aa_effect_mode effect_mode;
+ enum aa_scene_mode scene_mode;
+ uint32_t video_stabilization_mode;
+ enum aa_aemode ae_mode;
+ uint32_t ae_regions[5];
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region. */
+ enum ae_state ae_state;
+ enum aa_ae_flashmode ae_flash_mode;
+ enum aa_awbmode awb_mode;
+ uint32_t awb_regions[5];
+ enum awb_state awb_state;
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region. */
+ enum aa_afmode af_mode;
+ uint32_t af_regions[5];
+ /* 5 per region(x1,y1,x2,y2,weight). Currently assuming 1 region */
+ enum aa_afstate af_state;
+ enum aa_isomode iso_mode;
+ uint32_t iso_value;
+};
+
+struct camera2_aa_sm {
+ uint8_t available_scene_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ uint8_t available_effects[CAMERA2_MAX_AVAILABLE_MODE];
+ /* Assuming # of available scene modes = 10 */
+ uint32_t max_regions;
+ uint8_t ae_available_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ /* Assuming # of available ae modes = 8 */
+ struct rational ae_compensation_step;
+ int32_t ae_compensation_range[2];
+ uint32_t ae_available_target_fps_ranges[CAMERA2_MAX_AVAILABLE_MODE][2];
+ uint8_t ae_available_antibanding_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ uint8_t awb_available_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ /* Assuming # of awbAvailableModes = 10 */
+ uint8_t af_available_modes[CAMERA2_MAX_AVAILABLE_MODE];
+ /* Assuming # of afAvailableModes = 4 */
+ uint8_t available_video_stabilization_modes[4];
+ /* Assuming # of availableVideoStabilizationModes = 4 */
+ uint32_t iso_range[2];
+};
+
+struct camera2_lens_usm {
+ /* Frame delay between sending command and applying frame data */
+ uint32_t focus_distance_frame_delay;
+};
+
+struct camera2_sensor_usm {
+ /* Frame delay between sending command and applying frame data */
+ uint32_t exposure_time_frame_delay;
+ uint32_t frame_duration_frame_delay;
+ uint32_t sensitivity_frame_delay;
+};
+
+struct camera2_flash_usm {
+ /* Frame delay between sending command and applying frame data */
+ uint32_t flash_mode_frame_delay;
+ uint32_t firing_power_frame_delay;
+ uint64_t firing_time_frame_delay;
+};
+
+struct camera2_ctl {
+ struct camera2_request_ctl request;
+ struct camera2_lens_ctl lens;
+ struct camera2_sensor_ctl sensor;
+ struct camera2_flash_ctl flash;
+ struct camera2_hotpixel_ctl hotpixel;
+ struct camera2_demosaic_ctl demosaic;
+ struct camera2_noise_reduction_ctl noise;
+ struct camera2_shading_ctl shading;
+ struct camera2_geometric_ctl geometric;
+ struct camera2_color_correction_ctl color;
+ struct camera2_tonemap_ctl tonemap;
+ struct camera2_edge_ctl edge;
+ struct camera2_scaler_ctl scaler;
+ struct camera2_jpeg_ctl jpeg;
+ struct camera2_stats_ctl stats;
+ struct camera2_aa_ctl aa;
+};
+
+struct camera2_dm {
+ struct camera2_request_dm request;
+ struct camera2_lens_dm lens;
+ struct camera2_sensor_dm sensor;
+ struct camera2_flash_dm flash;
+ struct camera2_hotpixel_dm hotpixel;
+ struct camera2_demosaic_dm demosaic;
+ struct camera2_noise_reduction_dm noise;
+ struct camera2_shading_dm shading;
+ struct camera2_geometric_dm geometric;
+ struct camera2_color_correction_dm color;
+ struct camera2_tonemap_dm tonemap;
+ struct camera2_edge_dm edge;
+ struct camera2_scaler_dm scaler;
+ struct camera2_jpeg_dm jpeg;
+ struct camera2_stats_dm stats;
+ struct camera2_aa_dm aa;
+};
+
+struct camera2_sm {
+ struct camera2_lens_sm lens;
+ struct camera2_sensor_sm sensor;
+ struct camera2_flash_sm flash;
+ struct camera2_color_correction_sm color;
+ struct camera2_tonemap_sm tonemap;
+ struct camera2_scaler_sm scaler;
+ struct camera2_jpeg_sm jpeg;
+ struct camera2_stats_sm stats;
+ struct camera2_aa_sm aa;
+
+ /* User-defined(ispfw specific) static metadata. */
+ struct camera2_lens_usm lensud;
+ struct camera2_sensor_usm sensor_ud;
+ struct camera2_flash_usm flash_ud;
+};
+
+/*
+ * User-defined control for lens.
+ */
+struct camera2_lens_uctl {
+ struct camera2_lens_ctl ctl;
+
+ /* It depends by af algorithm(normally 255 or 1023) */
+ uint32_t max_pos;
+ /* Some actuator support slew rate control. */
+ uint32_t slew_rate;
+};
+
+/*
+ * User-defined metadata for lens.
+ */
+struct camera2_lens_udm {
+ /* It depends by af algorithm(normally 255 or 1023) */
+ uint32_t max_pos;
+ /* Some actuator support slew rate control. */
+ uint32_t slew_rate;
+};
+
+/*
+ * User-defined control for sensor.
+ */
+struct camera2_sensor_uctl {
+ struct camera2_sensor_ctl ctl;
+ /* Dynamic frame duration.
+ * This feature is decided to max. value between
+ * 'sensor.exposureTime'+alpha and 'sensor.frameDuration'.
+ */
+ uint64_t dynamic_frame_duration;
+};
+
+struct camera2_scaler_uctl {
+ /* Target address for next frame.
+ * [0] invalid address, stop
+ * [others] valid address
+ */
+ uint32_t scc_target_address[4];
+ uint32_t scp_target_address[4];
+};
+
+struct camera2_flash_uctl {
+ struct camera2_flash_ctl ctl;
+};
+
+struct camera2_uctl {
+ /* Set sensor, lens, flash control for next frame.
+ * This flag can be combined.
+ * [0 bit] lens
+ * [1 bit] sensor
+ * [2 bit] flash
+ */
+ uint32_t u_update_bitmap;
+
+ /* For debugging */
+ uint32_t u_frame_number;
+
+ /* isp fw specific control (user-defined) of lens. */
+ struct camera2_lens_uctl lens_ud;
+ /* isp fw specific control (user-defined) of sensor. */
+ struct camera2_sensor_uctl sensor_ud;
+ /* isp fw specific control (user-defined) of flash. */
+ struct camera2_flash_uctl flash_ud;
+
+ struct camera2_scaler_uctl scaler_ud;
+};
+
+struct camera2_udm {
+ struct camera2_lens_udm lens;
+};
+
+struct camera2_shot {
+ /* standard area */
+ struct camera2_ctl ctl;
+ struct camera2_dm dm;
+ /* user defined area */
+ struct camera2_uctl uctl;
+ struct camera2_udm udm;
+ /* magic : 23456789 */
+ uint32_t magicnumber;
+};
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-param.h b/drivers/media/platform/exynos5-is/fimc-is-param.h
new file mode 100644
index 00000000000..015cc13130a
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-param.h
@@ -0,0 +1,1159 @@
+/*
+ * Samsung Exynos5 SoC series FIMC-IS driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd
+ * Kil-yeon Lim <kilyeon.im@samsung.com>
+ * Arun Kumar K <arun.kk@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
+
+#define MAGIC_NUMBER 0x01020304
+
+#define PARAMETER_MAX_SIZE 128 /* in bytes */
+#define PARAMETER_MAX_MEMBER (PARAMETER_MAX_SIZE / 4)
+
+enum is_param_set_bit {
+ PARAM_GLOBAL_SHOTMODE = 0,
+ PARAM_SENSOR_CONTROL,
+ PARAM_SENSOR_OTF_INPUT,
+ PARAM_SENSOR_OTF_OUTPUT,
+ PARAM_SENSOR_FRAME_RATE,
+ PARAM_SENSOR_DMA_OUTPUT,
+ PARAM_BUFFER_CONTROL,
+ PARAM_BUFFER_OTF_INPUT,
+ PARAM_BUFFER_OTF_OUTPUT,
+ PARAM_ISP_CONTROL,
+ PARAM_ISP_OTF_INPUT = 10,
+ PARAM_ISP_DMA1_INPUT,
+ 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 = 20,
+ PARAM_ISP_OTF_OUTPUT,
+ 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 = 30,
+ PARAM_SCALERC_INPUT_CROP,
+ PARAM_SCALERC_OUTPUT_CROP,
+ PARAM_SCALERC_OTF_OUTPUT,
+ PARAM_SCALERC_DMA_OUTPUT = 34,
+ PARAM_ODC_CONTROL,
+ PARAM_ODC_OTF_INPUT,
+ PARAM_ODC_OTF_OUTPUT,
+ PARAM_DIS_CONTROL,
+ PARAM_DIS_OTF_INPUT,
+ PARAM_DIS_OTF_OUTPUT = 40,
+ PARAM_TDNR_CONTROL,
+ 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 = 50,
+ PARAM_SCALERP_ROTATION,
+ 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 = 58,
+ PARAM_END,
+};
+
+/* ---------------------- Input ----------------------------------- */
+enum control_command {
+ CONTROL_COMMAND_STOP = 0,
+ CONTROL_COMMAND_START = 1,
+ CONTROL_COMMAND_TEST = 2
+};
+
+enum bypass_command {
+ CONTROL_BYPASS_DISABLE = 0,
+ CONTROL_BYPASS_ENABLE = 1
+};
+
+enum control_error {
+ CONTROL_ERROR_NONE = 0
+};
+
+enum otf_input_command {
+ OTF_INPUT_COMMAND_DISABLE = 0,
+ OTF_INPUT_COMMAND_ENABLE = 1
+};
+
+enum otf_input_format {
+ OTF_INPUT_FORMAT_BAYER = 0, /* 1 Channel */
+ OTF_INPUT_FORMAT_YUV444 = 1, /* 3 Channel */
+ OTF_INPUT_FORMAT_YUV422 = 2, /* 3 Channel */
+ OTF_INPUT_FORMAT_YUV420 = 3, /* 3 Channel */
+ OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER = 10,
+ OTF_INPUT_FORMAT_BAYER_DMA = 11,
+};
+
+enum otf_input_bitwidth {
+ OTF_INPUT_BIT_WIDTH_14BIT = 14,
+ OTF_INPUT_BIT_WIDTH_12BIT = 12,
+ OTF_INPUT_BIT_WIDTH_11BIT = 11,
+ OTF_INPUT_BIT_WIDTH_10BIT = 10,
+ OTF_INPUT_BIT_WIDTH_9BIT = 9,
+ OTF_INPUT_BIT_WIDTH_8BIT = 8
+};
+
+enum otf_input_order {
+ OTF_INPUT_ORDER_BAYER_GR_BG = 0,
+ OTF_INPUT_ORDER_BAYER_RG_GB = 1,
+ OTF_INPUT_ORDER_BAYER_BG_GR = 2,
+ OTF_INPUT_ORDER_BAYER_GB_RG = 3
+};
+
+enum otf_intput_error {
+ OTF_INPUT_ERROR_NONE = 0 /* Input setting is done */
+};
+
+enum dma_input_command {
+ DMA_INPUT_COMMAND_DISABLE = 0,
+ DMA_INPUT_COMMAND_ENABLE = 1,
+ DMA_INPUT_COMMAND_BUF_MNGR = 2,
+ DMA_INPUT_COMMAND_RUN_SINGLE = 3,
+};
+
+enum dma_inut_format {
+ DMA_INPUT_FORMAT_BAYER = 0,
+ DMA_INPUT_FORMAT_YUV444 = 1,
+ DMA_INPUT_FORMAT_YUV422 = 2,
+ DMA_INPUT_FORMAT_YUV420 = 3,
+};
+
+enum dma_input_bitwidth {
+ DMA_INPUT_BIT_WIDTH_14BIT = 14,
+ DMA_INPUT_BIT_WIDTH_12BIT = 12,
+ DMA_INPUT_BIT_WIDTH_11BIT = 11,
+ DMA_INPUT_BIT_WIDTH_10BIT = 10,
+ DMA_INPUT_BIT_WIDTH_9BIT = 9,
+ DMA_INPUT_BIT_WIDTH_8BIT = 8
+};
+
+enum dma_input_order {
+ DMA_INPUT_ORDER_NONE = 0,
+ DMA_INPUT_ORDER_CBCR = 1,
+ DMA_INPUT_ORDER_CRCB = 2,
+ DMA_INPUT_ORDER_YCBCR = 3,
+ DMA_INPUT_ORDER_YYCBCR = 4,
+ DMA_INPUT_ORDER_YCBYCR = 5,
+ DMA_INPUT_ORDER_YCRYCB = 6,
+ DMA_INPUT_ORDER_CBYCRY = 7,
+ DMA_INPUT_ORDER_CRYCBY = 8,
+ DMA_INPUT_ORDER_GR_BG = 9
+};
+
+enum dma_input_error {
+ DMA_INPUT_ERROR_NONE = 0 /* DMA input setting is done */
+};
+
+/* ---------------------- Output ----------------------------------- */
+enum otf_output_crop {
+ OTF_OUTPUT_CROP_DISABLE = 0,
+ OTF_OUTPUT_CROP_ENABLE = 1
+};
+
+enum otf_output_command {
+ OTF_OUTPUT_COMMAND_DISABLE = 0,
+ OTF_OUTPUT_COMMAND_ENABLE = 1
+};
+
+enum orf_output_format {
+ OTF_OUTPUT_FORMAT_YUV444 = 1,
+ OTF_OUTPUT_FORMAT_YUV422 = 2,
+ OTF_OUTPUT_FORMAT_YUV420 = 3,
+ OTF_OUTPUT_FORMAT_RGB = 4
+};
+
+enum otf_output_bitwidth {
+ OTF_OUTPUT_BIT_WIDTH_14BIT = 14,
+ OTF_OUTPUT_BIT_WIDTH_12BIT = 12,
+ OTF_OUTPUT_BIT_WIDTH_11BIT = 11,
+ OTF_OUTPUT_BIT_WIDTH_10BIT = 10,
+ OTF_OUTPUT_BIT_WIDTH_9BIT = 9,
+ OTF_OUTPUT_BIT_WIDTH_8BIT = 8
+};
+
+enum otf_output_order {
+ OTF_OUTPUT_ORDER_BAYER_GR_BG = 0,
+};
+
+enum otf_output_error {
+ OTF_OUTPUT_ERROR_NONE = 0 /* Output Setting is done */
+};
+
+enum dma_output_command {
+ DMA_OUTPUT_COMMAND_DISABLE = 0,
+ DMA_OUTPUT_COMMAND_ENABLE = 1,
+ DMA_OUTPUT_COMMAND_BUF_MNGR = 2,
+ DMA_OUTPUT_UPDATE_MASK_BITS = 3
+};
+
+enum dma_output_format {
+ 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_bitwidth {
+ DMA_OUTPUT_BIT_WIDTH_14BIT = 14,
+ DMA_OUTPUT_BIT_WIDTH_12BIT = 12,
+ DMA_OUTPUT_BIT_WIDTH_11BIT = 11,
+ DMA_OUTPUT_BIT_WIDTH_10BIT = 10,
+ DMA_OUTPUT_BIT_WIDTH_9BIT = 9,
+ DMA_OUTPUT_BIT_WIDTH_8BIT = 8
+};
+
+enum dma_output_order {
+ DMA_OUTPUT_ORDER_NONE = 0,
+ DMA_OUTPUT_ORDER_CBCR = 1,
+ DMA_OUTPUT_ORDER_CRCB = 2,
+ DMA_OUTPUT_ORDER_YYCBCR = 3,
+ DMA_OUTPUT_ORDER_YCBYCR = 4,
+ DMA_OUTPUT_ORDER_YCRYCB = 5,
+ DMA_OUTPUT_ORDER_CBYCRY = 6,
+ DMA_OUTPUT_ORDER_CRYCBY = 7,
+ DMA_OUTPUT_ORDER_YCBCR = 8,
+ DMA_OUTPUT_ORDER_CRYCB = 9,
+ DMA_OUTPUT_ORDER_CRCBY = 10,
+ DMA_OUTPUT_ORDER_CBYCR = 11,
+ DMA_OUTPUT_ORDER_YCRCB = 12,
+ DMA_OUTPUT_ORDER_CBCRY = 13,
+ DMA_OUTPUT_ORDER_BGR = 14,
+ DMA_OUTPUT_ORDER_GB_BG = 15
+};
+
+enum dma_output_notify_dma_done {
+ DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE = 0,
+ DMA_OUTPUT_NOTIFY_DMA_DONE_ENBABLE = 1,
+};
+
+enum dma_output_error {
+ DMA_OUTPUT_ERROR_NONE = 0 /* DMA output setting is done */
+};
+
+/* ---------------------- Global ----------------------------------- */
+enum global_shotmode_error {
+ GLOBAL_SHOTMODE_ERROR_NONE = 0 /* shot-mode setting is done */
+};
+
+/* ------------------------- AA ------------------------------------ */
+enum isp_lock_command {
+ ISP_AA_COMMAND_START = 0,
+ ISP_AA_COMMAND_STOP = 1
+};
+
+enum isp_lock_target {
+ ISP_AA_TARGET_AF = 1,
+ ISP_AA_TARGET_AE = 2,
+ ISP_AA_TARGET_AWB = 4
+};
+
+enum isp_af_mode {
+ ISP_AF_MANUAL = 0,
+ ISP_AF_SINGLE,
+ ISP_AF_CONTINUOUS,
+ ISP_AF_REGION,
+ ISP_AF_SLEEP,
+ ISP_AF_INIT,
+ ISP_AF_SET_CENTER_WINDOW,
+ ISP_AF_SET_TOUCH_WINDOW,
+ ISP_AF_SET_FACE_WINDOW
+};
+
+enum isp_af_scene {
+ ISP_AF_SCENE_NORMAL = 0,
+ ISP_AF_SCENE_MACRO = 1
+};
+
+enum isp_af_touch {
+ ISP_AF_TOUCH_DISABLE = 0,
+ ISP_AF_TOUCH_ENABLE
+};
+
+enum isp_af_face {
+ ISP_AF_FACE_DISABLE = 0,
+ ISP_AF_FACE_ENABLE
+};
+
+enum isp_af_reponse {
+ ISP_AF_RESPONSE_PREVIEW = 0,
+ ISP_AF_RESPONSE_MOVIE
+};
+
+enum isp_af_sleep {
+ ISP_AF_SLEEP_OFF = 0,
+ ISP_AF_SLEEP_ON = 1
+};
+
+enum isp_af_continuous {
+ ISP_AF_CONTINUOUS_DISABLE = 0,
+ ISP_AF_CONTINUOUS_ENABLE = 1
+};
+
+enum isp_af_error {
+ ISP_AF_ERROR_NONE = 0, /* AF mode change is done */
+ ISP_AF_EROOR_NO_LOCK_DONE = 1 /* AF lock is done */
+};
+
+/* ------------------------- Flash ------------------------------------- */
+enum isp_flash_command {
+ ISP_FLASH_COMMAND_DISABLE = 0,
+ ISP_FLASH_COMMAND_MANUALON = 1, /* (forced flash) */
+ ISP_FLASH_COMMAND_AUTO = 2,
+ ISP_FLASH_COMMAND_TORCH = 3, /* 3 sec */
+ ISP_FLASH_COMMAND_FLASH_ON = 4,
+ ISP_FLASH_COMMAND_CAPTURE = 5,
+ ISP_FLASH_COMMAND_TRIGGER = 6,
+ ISP_FLASH_COMMAND_CALIBRATION = 7
+};
+
+enum isp_flash_redeye {
+ ISP_FLASH_REDEYE_DISABLE = 0,
+ ISP_FLASH_REDEYE_ENABLE = 1
+};
+
+enum isp_flash_error {
+ 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
+};
+
+enum isp_awb_error {
+ 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,
+ ISP_IMAGE_EFFECT_AQUA = 5,
+ ISP_IMAGE_EFFECT_EMBOSS = 6,
+ ISP_IMAGE_EFFECT_EMBOSS_MONO = 7,
+ ISP_IMAGE_EFFECT_SKETCH = 8,
+ ISP_IMAGE_EFFECT_RED_YELLOW_POINT = 9,
+ ISP_IMAGE_EFFECT_GREEN_POINT = 10,
+ ISP_IMAGE_EFFECT_BLUE_POINT = 11,
+ ISP_IMAGE_EFFECT_MAGENTA_POINT = 12,
+ ISP_IMAGE_EFFECT_WARM_VINTAGE = 13,
+ ISP_IMAGE_EFFECT_COLD_VINTAGE = 14,
+ ISP_IMAGE_EFFECT_POSTERIZE = 15,
+ ISP_IMAGE_EFFECT_SOLARIZE = 16,
+ ISP_IMAGE_EFFECT_WASHED = 17,
+ ISP_IMAGE_EFFECT_CCM = 18,
+};
+
+enum isp_imageeffect_error {
+ ISP_IMAGE_EFFECT_ERROR_NONE = 0 /* Image effect setting is done */
+};
+
+/* --------------------------- ISO ------------------------------------ */
+enum isp_iso_command {
+ ISP_ISO_COMMAND_AUTO = 0,
+ ISP_ISO_COMMAND_MANUAL = 1
+};
+
+enum iso_error {
+ ISP_ISO_ERROR_NONE = 0 /* ISO setting is done */
+};
+
+/* -------------------------- Adjust ----------------------------------- */
+enum iso_adjust_command {
+ ISP_ADJUST_COMMAND_AUTO = 0,
+ ISP_ADJUST_COMMAND_MANUAL_CONTRAST = (1 << 0),
+ ISP_ADJUST_COMMAND_MANUAL_SATURATION = (1 << 1),
+ ISP_ADJUST_COMMAND_MANUAL_SHARPNESS = (1 << 2),
+ ISP_ADJUST_COMMAND_MANUAL_EXPOSURE = (1 << 3),
+ ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS = (1 << 4),
+ ISP_ADJUST_COMMAND_MANUAL_HUE = (1 << 5),
+ ISP_ADJUST_COMMAND_MANUAL_HOTPIXEL = (1 << 6),
+ ISP_ADJUST_COMMAND_MANUAL_NOISEREDUCTION = (1 << 7),
+ ISP_ADJUST_COMMAND_MANUAL_SHADING = (1 << 8),
+ ISP_ADJUST_COMMAND_MANUAL_GAMMA = (1 << 9),
+ ISP_ADJUST_COMMAND_MANUAL_EDGEENHANCEMENT = (1 << 10),
+ ISP_ADJUST_COMMAND_MANUAL_SCENE = (1 << 11),
+ ISP_ADJUST_COMMAND_MANUAL_FRAMETIME = (1 << 12),
+ ISP_ADJUST_COMMAND_MANUAL_ALL = 0x1fff
+};
+
+enum isp_adjust_scene_index {
+ ISP_ADJUST_SCENE_NORMAL = 0,
+ ISP_ADJUST_SCENE_NIGHT_PREVIEW = 1,
+ ISP_ADJUST_SCENE_NIGHT_CAPTURE = 2
+};
+
+
+enum isp_adjust_error {
+ ISP_ADJUST_ERROR_NONE = 0 /* Adjust setting is done */
+};
+
+/* ------------------------- 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_COMMAND_EXPOSURE_MODE = (1 << 8)
+};
+
+enum isp_exposure_mode {
+ ISP_EXPOSUREMODE_OFF = 1,
+ ISP_EXPOSUREMODE_AUTO = 2
+};
+
+enum isp_metering_error {
+ 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
+};
+
+enum isp_afc_manual {
+ ISP_AFC_MANUAL_50HZ = 50,
+ ISP_AFC_MANUAL_60HZ = 60
+};
+
+enum isp_afc_error {
+ ISP_AFC_ERROR_NONE = 0 /* AFC setting is done */
+};
+
+enum isp_scene_command {
+ 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
+};
+
+/* -------------------------- Scaler --------------------------------- */
+enum scaler_imageeffect_command {
+ SCALER_IMAGE_EFFECT_COMMNAD_DISABLE = 0,
+ SCALER_IMAGE_EFFECT_COMMNAD_SEPIA_CB = 1,
+ SCALER_IMAGE_EFFECT_COMMAND_SEPIA_CR = 2,
+ SCALER_IMAGE_EFFECT_COMMAND_NEGATIVE = 3,
+ SCALER_IMAGE_EFFECT_COMMAND_ARTFREEZE = 4,
+ SCALER_IMAGE_EFFECT_COMMAND_EMBOSSING = 5,
+ SCALER_IMAGE_EFFECT_COMMAND_SILHOUETTE = 6
+};
+
+enum scaler_imageeffect_error {
+ SCALER_IMAGE_EFFECT_ERROR_NONE = 0
+};
+
+enum scaler_crop_command {
+ SCALER_CROP_COMMAND_DISABLE = 0,
+ SCALER_CROP_COMMAND_ENABLE = 1
+};
+
+enum scaler_crop_error {
+ SCALER_CROP_ERROR_NONE = 0 /* crop setting is done */
+};
+
+enum scaler_scaling_command {
+ SCALER_SCALING_COMMNAD_DISABLE = 0,
+ SCALER_SCALING_COMMAND_UP = 1,
+ SCALER_SCALING_COMMAND_DOWN = 2
+};
+
+enum scaler_scaling_error {
+ SCALER_SCALING_ERROR_NONE = 0
+};
+
+enum scaler_rotation_command {
+ SCALER_ROTATION_COMMAND_DISABLE = 0,
+ SCALER_ROTATION_COMMAND_CLOCKWISE90 = 1
+};
+
+enum scaler_rotation_error {
+ SCALER_ROTATION_ERROR_NONE = 0
+};
+
+enum scaler_flip_command {
+ SCALER_FLIP_COMMAND_NORMAL = 0,
+ SCALER_FLIP_COMMAND_X_MIRROR = 1,
+ SCALER_FLIP_COMMAND_Y_MIRROR = 2,
+ SCALER_FLIP_COMMAND_XY_MIRROR = 3 /* (180 rotation) */
+};
+
+enum scaler_flip_error {
+ SCALER_FLIP_ERROR_NONE = 0 /* flip setting is done */
+};
+
+/* -------------------------- 3DNR ----------------------------------- */
+enum tdnr_1st_frame_command {
+ TDNR_1ST_FRAME_COMMAND_NOPROCESSING = 0,
+ TDNR_1ST_FRAME_COMMAND_2DNR = 1
+};
+
+enum tdnr_1st_frame_error {
+ TDNR_1ST_FRAME_ERROR_NONE = 0
+};
+
+/* ---------------------------- 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,
+};
+
+enum fd_config_smile_mode {
+ FD_CONFIG_SMILE_MODE_DISABLE = 0,
+ FD_CONFIG_SMILE_MODE_ENABLE = 1
+};
+
+enum fd_config_blink_mode {
+ FD_CONFIG_BLINK_MODE_DISABLE = 0,
+ FD_CONFIG_BLINK_MODE_ENABLE = 1
+};
+
+enum fd_config_eye_result {
+ FD_CONFIG_EYES_DETECT_DISABLE = 0,
+ FD_CONFIG_EYES_DETECT_ENABLE = 1
+};
+
+enum fd_config_mouth_result {
+ FD_CONFIG_MOUTH_DETECT_DISABLE = 0,
+ FD_CONFIG_MOUTH_DETECT_ENABLE = 1
+};
+
+enum fd_config_orientation {
+ FD_CONFIG_ORIENTATION_DISABLE = 0,
+ FD_CONFIG_ORIENTATION_ENABLE = 1
+};
+
+struct param_control {
+ u32 cmd;
+ u32 bypass;
+ u32 buffer_address;
+ u32 buffer_number;
+ /* 0: continuous, 1: single */
+ u32 run_mode;
+ u32 reserved[PARAMETER_MAX_MEMBER - 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[PARAMETER_MAX_MEMBER - 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 bayer_crop_offset_x;
+ u32 bayer_crop_offset_y;
+ u32 bayer_crop_width;
+ u32 bayer_crop_height;
+ u32 dma_crop_offset_x;
+ u32 dma_crop_offset_y;
+ u32 dma_crop_width;
+ u32 dma_crop_height;
+ u32 user_min_frametime;
+ u32 user_max_frametime;
+ u32 wide_frame_gap;
+ u32 frame_gap;
+ u32 line_gap;
+ u32 reserved[PARAMETER_MAX_MEMBER - 23];
+ u32 err;
+};
+
+struct param_otf_output {
+ u32 cmd;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 bitwidth;
+ u32 order;
+ u32 crop_offset_x;
+ u32 crop_offset_y;
+ u32 reserved[PARAMETER_MAX_MEMBER - 9];
+ 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[PARAMETER_MAX_MEMBER - 12];
+ u32 err;
+};
+
+struct param_global_shotmode {
+ u32 cmd;
+ u32 skip_frames;
+ u32 reserved[PARAMETER_MAX_MEMBER - 3];
+ u32 err;
+};
+
+struct param_sensor_framerate {
+ u32 frame_rate;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_isp_aa {
+ u32 cmd;
+ u32 target;
+ u32 mode;
+ u32 scene;
+ u32 af_touch;
+ u32 af_face;
+ u32 af_response;
+ u32 sleep;
+ u32 touch_x;
+ u32 touch_y;
+ u32 manual_af_setting;
+ /* 0: Legacy, 1: Camera 2.0 */
+ u32 cam_api2p0;
+ /*
+ * For android.control.afRegions in Camera 2.0,
+ * Resolution based on YUV output size
+ */
+ u32 af_region_left;
+ u32 af_region_top;
+ u32 af_region_right;
+ u32 af_region_bottom;
+ u32 reserved[PARAMETER_MAX_MEMBER - 17];
+ u32 err;
+};
+
+struct param_isp_flash {
+ u32 cmd;
+ u32 redeye;
+ u32 flash_intensity;
+ u32 reserved[PARAMETER_MAX_MEMBER - 4];
+ u32 err;
+};
+
+struct param_isp_awb {
+ u32 cmd;
+ u32 illumination;
+ u32 reserved[PARAMETER_MAX_MEMBER - 3];
+ u32 err;
+};
+
+struct param_isp_imageeffect {
+ u32 cmd;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_isp_iso {
+ u32 cmd;
+ u32 value;
+ u32 reserved[PARAMETER_MAX_MEMBER - 3];
+ u32 err;
+};
+
+struct param_isp_adjust {
+ u32 cmd;
+ s32 contrast;
+ s32 saturation;
+ s32 sharpness;
+ s32 exposure;
+ s32 brightness;
+ s32 hue;
+ /* 0 or 1 */
+ u32 hotpixel_enable;
+ /* -127 ~ 127 */
+ s32 noise_reduction_strength;
+ /* 0 or 1 */
+ u32 shading_correction_enable;
+ /* 0 or 1 */
+ u32 user_gamma_enable;
+ /* -127 ~ 127 */
+ s32 edge_enhancement_strength;
+ u32 user_scene_mode;
+ u32 min_frametime;
+ u32 max_frametime;
+ u32 reserved[PARAMETER_MAX_MEMBER - 16];
+ u32 err;
+};
+
+struct param_isp_metering {
+ u32 cmd;
+ u32 win_pos_x;
+ u32 win_pos_y;
+ u32 win_width;
+ u32 win_height;
+ u32 exposure_mode;
+ /* 0: Legacy, 1: Camera 2.0 */
+ u32 cam_api2p0;
+ u32 reserved[PARAMETER_MAX_MEMBER - 8];
+ u32 err;
+};
+
+struct param_isp_afc {
+ u32 cmd;
+ u32 manual;
+ u32 reserved[PARAMETER_MAX_MEMBER - 3];
+ u32 err;
+};
+
+struct param_scaler_imageeffect {
+ u32 cmd;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_scaler_input_crop {
+ u32 cmd;
+ u32 pos_x;
+ u32 pos_y;
+ u32 crop_width;
+ u32 crop_height;
+ u32 in_width;
+ u32 in_height;
+ u32 out_width;
+ u32 out_height;
+ u32 reserved[PARAMETER_MAX_MEMBER - 10];
+ u32 err;
+};
+
+struct param_scaler_output_crop {
+ u32 cmd;
+ u32 pos_x;
+ u32 pos_y;
+ u32 crop_width;
+ u32 crop_height;
+ u32 format;
+ u32 reserved[PARAMETER_MAX_MEMBER - 7];
+ u32 err;
+};
+
+struct param_scaler_rotation {
+ u32 cmd;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_scaler_flip {
+ u32 cmd;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_3dnr_1stframe {
+ u32 cmd;
+ u32 reserved[PARAMETER_MAX_MEMBER - 2];
+ u32 err;
+};
+
+struct param_fd_config {
+ u32 cmd;
+ u32 max_number;
+ u32 roll_angle;
+ u32 yaw_angle;
+ s32 smile_mode;
+ s32 blink_mode;
+ u32 eye_detect;
+ u32 mouth_detect;
+ u32 orientation;
+ u32 orientation_value;
+ u32 reserved[PARAMETER_MAX_MEMBER - 11];
+ u32 err;
+};
+
+struct global_param {
+ struct param_global_shotmode shotmode;
+};
+
+struct sensor_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+ struct param_sensor_framerate frame_rate;
+ struct param_dma_output dma_output;
+};
+
+struct buffer_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+};
+
+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;
+};
+
+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;
+};
+
+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;
+};
+
+struct odc_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+};
+
+struct dis_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+};
+
+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;
+};
+
+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;
+};
+
+struct fd_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_dma_input dma_input;
+ struct param_fd_config config;
+};
+
+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;
+};
+
+#define NUMBER_OF_GAMMA_CURVE_POINTS 32
+
+struct is_sensor_tune {
+ u32 exposure;
+ u32 analog_gain;
+ u32 frame_rate;
+ u32 actuator_pos;
+};
+
+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_isp_tune {
+ /* 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;
+};
+
+struct is_tune_region {
+ struct is_sensor_tune sensor_tune;
+ struct is_isp_tune isp_tune;
+};
+
+struct rational_t {
+ u32 num;
+ u32 den;
+};
+
+struct srational_t {
+ 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
+
+struct exif_attribute {
+ struct rational_t exposure_time;
+ struct srational_t shutter_speed;
+ u32 iso_speed_rating;
+ u32 flash;
+ struct srational_t brightness;
+};
+
+struct is_frame_header {
+ u32 valid;
+ u32 bad_mark;
+ u32 captured;
+ u32 frame_number;
+ struct exif_attribute exif;
+};
+
+struct is_face_marker {
+ u32 frame_number;
+ struct v4l2_rect face;
+ struct v4l2_rect left_eye;
+ struct v4l2_rect right_eye;
+ struct v4l2_rect mouth;
+ u32 roll_angle;
+ u32 yaw_angle;
+ u32 confidence;
+ u32 stracked;
+ u32 tracked_faceid;
+ u32 smile_level;
+ u32 blink_level;
+};
+
+#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];
+};
+
+struct is_time_measure_us {
+ u32 min_time_us;
+ u32 max_time_us;
+ u32 avrg_time_us;
+ u32 current_time_us;
+};
+
+struct is_debug_frame_descriptor {
+ u32 sensor_frame_time;
+ u32 sensor_exposure_time;
+ u32 sensor_analog_gain;
+ u32 req_lei;
+};
+
+#define MAX_FRAMEDESCRIPTOR_CONTEXT_NUM (30 * 20) /* 600 frame */
+#define MAX_VERSION_DISPLAY_BUF (32)
+
+struct is_share_region {
+ u32 frame_time;
+ u32 exposure_time;
+ u32 analog_gain;
+
+ u32 r_gain;
+ u32 g_gain;
+ u32 b_gain;
+
+ u32 af_position;
+ u32 af_status;
+ 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 ispfw_version_no[MAX_VERSION_DISPLAY_BUF];
+ u8 ispfw_version_date[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_version_no[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_revsion_no[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_version_date[MAX_VERSION_DISPLAY_BUF];
+
+ /* measure timing */
+ struct is_time_measure_us isp_sdk_time;
+};
+
+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 */
+ u32 pd_ready_flag; /* 0:Normal, 1:EnterIdle(Ready to power down) */
+ u32 isp_frame_err; /* Frame Error Counters */
+ u32 drc_frame_err;
+ u32 scc_frame_err;
+ u32 odc_frame_err;
+ u32 dis_frame_err;
+ u32 tdnr_frame_err;
+ u32 scp_frame_err;
+ u32 fd_frame_err;
+ u32 isp_frame_drop; /* Frame Drop Counters */
+ u32 drc_frame_drop;
+ u32 dis_frame_drop;
+ u32 uifdframedrop;
+};
+
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c
new file mode 100644
index 00000000000..25eaf2433f5
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c
@@ -0,0 +1,1699 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@samsung.com>
+ * Kil-yeon Lim <kilyeon.im@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.h"
+#include "fimc-is-pipeline.h"
+#include "fimc-is-metadata.h"
+#include "fimc-is-regs.h"
+#include "fimc-is-cmd.h"
+#include <media/videobuf2-dma-contig.h>
+#include <linux/delay.h>
+
+/* Default setting values */
+#define DEFAULT_PREVIEW_STILL_WIDTH 1280
+#define DEFAULT_PREVIEW_STILL_HEIGHT 720
+#define DEFAULT_CAPTURE_VIDEO_WIDTH 1920
+#define DEFAULT_CAPTURE_VIDEO_HEIGHT 1080
+#define DEFAULT_CAPTURE_STILL_WIDTH 2560
+#define DEFAULT_CAPTURE_STILL_HEIGHT 1920
+#define DEFAULT_CAPTURE_STILL_CROP_WIDTH 2560
+#define DEFAULT_CAPTURE_STILL_CROP_HEIGHT 1440
+#define DEFAULT_PREVIEW_VIDEO_WIDTH 640
+#define DEFAULT_PREVIEW_VIDEO_HEIGHT 480
+
+/* Init params for pipeline devices */
+static const struct sensor_param init_sensor_param = {
+ .frame_rate = {
+ .frame_rate = 30,
+ },
+};
+
+static const struct isp_param init_isp_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_DISABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = OTF_INPUT_FORMAT_BAYER,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_10BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ .frametime_max = 33333,
+ },
+ .dma1_input = {
+ .cmd = DMA_INPUT_COMMAND_DISABLE,
+ },
+ .dma2_input = {
+ .cmd = DMA_INPUT_COMMAND_DISABLE,
+ },
+ .aa = {
+ .cmd = ISP_AA_COMMAND_START,
+ },
+ .flash = {
+ .cmd = ISP_FLASH_COMMAND_DISABLE,
+ .redeye = ISP_FLASH_REDEYE_DISABLE,
+ },
+ .awb = {
+ .cmd = ISP_AWB_COMMAND_AUTO,
+ },
+ .effect = {
+ .cmd = ISP_IMAGE_EFFECT_DISABLE,
+ },
+ .iso = {
+ .cmd = ISP_ISO_COMMAND_AUTO,
+ },
+ .adjust = {
+ .cmd = ISP_ADJUST_COMMAND_AUTO,
+ },
+ .metering = {
+ .cmd = ISP_METERING_COMMAND_CENTER,
+ .win_width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .win_height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ },
+ .afc = {
+ .cmd = ISP_AFC_COMMAND_AUTO,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV444,
+ .bitwidth = OTF_OUTPUT_BIT_WIDTH_12BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma1_output = {
+ .cmd = DMA_OUTPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = DMA_INPUT_FORMAT_YUV444,
+ .bitwidth = DMA_INPUT_BIT_WIDTH_8BIT,
+ .plane = 1,
+ .order = DMA_INPUT_ORDER_YCBCR,
+ },
+ .dma2_output = {
+ .cmd = DMA_OUTPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = DMA_OUTPUT_FORMAT_BAYER,
+ .bitwidth = DMA_OUTPUT_BIT_WIDTH_12BIT,
+ .plane = 1,
+ .order = DMA_OUTPUT_ORDER_GB_BG,
+ .dma_out_mask = 0xffffffff,
+ },
+};
+
+static const struct drc_param init_drc_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_12BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma_input = {
+ .cmd = DMA_INPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = DMA_INPUT_FORMAT_YUV444,
+ .bitwidth = DMA_INPUT_BIT_WIDTH_8BIT,
+ .plane = 1,
+ .order = DMA_INPUT_ORDER_YCBCR,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+};
+
+static const struct scalerc_param init_scalerc_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_12BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .input_crop = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .crop_width = DEFAULT_CAPTURE_STILL_CROP_WIDTH,
+ .crop_height = DEFAULT_CAPTURE_STILL_CROP_HEIGHT,
+ .in_width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .in_height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .out_width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .out_height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ },
+ .output_crop = {
+ .cmd = SCALER_CROP_COMMAND_DISABLE,
+ .crop_width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .crop_height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = DMA_OUTPUT_FORMAT_YUV420,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV444,
+ .bitwidth = OTF_OUTPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma_output = {
+ .cmd = DMA_OUTPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_STILL_WIDTH,
+ .height = DEFAULT_CAPTURE_STILL_HEIGHT,
+ .format = DMA_OUTPUT_FORMAT_YUV420,
+ .bitwidth = DMA_OUTPUT_BIT_WIDTH_8BIT,
+ .plane = 3,
+ .order = DMA_OUTPUT_ORDER_NONE,
+ .dma_out_mask = 0xffff,
+ },
+};
+
+static const struct odc_param init_odc_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ .err = CONTROL_ERROR_NONE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV422,
+ .bitwidth = OTF_OUTPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+};
+
+static const struct dis_param init_dis_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV422,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV422,
+ .bitwidth = OTF_OUTPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+};
+static const struct tdnr_param init_tdnr_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV422,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma_output = {
+ .cmd = DMA_OUTPUT_COMMAND_DISABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = DMA_OUTPUT_FORMAT_YUV420,
+ .bitwidth = DMA_OUTPUT_BIT_WIDTH_8BIT,
+ .plane = 2,
+ .order = DMA_OUTPUT_ORDER_CBCR,
+ .dma_out_mask = 0xffff,
+ },
+};
+
+static const struct scalerp_param init_scalerp_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_START,
+ .bypass = CONTROL_BYPASS_ENABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .input_crop = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .crop_width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .crop_height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .in_width = DEFAULT_CAPTURE_VIDEO_WIDTH,
+ .in_height = DEFAULT_CAPTURE_VIDEO_HEIGHT,
+ .out_width = DEFAULT_PREVIEW_STILL_WIDTH,
+ .out_height = DEFAULT_PREVIEW_STILL_HEIGHT,
+ },
+ .output_crop = {
+ .cmd = SCALER_CROP_COMMAND_DISABLE,
+ .crop_width = DEFAULT_PREVIEW_STILL_WIDTH,
+ .crop_height = DEFAULT_PREVIEW_STILL_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV420,
+ },
+ .otf_output = {
+ .cmd = OTF_OUTPUT_COMMAND_ENABLE,
+ .width = DEFAULT_PREVIEW_STILL_WIDTH,
+ .height = DEFAULT_PREVIEW_STILL_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_OUTPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma_output = {
+ .cmd = DMA_OUTPUT_COMMAND_DISABLE,
+ .width = DEFAULT_PREVIEW_STILL_WIDTH,
+ .height = DEFAULT_PREVIEW_STILL_HEIGHT,
+ .format = OTF_OUTPUT_FORMAT_YUV420,
+ .bitwidth = DMA_OUTPUT_BIT_WIDTH_8BIT,
+ .plane = 3,
+ .order = DMA_OUTPUT_ORDER_NONE,
+ .dma_out_mask = 0xffff,
+ },
+};
+
+static const struct fd_param init_fd_param = {
+ .control = {
+ .cmd = CONTROL_COMMAND_STOP,
+ .bypass = CONTROL_BYPASS_DISABLE,
+ },
+ .otf_input = {
+ .cmd = OTF_INPUT_COMMAND_ENABLE,
+ .width = DEFAULT_PREVIEW_STILL_WIDTH,
+ .height = DEFAULT_PREVIEW_STILL_HEIGHT,
+ .format = OTF_INPUT_FORMAT_YUV444,
+ .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT,
+ .order = OTF_INPUT_ORDER_BAYER_GR_BG,
+ },
+ .dma_input = {
+ .cmd = DMA_INPUT_COMMAND_DISABLE,
+ },
+ .config = {
+ .cmd = FD_CONFIG_COMMAND_MAXIMUM_NUMBER |
+ FD_CONFIG_COMMAND_ROLL_ANGLE |
+ FD_CONFIG_COMMAND_YAW_ANGLE |
+ FD_CONFIG_COMMAND_SMILE_MODE |
+ FD_CONFIG_COMMAND_BLINK_MODE |
+ FD_CONFIG_COMMAND_EYES_DETECT |
+ FD_CONFIG_COMMAND_MOUTH_DETECT |
+ FD_CONFIG_COMMAND_ORIENTATION |
+ FD_CONFIG_COMMAND_ORIENTATION_VALUE,
+ .max_number = CAMERA2_MAX_FACES,
+ .roll_angle = FD_CONFIG_ROLL_ANGLE_FULL,
+ .yaw_angle = FD_CONFIG_YAW_ANGLE_45_90,
+ .smile_mode = FD_CONFIG_SMILE_MODE_DISABLE,
+ .blink_mode = FD_CONFIG_BLINK_MODE_DISABLE,
+ .eye_detect = FD_CONFIG_EYES_DETECT_ENABLE,
+ .mouth_detect = FD_CONFIG_MOUTH_DETECT_DISABLE,
+ .orientation = FD_CONFIG_ORIENTATION_DISABLE,
+ },
+};
+
+static int fimc_is_pipeline_create_subdevs(struct fimc_is_pipeline *pipeline)
+{
+ struct fimc_is *is = pipeline->is;
+ int ret;
+
+ /* ISP */
+ ret = fimc_is_isp_subdev_create(&pipeline->isp,
+ is->alloc_ctx, pipeline);
+ if (ret)
+ return ret;
+
+ /* SCC scaler */
+ ret = fimc_is_scaler_subdev_create(&pipeline->scaler[SCALER_SCC],
+ SCALER_SCC, is->alloc_ctx, pipeline);
+ if (ret)
+ return ret;
+
+ /* SCP scaler */
+ ret = fimc_is_scaler_subdev_create(&pipeline->scaler[SCALER_SCP],
+ SCALER_SCP, is->alloc_ctx, pipeline);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int fimc_is_pipeline_unregister_subdevs(struct fimc_is_pipeline *p)
+{
+ fimc_is_isp_subdev_destroy(&p->isp);
+ fimc_is_scaler_subdev_destroy(&p->scaler[SCALER_SCC]);
+ fimc_is_scaler_subdev_destroy(&p->scaler[SCALER_SCP]);
+
+ return 0;
+}
+
+int fimc_is_pipeline_init(struct fimc_is_pipeline *pipeline,
+ unsigned int instance, void *is_ctx)
+{
+ struct fimc_is *is = is_ctx;
+ unsigned int i;
+ int ret;
+
+ if (test_bit(PIPELINE_INIT, &pipeline->state))
+ return -EINVAL;
+
+ /* Initialize context variables */
+ pipeline->instance = instance;
+ pipeline->is = is;
+ pipeline->dev = &is->pdev->dev;
+ pipeline->minfo = &is->minfo;
+ pipeline->state = 0;
+ pipeline->force_down = false;
+ for (i = 0; i < FIMC_IS_NUM_COMPS; i++)
+ pipeline->comp_state[i] = 0;
+
+ spin_lock_init(&pipeline->slock_buf);
+ init_waitqueue_head(&pipeline->wait_q);
+ mutex_init(&pipeline->pipe_lock);
+ mutex_init(&pipeline->pipe_scl_lock);
+
+ ret = fimc_is_pipeline_create_subdevs(pipeline);
+ if (ret) {
+ dev_err(pipeline->dev, "Subdev creation failed\n");
+ return -EINVAL;
+ }
+
+ set_bit(PIPELINE_INIT, &pipeline->state);
+ return 0;
+}
+
+int fimc_is_pipeline_destroy(struct fimc_is_pipeline *pipeline)
+{
+ if (!pipeline)
+ return -EINVAL;
+
+ if (!test_bit(PIPELINE_INIT, &pipeline->state))
+ return -EINVAL;
+ return fimc_is_pipeline_unregister_subdevs(pipeline);
+}
+
+static int fimc_is_pipeline_initmem(struct fimc_is_pipeline *pipeline)
+{
+ struct fimc_is_meminfo *minfo = pipeline->minfo;
+ unsigned int offset;
+
+ /* Allocate fw memory */
+ minfo->fw.vaddr = dma_alloc_coherent(pipeline->dev,
+ FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE,
+ &minfo->fw.paddr, GFP_KERNEL);
+ if (minfo->fw.vaddr == NULL)
+ return -ENOMEM;
+ minfo->fw.size = FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE;
+
+ /* FW memory should be 64MB aligned */
+ if ((u32)minfo->fw.paddr & FIMC_IS_FW_BASE_MASK) {
+ dev_err(pipeline->dev, "FW memory not 64MB aligned\n");
+ dma_free_coherent(pipeline->dev, minfo->fw.size,
+ minfo->fw.vaddr,
+ minfo->fw.paddr);
+ return -EIO;
+ }
+
+ /* Assigning memory regions */
+ offset = FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE;
+ minfo->region.paddr = minfo->fw.paddr + offset;
+ minfo->region.vaddr = minfo->fw.vaddr + offset;
+ pipeline->is_region = (struct is_region *)minfo->region.vaddr;
+
+ minfo->shared.paddr = minfo->fw.paddr +
+ (unsigned int)((void *)&pipeline->is_region->shared[0] -
+ minfo->fw.vaddr);
+ minfo->shared.vaddr = minfo->fw.vaddr +
+ (unsigned int)((void *)&pipeline->is_region->shared[0] -
+ minfo->fw.vaddr);
+
+ /* Allocate shot buffer */
+ minfo->shot.vaddr = dma_alloc_coherent(pipeline->dev,
+ sizeof(struct camera2_shot),
+ &minfo->shot.paddr, GFP_KERNEL);
+ if (minfo->shot.vaddr == NULL) {
+ dma_free_coherent(pipeline->dev, minfo->fw.size,
+ minfo->fw.vaddr,
+ minfo->fw.paddr);
+ return -ENOMEM;
+ }
+ minfo->shot.size = sizeof(struct camera2_shot);
+
+ return 0;
+}
+
+static void fimc_is_pipeline_freemem(struct fimc_is_pipeline *pipeline)
+{
+ struct fimc_is_meminfo *minfo = pipeline->minfo;
+ if (minfo->fw.vaddr)
+ dma_free_coherent(pipeline->dev, minfo->fw.size,
+ minfo->fw.vaddr,
+ minfo->fw.paddr);
+ if (minfo->shot.vaddr)
+ dma_free_coherent(pipeline->dev, minfo->shot.size,
+ minfo->shot.vaddr,
+ minfo->shot.paddr);
+}
+
+static int fimc_is_pipeline_load_firmware(struct fimc_is_pipeline *pipeline)
+{
+ const struct firmware *fw_blob;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+
+ ret = request_firmware(&fw_blob, is->drvdata->fw_name, &is->pdev->dev);
+ if (ret) {
+ dev_err(pipeline->dev, "Firmware file not found\n");
+ return -EINVAL;
+ }
+ if (fw_blob->size > FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE) {
+ dev_err(pipeline->dev, "Firmware file too big\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+
+ memcpy(pipeline->minfo->fw.vaddr, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+
+ return 0;
+}
+
+static void fimc_is_pipeline_forcedown(struct fimc_is_pipeline *pipeline,
+ bool on)
+{
+ struct fimc_is *is = pipeline->is;
+ if (on) {
+ pmu_is_write(0x0, is, PMUREG_ISP_ARM_OPTION);
+ pmu_is_write(0x1cf82000, is, PMUREG_ISP_LOW_POWER_OFF);
+ pipeline->force_down = true;
+ } else {
+ pmu_is_write(0xffffffff, is, PMUREG_ISP_ARM_OPTION);
+ pmu_is_write(0x8, is, PMUREG_ISP_LOW_POWER_OFF);
+ pipeline->force_down = false;
+ }
+}
+
+static int fimc_is_pipeline_wait_timeout(struct fimc_is_pipeline *pipeline,
+ int on)
+{
+ struct fimc_is *is = pipeline->is;
+ u32 timeout = FIMC_IS_A5_TIMEOUT;
+
+ while ((pmu_is_read(is, PMUREG_ISP_ARM_STATUS) & 0x1) != on) {
+ if (timeout == 0)
+ return -ETIME;
+ timeout--;
+ udelay(1);
+ }
+ return 0;
+}
+
+static int fimc_is_pipeline_power(struct fimc_is_pipeline *pipeline, int on)
+{
+ int ret = 0;
+ struct fimc_is *is = pipeline->is;
+ struct device *dev = &is->pdev->dev;
+
+ if (on) {
+ /* force poweroff setting */
+ if (pipeline->force_down)
+ fimc_is_pipeline_forcedown(pipeline, false);
+
+ /* FIMC-IS local power enable */
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ return ret;
+
+ /* A5 start address setting */
+ writel(pipeline->minfo->fw.paddr, is->interface.regs + BBOAR);
+
+ /* A5 power on*/
+ pmu_is_write(0x1, is, PMUREG_ISP_ARM_CONFIGURATION);
+
+ /* enable A5 */
+ pmu_is_write(0x00018000, is, PMUREG_ISP_ARM_OPTION);
+ ret = fimc_is_pipeline_wait_timeout(pipeline, on);
+ if (ret)
+ dev_err(pipeline->dev, "A5 power on failed\n");
+ } else {
+ /* disable A5 */
+ pmu_is_write(0x10000, is, PMUREG_ISP_ARM_OPTION);
+
+ /* A5 power off*/
+ pmu_is_write(0x0, is, PMUREG_ISP_ARM_CONFIGURATION);
+
+ /* Check A5 power off status register */
+ ret = fimc_is_pipeline_wait_timeout(pipeline, on);
+ if (ret) {
+ dev_err(pipeline->dev, "A5 power off failed\n");
+ fimc_is_pipeline_forcedown(pipeline, true);
+ }
+
+ pmu_is_write(0x0, is, PMUREG_CMU_RESET_ISP_SYS_PWR_REG);
+
+ /* FIMC-IS local power down */
+ pm_runtime_put_sync(dev);
+ }
+
+ return ret;
+}
+
+static int fimc_is_pipeline_load_setfile(struct fimc_is_pipeline *pipeline,
+ unsigned int setfile_addr,
+ unsigned char *setfile_name)
+{
+ struct fimc_is *is = pipeline->is;
+ const struct firmware *fw_blob;
+ int ret;
+
+ ret = request_firmware(&fw_blob, setfile_name, &is->pdev->dev);
+ if (ret) {
+ dev_err(pipeline->dev, "Setfile %s not found\n", setfile_name);
+ return ret;
+ }
+
+ memcpy(pipeline->minfo->fw.vaddr + setfile_addr,
+ fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_setfile(struct fimc_is_pipeline *pipeline)
+{
+ struct fimc_is *is = pipeline->is;
+ struct fimc_is_sensor *sensor = pipeline->sensor;
+ int ret;
+ unsigned int setfile_addr;
+
+ /* Get setfile addr from HW */
+ ret = fimc_is_itf_get_setfile_addr(&is->interface,
+ pipeline->instance, &setfile_addr);
+ if (ret) {
+ dev_err(pipeline->dev, "Get setfile addr failed\n");
+ return ret;
+ }
+
+ if (!sensor->drvdata->setfile_name)
+ return -EINVAL;
+
+ /* Load setfile */
+ ret = fimc_is_pipeline_load_setfile(pipeline, setfile_addr,
+ sensor->drvdata->setfile_name);
+ if (ret) {
+ dev_err(pipeline->dev, "Load setfile failed\n");
+ return ret;
+ }
+
+ /* Send HW command */
+ ret = fimc_is_itf_load_setfile(&is->interface, pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "HW Load setfile failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fimc_is_pipeline_isp_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct isp_param *isp_param = &pipeline->is_region->parameter.isp;
+ struct fimc_is *is = pipeline->is;
+ unsigned long index[2] = {0};
+ unsigned int sensor_width, sensor_height, scc_width, scc_height;
+ unsigned int crop_x, crop_y, isp_width, isp_height;
+ unsigned int sensor_ratio, output_ratio;
+ int ret;
+
+ /* Crop calculation */
+ sensor_width = pipeline->sensor->width;
+ sensor_height = pipeline->sensor->height;
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+ isp_width = sensor_width;
+ isp_height = sensor_height;
+ crop_x = crop_y = 0;
+
+ sensor_ratio = sensor_width * 1000 / sensor_height;
+ output_ratio = scc_width * 1000 / scc_height;
+
+ if (sensor_ratio == output_ratio) {
+ isp_width = sensor_width;
+ isp_height = sensor_height;
+ } else if (sensor_ratio < output_ratio) {
+ isp_height = (sensor_width * scc_height) / scc_width;
+ isp_height = ALIGN(isp_height, 2);
+ crop_y = ((sensor_height - isp_height) >> 1) & ~1U;
+ } else {
+ isp_width = (sensor_height * scc_width) / scc_height;
+ isp_width = ALIGN(isp_width, 4);
+ crop_x = ((sensor_width - isp_width) >> 1) & ~1U;
+ }
+ pipeline->isp_width = isp_width;
+ pipeline->isp_height = isp_height;
+
+ isp_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
+ isp_param->otf_output.width = pipeline->sensor->width;
+ isp_param->otf_output.height = pipeline->sensor->height;
+ isp_param->otf_output.format = OTF_OUTPUT_FORMAT_YUV444;
+ isp_param->otf_output.bitwidth = OTF_OUTPUT_BIT_WIDTH_12BIT;
+ isp_param->otf_output.order = OTF_INPUT_ORDER_BAYER_GR_BG;
+ __set_bit(PARAM_ISP_OTF_OUTPUT, index);
+
+ isp_param->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ __set_bit(PARAM_ISP_DMA1_OUTPUT, index);
+
+ isp_param->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ __set_bit(PARAM_ISP_DMA2_OUTPUT, index);
+
+ if (enable)
+ isp_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ isp_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ isp_param->control.cmd = CONTROL_COMMAND_START;
+ isp_param->control.run_mode = 1;
+ __set_bit(PARAM_ISP_CONTROL, index);
+
+ isp_param->dma1_input.cmd = DMA_INPUT_COMMAND_BUF_MNGR;
+ isp_param->dma1_input.width = sensor_width;
+ isp_param->dma1_input.height = sensor_height;
+ isp_param->dma1_input.dma_crop_offset_x = crop_x;
+ isp_param->dma1_input.dma_crop_offset_y = crop_y;
+ isp_param->dma1_input.dma_crop_width = isp_width;
+ isp_param->dma1_input.dma_crop_height = isp_height;
+ isp_param->dma1_input.bayer_crop_offset_x = 0;
+ isp_param->dma1_input.bayer_crop_offset_y = 0;
+ isp_param->dma1_input.bayer_crop_width = 0;
+ isp_param->dma1_input.bayer_crop_height = 0;
+ isp_param->dma1_input.user_min_frametime = 0;
+ isp_param->dma1_input.user_max_frametime = 66666;
+ isp_param->dma1_input.wide_frame_gap = 1;
+ isp_param->dma1_input.frame_gap = 4096;
+ isp_param->dma1_input.line_gap = 45;
+ isp_param->dma1_input.order = DMA_INPUT_ORDER_GR_BG;
+ isp_param->dma1_input.plane = 1;
+ isp_param->dma1_input.buffer_number = 1;
+ isp_param->dma1_input.buffer_address = 0;
+ isp_param->dma1_input.reserved[1] = 0;
+ isp_param->dma1_input.reserved[2] = 0;
+ if (pipeline->isp.fmt->fourcc == V4L2_PIX_FMT_SGRBG8)
+ isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_8BIT;
+ else if (pipeline->isp.fmt->fourcc == V4L2_PIX_FMT_SGRBG10)
+ isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_10BIT;
+ else
+ isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_12BIT;
+ __set_bit(PARAM_ISP_DMA1_INPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_ISP]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_drc_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct drc_param *drc_param = &pipeline->is_region->parameter.drc;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+
+ if (enable)
+ drc_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ drc_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_DRC_CONTROL, index);
+
+ drc_param->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
+ drc_param->otf_input.width = pipeline->isp_width;
+ drc_param->otf_input.height = pipeline->isp_height;
+ __set_bit(PARAM_DRC_OTF_INPUT, index);
+
+ drc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
+ drc_param->otf_output.width = pipeline->isp_width;
+ drc_param->otf_output.height = pipeline->isp_height;
+ __set_bit(PARAM_DRC_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ if (enable)
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_DRC]);
+ else
+ clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_DRC]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_scc_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct scalerc_param *scc_param =
+ &pipeline->is_region->parameter.scalerc;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+ unsigned int scc_width, scc_height;
+
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+
+ if (enable)
+ scc_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ scc_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_SCALERC_CONTROL, index);
+
+ scc_param->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
+ scc_param->otf_input.width = pipeline->isp_width;
+ scc_param->otf_input.height = pipeline->isp_height;
+ __set_bit(PARAM_SCALERC_OTF_INPUT, index);
+
+ /* SCC OUTPUT */
+ scc_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE;
+ scc_param->input_crop.pos_x = 0;
+ scc_param->input_crop.pos_y = 0;
+ scc_param->input_crop.crop_width = pipeline->isp_width;
+ scc_param->input_crop.crop_height = pipeline->isp_height;
+ scc_param->input_crop.in_width = pipeline->isp_width;
+ scc_param->input_crop.in_height = pipeline->isp_height;
+ scc_param->input_crop.out_width = scc_width;
+ scc_param->input_crop.out_height = scc_height;
+ __set_bit(PARAM_SCALERC_INPUT_CROP, index);
+
+ scc_param->output_crop.cmd = SCALER_CROP_COMMAND_DISABLE;
+ scc_param->output_crop.pos_x = 0;
+ scc_param->output_crop.pos_y = 0;
+ scc_param->output_crop.crop_width = scc_width;
+ scc_param->output_crop.crop_height = scc_height;
+ __set_bit(PARAM_SCALERC_OUTPUT_CROP, index);
+
+ scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
+ scc_param->otf_output.width = scc_width;
+ scc_param->otf_output.height = scc_height;
+ __set_bit(PARAM_SCALERC_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_SCC]);
+ return 0;
+}
+
+static int fimc_is_pipeline_odc_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct odc_param *odc_param = &pipeline->is_region->parameter.odc;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+ unsigned int scc_width, scc_height;
+
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+
+ if (enable)
+ odc_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ odc_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_ODC_CONTROL, index);
+
+ odc_param->otf_input.width = scc_width;
+ odc_param->otf_input.height = scc_height;
+ __set_bit(PARAM_ODC_OTF_INPUT, index);
+
+ odc_param->otf_output.width = scc_width;
+ odc_param->otf_output.height = scc_height;
+ __set_bit(PARAM_ODC_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ if (enable)
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_ODC]);
+ else
+ clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_ODC]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_dis_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct dis_param *dis_param = &pipeline->is_region->parameter.dis;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+ unsigned int scc_width, scc_height;
+
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+
+ if (enable)
+ dis_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ dis_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_DIS_CONTROL, index);
+
+ /* DIS INPUT */
+ dis_param->otf_input.width = scc_width;
+ dis_param->otf_input.height = scc_height;
+ __set_bit(PARAM_DIS_OTF_INPUT, index);
+
+ /* DIS OUTPUT */
+ dis_param->otf_output.width = scc_width;
+ dis_param->otf_output.height = scc_height;
+ __set_bit(PARAM_DIS_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ if (enable)
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_DIS]);
+ else
+ clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_DIS]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_3dnr_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct tdnr_param *tdnr_param = &pipeline->is_region->parameter.tdnr;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+ unsigned int scc_width, scc_height;
+
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+
+ if (enable)
+ tdnr_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ tdnr_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_TDNR_CONTROL, index);
+
+ tdnr_param->otf_input.width = scc_width;
+ tdnr_param->otf_input.height = scc_height;
+ __set_bit(PARAM_TDNR_OTF_INPUT, index);
+
+ tdnr_param->dma_output.width = scc_width;
+ tdnr_param->dma_output.height = scc_height;
+ __set_bit(PARAM_TDNR_DMA_OUTPUT, index);
+
+ tdnr_param->otf_output.width = scc_width;
+ tdnr_param->otf_output.height = scc_height;
+ __set_bit(PARAM_TDNR_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ if (enable)
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_TDNR]);
+ else
+ clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_TDNR]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_scp_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct scalerp_param *scp_param =
+ &pipeline->is_region->parameter.scalerp;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+ unsigned int scc_width, scc_height;
+ unsigned int scp_width, scp_height;
+
+ scc_width = pipeline->scaler[SCALER_SCC].width;
+ scc_height = pipeline->scaler[SCALER_SCC].height;
+ scp_width = pipeline->scaler[SCALER_SCP].width;
+ scp_height = pipeline->scaler[SCALER_SCP].height;
+
+ if (enable)
+ scp_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ scp_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_SCALERP_CONTROL, index);
+
+ /* SCP Input */
+ scp_param->otf_input.width = scc_width;
+ scp_param->otf_input.height = scc_height;
+ __set_bit(PARAM_SCALERP_OTF_INPUT, index);
+
+ /* SCP Output */
+ scp_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE;
+ scp_param->input_crop.pos_x = 0;
+ scp_param->input_crop.pos_y = 0;
+ scp_param->input_crop.crop_width = scc_width;
+ scp_param->input_crop.crop_height = scc_height;
+ scp_param->input_crop.in_width = scc_width;
+ scp_param->input_crop.in_height = scc_height;
+ scp_param->input_crop.out_width = scp_width;
+ scp_param->input_crop.out_height = scp_height;
+ __set_bit(PARAM_SCALERP_INPUT_CROP, index);
+
+ scp_param->output_crop.cmd = SCALER_CROP_COMMAND_DISABLE;
+ __set_bit(PARAM_SCALERP_OUTPUT_CROP, index);
+
+ scp_param->otf_output.width = scp_width;
+ scp_param->otf_output.height = scp_height;
+ __set_bit(PARAM_SCALERP_OTF_OUTPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_SCP]);
+
+ return 0;
+}
+
+static int fimc_is_pipeline_fd_setparams(struct fimc_is_pipeline *pipeline,
+ unsigned int enable)
+{
+ struct fd_param *fd_param = &pipeline->is_region->parameter.fd;
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned long index[2] = {0};
+
+ if (enable)
+ fd_param->control.bypass = CONTROL_BYPASS_DISABLE;
+ else
+ fd_param->control.bypass = CONTROL_BYPASS_ENABLE;
+ __set_bit(PARAM_FD_CONTROL, index);
+
+ fd_param->otf_input.width = pipeline->scaler[SCALER_SCP].width;
+ fd_param->otf_input.height = pipeline->scaler[SCALER_SCP].height;
+ __set_bit(PARAM_FD_OTF_INPUT, index);
+
+ wmb();
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ if (enable)
+ set_bit(COMP_ENABLE, &pipeline->comp_state[IS_FD]);
+ else
+ clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_FD]);
+
+ return 0;
+}
+
+void fimc_is_pipeline_buf_lock(struct fimc_is_pipeline *pipeline)
+{
+ spin_lock_irqsave(&pipeline->slock_buf, pipeline->slock_flags);
+}
+
+void fimc_is_pipeline_buf_unlock(struct fimc_is_pipeline *pipeline)
+{
+ spin_unlock_irqrestore(&pipeline->slock_buf, pipeline->slock_flags);
+}
+
+int fimc_is_pipeline_setparams(struct fimc_is_pipeline *pipeline)
+{
+ int ret;
+
+ /* Enabling basic components in pipeline */
+ ret = fimc_is_pipeline_isp_setparams(pipeline, true);
+ if (!ret)
+ ret = fimc_is_pipeline_drc_setparams(pipeline, false);
+ if (!ret)
+ ret = fimc_is_pipeline_scc_setparams(pipeline, true);
+ if (!ret)
+ ret = fimc_is_pipeline_odc_setparams(pipeline, false);
+ if (!ret)
+ ret = fimc_is_pipeline_dis_setparams(pipeline, false);
+ if (!ret)
+ ret = fimc_is_pipeline_3dnr_setparams(pipeline, false);
+ if (!ret)
+ ret = fimc_is_pipeline_scp_setparams(pipeline, true);
+ if (!ret)
+ ret = fimc_is_pipeline_fd_setparams(pipeline, false);
+ if (ret)
+ dev_err(pipeline->dev, "Pipeline setparam failed\n");
+
+ return ret;
+}
+
+int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline,
+ enum fimc_is_scaler_id scaler_id,
+ unsigned int num_bufs,
+ unsigned int num_planes)
+{
+ struct fimc_is *is = pipeline->is;
+ struct scalerp_param *scp_param =
+ &pipeline->is_region->parameter.scalerp;
+ struct scalerc_param *scc_param =
+ &pipeline->is_region->parameter.scalerc;
+ struct param_dma_output *dma_output;
+ const struct fimc_is_fmt *fmt;
+ unsigned int region_index;
+ unsigned long *comp_state;
+ int ret;
+ unsigned int pipe_start_flag = 0;
+ unsigned int i, buf_mask = 0;
+ unsigned long index[2] = {0};
+ enum fimc_is_scaler_id id;
+
+ if (!test_bit(PIPELINE_OPEN, &pipeline->state)) {
+ dev_err(pipeline->dev, "Pipeline not opened.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pipeline->pipe_scl_lock);
+
+ if (test_bit(PIPELINE_START, &pipeline->state)) {
+ /*
+ * Pipeline is started.
+ * Stop it now to set params and start again
+ */
+ ret = fimc_is_pipeline_stop(pipeline, 0);
+ if (ret) {
+ dev_err(pipeline->dev, "Not able to stop pipeline\n");
+ goto exit;
+ }
+ pipe_start_flag = 1;
+ }
+
+ if (scaler_id == SCALER_SCC) {
+ dma_output = &scc_param->dma_output;
+ region_index = FIMC_IS_SCC_REGION_INDEX;
+ comp_state = &pipeline->comp_state[IS_SCC];
+ id = SCALER_SCC;
+ __set_bit(PARAM_SCALERC_DMA_OUTPUT, index);
+ } else {
+ dma_output = &scp_param->dma_output;
+ comp_state = &pipeline->comp_state[IS_SCP];
+ region_index = FIMC_IS_SCP_REGION_INDEX;
+ id = SCALER_SCP;
+ __set_bit(PARAM_SCALERP_DMA_OUTPUT, index);
+ }
+
+ for (i = 0; i < num_bufs; i++)
+ buf_mask |= BIT(i);
+
+ fmt = pipeline->scaler[id].fmt;
+ dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE;
+ dma_output->dma_out_mask = buf_mask;
+ dma_output->buffer_number = num_bufs;
+ dma_output->plane = num_planes;
+ dma_output->width = pipeline->scaler[id].width;
+ dma_output->height = pipeline->scaler[id].height;
+ if ((fmt->fourcc == V4L2_PIX_FMT_YUV420M) ||
+ (fmt->fourcc == V4L2_PIX_FMT_NV12M))
+ dma_output->format = DMA_OUTPUT_FORMAT_YUV420;
+ else if (fmt->fourcc == V4L2_PIX_FMT_NV16)
+ dma_output->format = DMA_OUTPUT_FORMAT_YUV422;
+
+ dma_output->buffer_address = pipeline->minfo->shared.paddr +
+ region_index * sizeof(unsigned int);
+ __set_bit(PARAM_SCALERC_DMA_OUTPUT, index);
+
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret)
+ dev_err(pipeline->dev, "fimc_is_itf_set_param is fail\n");
+ else
+ set_bit(COMP_START, comp_state);
+
+ if (pipe_start_flag) {
+ ret = fimc_is_pipeline_start(pipeline, 0);
+ if (ret)
+ dev_err(pipeline->dev,
+ "Not able to start pipeline back\n");
+ }
+exit:
+ mutex_unlock(&pipeline->pipe_scl_lock);
+ return ret;
+}
+
+int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline,
+ enum fimc_is_scaler_id scaler_id)
+{
+ struct fimc_is *is = pipeline->is;
+ struct scalerp_param *scp_param =
+ &pipeline->is_region->parameter.scalerp;
+ struct scalerc_param *scc_param =
+ &pipeline->is_region->parameter.scalerc;
+ struct param_dma_output *dma_output;
+ unsigned int pipe_start_flag = 0;
+ unsigned long index[2] = {0};
+ unsigned long *comp_state;
+ int ret;
+
+ if (!test_bit(PIPELINE_OPEN, &pipeline->state))
+ return -EINVAL;
+
+ mutex_lock(&pipeline->pipe_scl_lock);
+
+ if (test_bit(PIPELINE_START, &pipeline->state)) {
+ /*
+ * Pipeline is started.
+ * Stop it now to set params and start again
+ */
+ ret = fimc_is_pipeline_stop(pipeline, 0);
+ if (ret) {
+ dev_err(pipeline->dev, "Not able to stop pipeline\n");
+ goto exit;
+ }
+ pipe_start_flag = 1;
+ }
+
+ comp_state = (scaler_id == SCALER_SCC) ?
+ &pipeline->comp_state[IS_SCC] : &pipeline->comp_state[IS_SCP];
+
+ if (!test_bit(COMP_START, comp_state)) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (scaler_id == SCALER_SCC) {
+ dma_output = &scc_param->dma_output;
+ __set_bit(PARAM_SCALERC_DMA_OUTPUT, index);
+ } else {
+ dma_output = &scp_param->dma_output;
+ __set_bit(PARAM_SCALERP_DMA_OUTPUT, index);
+ }
+ dma_output->cmd = DMA_OUTPUT_COMMAND_DISABLE;
+
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret)
+ dev_err(pipeline->dev, "fimc_is_itf_set_param is fail\n");
+ else
+ clear_bit(COMP_START, comp_state);
+
+ if (pipe_start_flag) {
+ ret = fimc_is_pipeline_start(pipeline, 0);
+ if (ret)
+ dev_err(pipeline->dev,
+ "Not able to start pipeline back\n");
+ }
+exit:
+ mutex_unlock(&pipeline->pipe_scl_lock);
+ return ret;
+}
+
+void fimc_is_pipeline_config_shot(struct fimc_is_pipeline *pipeline)
+{
+ struct camera2_shot *shot =
+ (struct camera2_shot *)pipeline->minfo->shot.vaddr;
+
+ shot->magicnumber = FIMC_IS_MAGIC_NUMBER;
+
+ shot->ctl.aa.mode = AA_CONTROL_AUTO;
+ shot->ctl.aa.ae_mode = AA_AEMODE_ON;
+}
+
+int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline)
+{
+ int ret;
+ mutex_lock(&pipeline->pipe_lock);
+ ret = fimc_is_pipeline_shot(pipeline);
+ mutex_unlock(&pipeline->pipe_lock);
+ return ret;
+}
+
+int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline)
+{
+ struct fimc_is *is = pipeline->is;
+ int ret;
+ unsigned int rcount, i;
+ struct camera2_shot *shot = pipeline->minfo->shot.vaddr;
+ struct fimc_is_buf *scc_buf, *scp_buf, *bayer_buf;
+
+ if (!test_bit(PIPELINE_START, &pipeline->state))
+ return -EINVAL;
+
+ if (test_bit(PIPELINE_RUN, &pipeline->state))
+ return -EBUSY;
+
+ fimc_is_pipeline_buf_lock(pipeline);
+
+ if (list_empty(&pipeline->isp.wait_queue)) {
+ /* No more bayer buffers */
+ wake_up(&pipeline->wait_q);
+ fimc_is_pipeline_buf_unlock(pipeline);
+ return 0;
+ }
+
+ set_bit(PIPELINE_RUN, &pipeline->state);
+
+ /* Get bayer input buffer */
+ bayer_buf = fimc_is_isp_wait_queue_get(&pipeline->isp);
+ if (!bayer_buf) {
+ fimc_is_pipeline_buf_unlock(pipeline);
+ goto err_exit;
+ }
+ fimc_is_isp_run_queue_add(&pipeline->isp, bayer_buf);
+
+ /* Get SCC buffer */
+ if (test_bit(COMP_START, &pipeline->comp_state[IS_SCC]) &&
+ !list_empty(&pipeline->scaler[SCALER_SCC].wait_queue)) {
+ scc_buf = fimc_is_scaler_wait_queue_get(
+ &pipeline->scaler[SCALER_SCC]);
+ if (scc_buf) {
+ fimc_is_scaler_run_queue_add(
+ &pipeline->scaler[SCALER_SCC],
+ scc_buf);
+ for (i = 0; i < 3; i++)
+ shot->uctl.scaler_ud.scc_target_address[i] =
+ scc_buf->paddr[i];
+ set_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]);
+ }
+ } else {
+ dev_dbg(pipeline->dev, "No SCC buffer available\n");
+ for (i = 0; i < 3; i++)
+ shot->uctl.scaler_ud.scc_target_address[i] = 0;
+ }
+
+ /* Get SCP buffer */
+ if (test_bit(COMP_START, &pipeline->comp_state[IS_SCP]) &&
+ !list_empty(&pipeline->scaler[SCALER_SCP].wait_queue)) {
+ scp_buf = fimc_is_scaler_wait_queue_get(
+ &pipeline->scaler[SCALER_SCP]);
+ if (scp_buf) {
+ fimc_is_scaler_run_queue_add(
+ &pipeline->scaler[SCALER_SCP],
+ scp_buf);
+ for (i = 0; i < 3; i++)
+ shot->uctl.scaler_ud.scp_target_address[i] =
+ scp_buf->paddr[i];
+ set_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]);
+ }
+ } else {
+ dev_dbg(pipeline->dev, "No SCP buffer available\n");
+ for (i = 0; i < 3; i++)
+ shot->uctl.scaler_ud.scp_target_address[i] = 0;
+ }
+ fimc_is_pipeline_buf_unlock(pipeline);
+
+ /* Send shot command */
+ pipeline->fcount++;
+ rcount = pipeline->fcount;
+ shot->ctl.request.framecount = pipeline->fcount;
+ dev_dbg(pipeline->dev,
+ "Shot command fcount : %d, Bayer addr : 0x%08x\n",
+ pipeline->fcount, bayer_buf->paddr[0]);
+ ret = fimc_is_itf_shot_nblk(&is->interface, pipeline->instance,
+ bayer_buf->paddr[0],
+ pipeline->minfo->shot.paddr,
+ pipeline->fcount,
+ rcount);
+ if (ret) {
+ dev_err(pipeline->dev, "Shot command failed\n");
+ goto err_exit;
+ }
+ return 0;
+
+err_exit:
+ clear_bit(PIPELINE_RUN, &pipeline->state);
+ clear_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]);
+ clear_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]);
+ return -EINVAL;
+}
+
+int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon)
+{
+ int ret = 0;
+ struct fimc_is *is = pipeline->is;
+
+ /* Check if open or not */
+ if (!test_bit(PIPELINE_OPEN, &pipeline->state)) {
+ dev_err(pipeline->dev, "Pipeline not open\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pipeline->pipe_lock);
+
+ /* Check if already started */
+ if (test_bit(PIPELINE_START, &pipeline->state))
+ goto err_exit;
+
+ /* Set pipeline component params */
+ ret = fimc_is_pipeline_setparams(pipeline);
+ if (ret) {
+ dev_err(pipeline->dev, "Set params failed\n");
+ goto err_exit;
+ }
+
+ /* Send preview still command */
+ ret = fimc_is_itf_preview_still(&is->interface, pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "Preview still failed\n");
+ goto err_exit;
+ }
+
+ /* Confiture shot memory to A5 */
+ ret = fimc_is_itf_cfg_mem(&is->interface, pipeline->instance,
+ pipeline->minfo->shot.paddr,
+ sizeof(struct camera2_shot));
+ if (ret) {
+ dev_err(pipeline->dev, "Config A5 mem failed\n");
+ goto err_exit;
+ }
+
+ /* Set shot params */
+ fimc_is_pipeline_config_shot(pipeline);
+
+ /* Process ON command */
+ ret = fimc_is_itf_process_on(&is->interface, pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "Process on failed\n");
+ goto err_exit;
+ }
+
+ /* Stream ON */
+ if (streamon) {
+ ret = fimc_is_itf_stream_on(&is->interface, pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "Stream On failed\n");
+ goto err_exit;
+ }
+ }
+
+ /* Set state to START */
+ set_bit(PIPELINE_START, &pipeline->state);
+
+ mutex_unlock(&pipeline->pipe_lock);
+ return 0;
+
+err_exit:
+ mutex_unlock(&pipeline->pipe_lock);
+ return ret;
+}
+
+int fimc_is_pipeline_stop(struct fimc_is_pipeline *pipeline, int streamoff)
+{
+ int ret;
+ struct fimc_is *is = pipeline->is;
+
+ mutex_lock(&pipeline->pipe_lock);
+
+ /* Check if started */
+ if (!test_bit(PIPELINE_OPEN, &pipeline->state) ||
+ !test_bit(PIPELINE_START, &pipeline->state)) {
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ /* Wait if any operation is in progress */
+ ret = wait_event_timeout(pipeline->wait_q,
+ !test_bit(PIPELINE_RUN, &pipeline->state),
+ FIMC_IS_COMMAND_TIMEOUT);
+ if (!ret) {
+ dev_err(pipeline->dev, "Pipeline timeout");
+ ret = -EBUSY;
+ goto err_exit;
+ }
+
+ /* Wait for scaler operations if any to complete */
+ ret = wait_event_timeout(pipeline->scaler[SCALER_SCC].event_q,
+ !test_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]),
+ FIMC_IS_COMMAND_TIMEOUT);
+ if (!ret) {
+ dev_err(pipeline->dev, "SCC timeout");
+ ret = -EBUSY;
+ goto err_exit;
+ }
+ ret = wait_event_timeout(pipeline->scaler[SCALER_SCP].event_q,
+ !test_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]),
+ FIMC_IS_COMMAND_TIMEOUT);
+ if (!ret) {
+ dev_err(pipeline->dev, "SCP timeout");
+ ret = -EBUSY;
+ goto err_exit;
+ }
+
+ if (streamoff) {
+ /* Stream OFF */
+ ret = fimc_is_itf_stream_off(&is->interface,
+ pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "Stream Off failed\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+ }
+
+ /* Process OFF */
+ ret = fimc_is_itf_process_off(&is->interface, pipeline->instance);
+ if (ret) {
+ dev_err(pipeline->dev, "Process off failed\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ /* Clear state */
+ clear_bit(PIPELINE_START, &pipeline->state);
+
+err_exit:
+ mutex_unlock(&pipeline->pipe_lock);
+ return ret;
+}
+
+int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline,
+ struct fimc_is_sensor *sensor)
+{
+ struct fimc_is *is = pipeline->is;
+ struct is_region *region;
+ unsigned long index[2] = {0};
+ int ret;
+
+ if (!sensor)
+ return -EINVAL;
+
+ mutex_lock(&pipeline->pipe_lock);
+
+ if (test_bit(PIPELINE_OPEN, &pipeline->state)) {
+ dev_err(pipeline->dev, "Pipeline already open\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ pipeline->fcount = 0;
+ pipeline->sensor = sensor;
+
+ if (is->num_pipelines == 0) {
+ /* Init memory */
+ ret = fimc_is_pipeline_initmem(pipeline);
+ if (ret) {
+ dev_err(pipeline->dev, "Pipeline memory init failed\n");
+ goto err_exit;
+ }
+
+ /* Load firmware */
+ ret = fimc_is_pipeline_load_firmware(pipeline);
+ if (ret) {
+ dev_err(pipeline->dev, "Firmware load failed\n");
+ goto err_fw;
+ }
+
+ /* Power ON */
+ ret = fimc_is_pipeline_power(pipeline, 1);
+ if (ret) {
+ dev_err(pipeline->dev, "A5 power on failed\n");
+ goto err_fw;
+ }
+
+ /* Wait for FW Init to complete */
+ ret = fimc_is_itf_wait_init_state(&is->interface);
+ if (ret) {
+ dev_err(pipeline->dev, "FW init failed\n");
+ goto err_fw;
+ }
+ }
+
+ /* Open Sensor */
+ region = pipeline->is_region;
+ ret = fimc_is_itf_open_sensor(&is->interface,
+ pipeline->instance,
+ sensor->drvdata->id,
+ sensor->i2c_bus,
+ pipeline->minfo->shared.paddr);
+ if (ret) {
+ dev_err(pipeline->dev, "Open sensor failed\n");
+ goto err_exit;
+ }
+
+ /* Load setfile */
+ ret = fimc_is_pipeline_setfile(pipeline);
+ if (ret)
+ goto err_exit;
+
+ /* Stream off */
+ ret = fimc_is_itf_stream_off(&is->interface, pipeline->instance);
+ if (ret)
+ goto err_exit;
+
+ /* Process off */
+ ret = fimc_is_itf_process_off(&is->interface, pipeline->instance);
+ if (ret)
+ goto err_exit;
+
+ if (is->num_pipelines == 0) {
+ /* Copy init params to FW region */
+ memset(&region->parameter, 0x0, sizeof(struct is_param_region));
+
+ region->parameter.sensor = init_sensor_param;
+ region->parameter.isp = init_isp_param;
+ region->parameter.drc = init_drc_param;
+ region->parameter.scalerc = init_scalerc_param;
+ region->parameter.odc = init_odc_param;
+ region->parameter.dis = init_dis_param;
+ region->parameter.tdnr = init_tdnr_param;
+ region->parameter.scalerp = init_scalerp_param;
+ region->parameter.fd = init_fd_param;
+ wmb();
+
+ /* Set all init params to FW */
+ index[0] = 0xffffffff;
+ index[1] = 0xffffffff;
+ ret = fimc_is_itf_set_param(&is->interface, pipeline->instance,
+ index[0], index[1]);
+ if (ret) {
+ dev_err(pipeline->dev, "%s failed\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ /* Set state to OPEN */
+ set_bit(PIPELINE_OPEN, &pipeline->state);
+ is->num_pipelines++;
+
+ mutex_unlock(&pipeline->pipe_lock);
+ return 0;
+
+err_fw:
+ fimc_is_pipeline_freemem(pipeline);
+err_exit:
+ mutex_unlock(&pipeline->pipe_lock);
+ return ret;
+}
+
+int fimc_is_pipeline_close(struct fimc_is_pipeline *pipeline)
+{
+ int ret;
+ struct fimc_is *is = pipeline->is;
+
+ mutex_lock(&pipeline->pipe_lock);
+
+ if (!test_bit(PIPELINE_OPEN, &pipeline->state)) {
+ dev_err(pipeline->dev, "Pipeline not opened\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ if (test_bit(PIPELINE_START, &pipeline->state)) {
+ dev_err(pipeline->dev, "Cannot close pipeline when its started\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ is->num_pipelines--;
+ if (is->num_pipelines == 0) {
+ /* FW power off command */
+ ret = fimc_is_itf_power_down(&is->interface,
+ pipeline->instance);
+ if (ret)
+ dev_err(pipeline->dev, "FW power down error\n");
+
+ /* Pipeline power off*/
+ fimc_is_pipeline_power(pipeline, 0);
+
+ /* Free memory */
+ fimc_is_pipeline_freemem(pipeline);
+ }
+
+ clear_bit(PIPELINE_OPEN, &pipeline->state);
+ mutex_unlock(&pipeline->pipe_lock);
+ return 0;
+
+err_exit:
+ mutex_unlock(&pipeline->pipe_lock);
+ return ret;
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.h b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h
new file mode 100644
index 00000000000..b327edd6928
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h
@@ -0,0 +1,129 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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_PIPELINE_H_
+#define FIMC_IS_PIPELINE_H_
+
+#include "fimc-is-core.h"
+#include "fimc-is-sensor.h"
+#include "fimc-is-isp.h"
+#include "fimc-is-scaler.h"
+
+#define FIMC_IS_A5_MEM_SIZE 0x00a00000
+#define FIMC_IS_A5_SEN_SIZE 0x00100000
+#define FIMC_IS_REGION_SIZE 0x5000
+#define FIMC_IS_SETFILE_SIZE 0xc0d8
+#define FIMC_IS_TDNR_MEM_SIZE (1920 * 1080 * 4)
+#define FIMC_IS_DEBUG_REGION_ADDR 0x00840000
+#define FIMC_IS_SHARED_REGION_ADDR 0x008c0000
+#define FIMC_IS_A5_TIMEOUT 1000
+
+#define FIMC_IS_SCP_REGION_INDEX 400
+#define FIMC_IS_SCC_REGION_INDEX 447
+
+#define MAX_ODC_INTERNAL_BUF_WIDTH 2560 /* 4808 in HW */
+#define MAX_ODC_INTERNAL_BUF_HEIGHT 1920 /* 3356 in HW */
+#define SIZE_ODC_INTERNAL_BUF \
+ (MAX_ODC_INTERNAL_BUF_WIDTH * MAX_ODC_INTERNAL_BUF_HEIGHT * 3)
+
+#define MAX_DIS_INTERNAL_BUF_WIDTH 2400
+#define MAX_DIS_INTERNAL_BUF_HEIGHT 1360
+#define SIZE_DIS_INTERNAL_BUF \
+ (MAX_DIS_INTERNAL_BUF_WIDTH * MAX_DIS_INTERNAL_BUF_HEIGHT * 2)
+
+#define MAX_3DNR_INTERNAL_BUF_WIDTH 1920
+#define MAX_3DNR_INTERNAL_BUF_HEIGHT 1088
+#define SIZE_DNR_INTERNAL_BUF \
+ (MAX_3DNR_INTERNAL_BUF_WIDTH * MAX_3DNR_INTERNAL_BUF_HEIGHT * 2)
+
+#define NUM_ODC_INTERNAL_BUF 2
+#define NUM_DIS_INTERNAL_BUF 5
+#define NUM_DNR_INTERNAL_BUF 2
+
+#define FIMC_IS_FW_BASE_MASK ((1 << 26) - 1)
+
+#define FIMC_IS_NUM_COMPS 8
+
+#define FIMC_IS_MAGIC_NUMBER 0x23456789
+
+enum pipeline_state {
+ PIPELINE_INIT,
+ PIPELINE_OPEN,
+ PIPELINE_START,
+ PIPELINE_RUN,
+};
+
+enum is_components {
+ IS_ISP,
+ IS_DRC,
+ IS_SCC,
+ IS_ODC,
+ IS_DIS,
+ IS_TDNR,
+ IS_SCP,
+ IS_FD
+};
+
+enum component_state {
+ COMP_ENABLE,
+ COMP_START,
+ COMP_RUN
+};
+
+struct fimc_is_pipeline {
+ unsigned long state;
+ unsigned long comp_state[FIMC_IS_NUM_COMPS];
+ bool force_down;
+ unsigned int instance;
+ /* Locks the isp / scaler buffers */
+ spinlock_t slock_buf;
+ unsigned long slock_flags;
+ wait_queue_head_t wait_q;
+ /* For locking pipeline ops */
+ struct mutex pipe_lock;
+ /* For locking scaler ops in pipeline */
+ struct mutex pipe_scl_lock;
+
+ struct fimc_is_meminfo *minfo;
+ struct is_region *is_region;
+ struct device *dev;
+
+ struct fimc_is *is;
+ struct fimc_is_sensor *sensor;
+ struct fimc_is_isp isp;
+ struct fimc_is_scaler scaler[FIMC_IS_NUM_SCALERS];
+
+ unsigned int fcount;
+ unsigned int isp_width;
+ unsigned int isp_height;
+};
+
+void fimc_is_pipeline_buf_lock(struct fimc_is_pipeline *pipeline);
+void fimc_is_pipeline_buf_unlock(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_setparams(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline,
+ enum fimc_is_scaler_id scaler_id,
+ unsigned int num_bufs,
+ unsigned int num_planes);
+int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline,
+ enum fimc_is_scaler_id scaler_id);
+void fimc_is_pipeline_config_shot(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon);
+int fimc_is_pipeline_stop(struct fimc_is_pipeline *pipeline, int streamoff);
+int fimc_is_pipeline_init(struct fimc_is_pipeline *pipeline,
+ unsigned int instance, void *is_ctx);
+int fimc_is_pipeline_destroy(struct fimc_is_pipeline *pipeline);
+int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline,
+ struct fimc_is_sensor *sensor);
+int fimc_is_pipeline_close(struct fimc_is_pipeline *pipeline);
+
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-regs.h b/drivers/media/platform/exynos5-is/fimc-is-regs.h
new file mode 100644
index 00000000000..06aa466624f
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-regs.h
@@ -0,0 +1,105 @@
+/*
+ * Samsung Exynos5 SoC series FIMC-IS driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd
+ * Arun Kumar K <arun.kk@samsung.com>
+ * Kil-yeon Lim <kilyeon.im@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_REGS_H
+#define FIMC_IS_REGS_H
+
+/* WDT_ISP register */
+#define WDT 0x00170000
+/* MCUCTL register */
+#define MCUCTL 0x00180000
+/* MCU Controller Register */
+#define MCUCTLR (MCUCTL+0x00)
+#define MCUCTLR_AXI_ISPX_AWCACHE(x) ((x) << 16)
+#define MCUCTLR_AXI_ISPX_ARCACHE(x) ((x) << 12)
+#define MCUCTLR_MSWRST (1 << 0)
+/* Boot Base OFfset Address Register */
+#define BBOAR (MCUCTL+0x04)
+#define BBOAR_BBOA(x) ((x) << 0)
+
+/* Interrupt Generation Register 0 from Host CPU to VIC */
+#define INTGR0 (MCUCTL+0x08)
+#define INTGR0_INTGC(n) (1 << ((n) + 16))
+#define INTGR0_INTGD(n) (1 << (n))
+
+/* Interrupt Clear Register 0 from Host CPU to VIC */
+#define INTCR0 (MCUCTL+0x0c)
+#define INTCR0_INTCC(n) (1 << ((n) + 16))
+#define INTCR0_INTCD(n) (1 << (n))
+
+/* Interrupt Mask Register 0 from Host CPU to VIC */
+#define INTMR0 (MCUCTL+0x10)
+#define INTMR0_INTMC(n) (1 << ((n) + 16))
+#define INTMR0_INTMD(n) (1 << (n))
+
+/* Interrupt Status Register 0 from Host CPU to VIC */
+#define INTSR0 (MCUCTL+0x14)
+#define INTSR0_GET_INTSD(n, x) (((x) >> (n)) & 0x1)
+#define INTSR0_GET_INTSC(n, x) (((x) >> ((n) + 16)) & 0x1)
+
+/* Interrupt Mask Status Register 0 from Host CPU to VIC */
+#define INTMSR0 (MCUCTL+0x18)
+#define INTMSR0_GET_INTMSD(n, x) (((x) >> (n)) & 0x1)
+#define INTMSR0_GET_INTMSC(n, x) (((x) >> ((n) + 16)) & 0x1)
+
+/* Interrupt Generation Register 1 from ISP CPU to Host IC */
+#define INTGR1 (MCUCTL+0x1c)
+#define INTGR1_INTGC(n) (1 << (n))
+
+/* Interrupt Clear Register 1 from ISP CPU to Host IC */
+#define INTCR1 (MCUCTL+0x20)
+#define INTCR1_INTCC(n) (1 << (n))
+
+/* Interrupt Mask Register 1 from ISP CPU to Host IC */
+#define INTMR1 (MCUCTL+0x24)
+#define INTMR1_INTMC(n) (1 << (n))
+
+/* Interrupt Status Register 1 from ISP CPU to Host IC */
+#define INTSR1 (MCUCTL+0x28)
+/* Interrupt Mask Status Register 1 from ISP CPU to Host IC */
+#define INTMSR1 (MCUCTL+0x2c)
+/* Interrupt Clear Register 2 from ISP BLK's interrupts to Host IC */
+#define INTCR2 (MCUCTL+0x30)
+#define INTCR2_INTCC(n) (1 << (n))
+
+/* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */
+#define INTMR2 (MCUCTL+0x34)
+#define INTMR2_INTMCIS(n) (1 << (n))
+
+/* Interrupt Status Register 2 from ISP BLK's interrupts to Host IC */
+#define INTSR2 (MCUCTL+0x38)
+/* Interrupt Mask Status Register 2 from ISP BLK's interrupts to Host IC */
+#define INTMSR2 (MCUCTL+0x3c)
+/* General Purpose Output Control Register (0~17) */
+#define GPOCTLR (MCUCTL+0x40)
+#define GPOCTLR_GPOG(n, x) ((x) << (n))
+
+/* General Purpose Pad Output Enable Register (0~17) */
+#define GPOENCTLR (MCUCTL+0x44)
+#define GPOENCTLR_GPOEN0(n, x) ((x) << (n))
+
+/* General Purpose Input Control Register (0~17) */
+#define GPICTLR (MCUCTL+0x48)
+
+/* IS Shared Registers between ISP CPU and HOST CPU */
+#define ISSR(n) (MCUCTL + 0x80 + (n))
+
+/* PMU for FIMC-IS*/
+#define PMUREG_CMU_RESET_ISP_SYS_PWR_REG 0x1584
+#define PMUREG_ISP_ARM_CONFIGURATION 0x2280
+#define PMUREG_ISP_ARM_STATUS 0x2284
+#define PMUREG_ISP_ARM_OPTION 0x2288
+#define PMUREG_ISP_LOW_POWER_OFF 0x0004
+#define PMUREG_ISP_CONFIGURATION 0x4020
+#define PMUREG_ISP_STATUS 0x4024
+
+#endif
diff --git a/drivers/media/platform/exynos5-is/fimc-is-scaler.c b/drivers/media/platform/exynos5-is/fimc-is-scaler.c
new file mode 100644
index 00000000000..370cc5a4fc4
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-scaler.c
@@ -0,0 +1,474 @@
+/*
+ * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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 <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-is.h"
+
+#define IS_SCALER_DRV_NAME "fimc-is-scaler"
+
+static const struct fimc_is_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 3p MultiPlanar",
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .depth = {8, 2, 2},
+ .num_planes = 3,
+ },
+ {
+ .name = "YUV 4:2:0 2p MultiPlanar",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .depth = {8, 4},
+ .num_planes = 2,
+ },
+ {
+ .name = "YUV 4:2:2 1p MultiPlanar",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = {16},
+ .num_planes = 1,
+ },
+};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static const struct fimc_is_fmt *find_format(struct v4l2_format *f)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static int scaler_video_capture_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ int ret;
+
+ ret = fimc_is_pipeline_scaler_start(ctx->pipeline,
+ ctx->scaler_id,
+ vq->num_buffers,
+ ctx->fmt->num_planes);
+ if (ret) {
+ v4l2_err(&ctx->subdev, "Scaler start failed.\n");
+ return -EINVAL;
+ }
+
+ set_bit(STATE_RUNNING, &ctx->capture_state);
+ return 0;
+}
+
+static void scaler_video_capture_stop_streaming(struct vb2_queue *vq)
+{
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *buf;
+ int ret;
+
+ ret = fimc_is_pipeline_scaler_stop(ctx->pipeline, ctx->scaler_id);
+ if (ret)
+ v4l2_info(&ctx->subdev, "Scaler already stopped.\n");
+
+ /* Release un-used buffers */
+ while (!list_empty(&ctx->wait_queue)) {
+ buf = fimc_is_scaler_wait_queue_get(ctx);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ while (!list_empty(&ctx->run_queue)) {
+ buf = fimc_is_scaler_run_queue_get(ctx);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ clear_bit(STATE_RUNNING, &ctx->capture_state);
+}
+
+static int scaler_video_capture_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ const struct fimc_is_fmt *fmt = ctx->fmt;
+ unsigned int wh;
+ int i;
+
+ if (!fmt)
+ return -EINVAL;
+
+ *num_planes = fmt->num_planes;
+ wh = ctx->width * ctx->height;
+
+ for (i = 0; i < *num_planes; i++) {
+ allocators[i] = ctx->alloc_ctx;
+ sizes[i] = (wh * fmt->depth[i]) / 8;
+ }
+ return 0;
+}
+
+static int scaler_video_capture_buffer_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *buf = container_of(vb, struct fimc_is_buf, vb);
+ const struct fimc_is_fmt *fmt;
+ int i;
+
+ fmt = ctx->fmt;
+ for (i = 0; i < fmt->num_planes; i++)
+ buf->paddr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ return 0;
+}
+
+static int scaler_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ int i;
+
+ for (i = 0; i < ctx->fmt->num_planes; i++) {
+ unsigned long size = (ctx->width * ctx->height *
+ ctx->fmt->depth[i]) / 8;
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&ctx->subdev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void scaler_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *buf = container_of(vb, struct fimc_is_buf, vb);
+
+ /* Add buffer to the wait queue */
+ fimc_is_pipeline_buf_lock(ctx->pipeline);
+ fimc_is_scaler_wait_queue_add(ctx, buf);
+ fimc_is_pipeline_buf_unlock(ctx->pipeline);
+}
+
+static const struct vb2_ops scaler_video_capture_qops = {
+ .queue_setup = scaler_video_capture_queue_setup,
+ .buf_init = scaler_video_capture_buffer_init,
+ .buf_prepare = scaler_video_capture_buffer_prepare,
+ .buf_queue = scaler_video_capture_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = scaler_video_capture_start_streaming,
+ .stop_streaming = scaler_video_capture_stop_streaming,
+};
+
+static const struct v4l2_file_operations scaler_video_capture_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int scaler_querycap_capture(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_is_scaler *ctx = video_drvdata(file);
+ char *name = (ctx->scaler_id == SCALER_SCC) ?
+ "fimc-is-scc" : "fimc-is-scp";
+
+ strncpy(cap->driver, name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, name, sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ name);
+ cap->device_caps = V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int scaler_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_is_fmt *fmt;
+
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int scaler_g_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_is_scaler *ctx = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ const struct fimc_is_fmt *fmt = ctx->fmt;
+ int i;
+
+ for (i = 0; i < fmt->num_planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[i];
+ plane_fmt->bytesperline = (ctx->width * fmt->depth[i]) / 8;
+ plane_fmt->sizeimage = plane_fmt->bytesperline * ctx->height;
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+ }
+
+ pixm->num_planes = fmt->num_planes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->width = ctx->width;
+ pixm->height = ctx->height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ return 0;
+}
+
+static int scaler_try_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct fimc_is_fmt *fmt;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ u32 i;
+
+ fmt = find_format(f);
+ if (!fmt)
+ fmt = &formats[0];
+
+ v4l_bound_align_image(&pixm->width, SCALER_MIN_WIDTH,
+ SCALER_MAX_WIDTH, 0,
+ &pixm->height, SCALER_MIN_HEIGHT,
+ SCALER_MAX_HEIGHT, 0, 0);
+
+ for (i = 0; i < fmt->num_planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[i];
+
+ plane_fmt->bytesperline = (pixm->width * fmt->depth[i]) / 8;
+ plane_fmt->sizeimage = (pixm->width * pixm->height *
+ fmt->depth[i]) / 8;
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+ }
+ pixm->num_planes = fmt->num_planes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->field = V4L2_FIELD_NONE;
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ return 0;
+}
+
+static int scaler_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_is_scaler *ctx = video_drvdata(file);
+ const struct fimc_is_fmt *fmt;
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ int ret;
+
+ ret = scaler_try_fmt_mplane(file, priv, f);
+ if (ret)
+ return ret;
+
+ /* Get format type */
+ fmt = find_format(f);
+ if (!fmt) {
+ fmt = &formats[0];
+ pixm->pixelformat = fmt->fourcc;
+ pixm->num_planes = fmt->num_planes;
+ }
+
+ /* Save values to context */
+ ctx->fmt = fmt;
+ ctx->width = pixm->width;
+ ctx->height = pixm->height;
+ set_bit(STATE_INIT, &ctx->capture_state);
+ return 0;
+}
+
+static int scaler_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_is_scaler *ctx = video_drvdata(file);
+ int ret;
+
+ reqbufs->count = max_t(u32, FIMC_IS_SCALER_REQ_BUFS_MIN,
+ reqbufs->count);
+ ret = vb2_reqbufs(&ctx->vbq, reqbufs);
+ if (ret) {
+ v4l2_err(&ctx->subdev, "vb2 req buffers failed\n");
+ return ret;
+ }
+
+ if (reqbufs->count < FIMC_IS_SCALER_REQ_BUFS_MIN) {
+ reqbufs->count = 0;
+ vb2_reqbufs(&ctx->vbq, reqbufs);
+ return -ENOMEM;
+ }
+ set_bit(STATE_BUFS_ALLOCATED, &ctx->capture_state);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops scaler_video_capture_ioctl_ops = {
+ .vidioc_querycap = scaler_querycap_capture,
+ .vidioc_enum_fmt_vid_cap_mplane = scaler_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = scaler_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = scaler_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = scaler_g_fmt_mplane,
+ .vidioc_reqbufs = scaler_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static int scaler_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_is_scaler *ctx = v4l2_get_subdevdata(sd);
+ struct vb2_queue *q = &ctx->vbq;
+ struct video_device *vfd = &ctx->vfd;
+ int ret;
+
+ mutex_init(&ctx->video_lock);
+
+ memset(vfd, 0, sizeof(*vfd));
+ if (ctx->scaler_id == SCALER_SCC)
+ snprintf(vfd->name, sizeof(vfd->name), "fimc-is-scaler.codec");
+ else
+ snprintf(vfd->name, sizeof(vfd->name),
+ "fimc-is-scaler.preview");
+
+ vfd->fops = &scaler_video_capture_fops;
+ vfd->ioctl_ops = &scaler_video_capture_ioctl_ops;
+ vfd->v4l2_dev = sd->v4l2_dev;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &ctx->video_lock;
+ vfd->queue = q;
+ vfd->vfl_dir = VFL_DIR_RX;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->ops = &scaler_video_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct fimc_is_buf);
+ q->drv_priv = ctx;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ return ret;
+
+ ctx->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &ctx->vd_pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vfd, ctx);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vfd->entity);
+ return ret;
+ }
+
+ v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+}
+
+static void scaler_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_is_scaler *ctx = v4l2_get_subdevdata(sd);
+
+ if (ctx && video_is_registered(&ctx->vfd))
+ video_unregister_device(&ctx->vfd);
+}
+
+static const struct v4l2_subdev_internal_ops scaler_subdev_internal_ops = {
+ .registered = scaler_subdev_registered,
+ .unregistered = scaler_subdev_unregistered,
+};
+
+static struct v4l2_subdev_ops scaler_subdev_ops;
+
+int fimc_is_scaler_subdev_create(struct fimc_is_scaler *ctx,
+ enum fimc_is_scaler_id scaler_id,
+ struct vb2_alloc_ctx *alloc_ctx,
+ struct fimc_is_pipeline *pipeline)
+{
+ struct v4l2_ctrl_handler *handler = &ctx->ctrl_handler;
+ struct v4l2_subdev *sd = &ctx->subdev;
+ int ret;
+
+ ctx->scaler_id = scaler_id;
+ ctx->alloc_ctx = alloc_ctx;
+ ctx->pipeline = pipeline;
+ ctx->fmt = &formats[0];
+ ctx->width = SCALER_DEF_WIDTH;
+ ctx->height = SCALER_DEF_HEIGHT;
+ init_waitqueue_head(&ctx->event_q);
+ INIT_LIST_HEAD(&ctx->wait_queue);
+ INIT_LIST_HEAD(&ctx->run_queue);
+
+ /* Initialize scaler subdev */
+ v4l2_subdev_init(sd, &scaler_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ if (scaler_id == SCALER_SCC)
+ snprintf(sd->name, sizeof(sd->name), "fimc-is-scc");
+ else
+ snprintf(sd->name, sizeof(sd->name), "fimc-is-scp");
+
+ ctx->subdev_pads[SCALER_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ ctx->subdev_pads[SCALER_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE;
+ ctx->subdev_pads[SCALER_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, ISP_SD_PADS_NUM,
+ ctx->subdev_pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (handler->error)
+ goto err_ctrl;
+
+ sd->ctrl_handler = handler;
+ sd->internal_ops = &scaler_subdev_internal_ops;
+ v4l2_set_subdevdata(sd, ctx);
+
+ return 0;
+err_ctrl:
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(handler);
+ return ret;
+}
+
+void fimc_is_scaler_subdev_destroy(struct fimc_is_scaler *ctx)
+{
+ struct v4l2_subdev *sd = &ctx->subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-scaler.h b/drivers/media/platform/exynos5-is/fimc-is-scaler.h
new file mode 100644
index 00000000000..97a9d0d72d3
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-scaler.h
@@ -0,0 +1,106 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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_SCALER_H_
+#define FIMC_IS_SCALER_H_
+
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include <linux/irqreturn.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>
+
+#include "fimc-is-core.h"
+
+#define SCALER_SD_PAD_SINK 0
+#define SCALER_SD_PAD_SRC_FIFO 1
+#define SCALER_SD_PAD_SRC_DMA 2
+#define SCALER_SD_PADS_NUM 3
+
+#define SCALER_MAX_BUFS 32
+#define SCALER_MAX_PLANES 3
+
+#define FIMC_IS_SCALER_REQ_BUFS_MIN 2
+
+#define SCALER_DEF_WIDTH 1280
+#define SCALER_DEF_HEIGHT 720
+#define SCALER_MAX_WIDTH 4808
+#define SCALER_MAX_HEIGHT 3356
+#define SCALER_MIN_WIDTH 32
+#define SCALER_MIN_HEIGHT 32
+
+/**
+ * struct fimc_is_scaler - fimc-is scaler structure
+ * @vfd: video device node
+ * @fh: v4l2 file handle
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @subdev: fimc-is-scaler subdev
+ * @vd_pad: media pad for the output video node
+ * @subdev_pads: the subdev media pads
+ * @ctrl_handler: v4l2 control handler
+ * @video_lock: video lock mutex
+ * @event_q: notifies scaler events
+ * @pipeline: pipeline instance for this scaler context
+ * @scaler_id: distinguishes scaler preview or scaler codec
+ * @vbq: vb2 buffers queue for ISP output video node
+ * @wait_queue: list holding buffers waiting to be queued to HW
+ * @wait_queue_cnt: wait queue number of buffers
+ * @run_queue: list holding buffers queued to HW
+ * @run_queue_cnt: run queue number of buffers
+ * @capture_bufs: scaler capture buffers array
+ * @fmt: capture plane format for scaler
+ * @width: user configured output width
+ * @height: user configured output height
+ * @capture_state: state of the capture video node operations
+ */
+struct fimc_is_scaler {
+ struct video_device vfd;
+ struct v4l2_fh fh;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_subdev subdev;
+ struct media_pad vd_pad;
+ struct media_pad subdev_pads[SCALER_SD_PADS_NUM];
+ struct v4l2_mbus_framefmt subdev_fmt;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct mutex video_lock;
+ wait_queue_head_t event_q;
+
+ struct fimc_is_pipeline *pipeline;
+ enum fimc_is_scaler_id scaler_id;
+
+ struct vb2_queue vbq;
+ struct list_head wait_queue;
+ unsigned int wait_queue_cnt;
+ struct list_head run_queue;
+ unsigned int run_queue_cnt;
+
+ const struct fimc_is_fmt *fmt;
+ unsigned int width;
+ unsigned int height;
+ unsigned long capture_state;
+};
+
+int fimc_is_scaler_subdev_create(struct fimc_is_scaler *ctx,
+ enum fimc_is_scaler_id scaler_id,
+ struct vb2_alloc_ctx *alloc_ctx,
+ struct fimc_is_pipeline *pipeline);
+void fimc_is_scaler_subdev_destroy(struct fimc_is_scaler *scaler);
+
+#endif /* FIMC_IS_SCALER_H_ */
diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.c b/drivers/media/platform/exynos5-is/fimc-is-sensor.c
new file mode 100644
index 00000000000..475f1c3051b
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.c
@@ -0,0 +1,45 @@
+/*
+ * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Arun Kumar K <arun.kk@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-sensor.h"
+
+static const struct sensor_drv_data s5k6a3_drvdata = {
+ .id = FIMC_IS_SENSOR_ID_S5K6A3,
+ .open_timeout = S5K6A3_OPEN_TIMEOUT,
+ .setfile_name = "exynos5_s5k6a3_setfile.bin",
+};
+
+static const struct sensor_drv_data s5k4e5_drvdata = {
+ .id = FIMC_IS_SENSOR_ID_S5K4E5,
+ .open_timeout = S5K4E5_OPEN_TIMEOUT,
+ .setfile_name = "exynos5_s5k4e5_setfile.bin",
+};
+
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
+ {
+ .compatible = "samsung,s5k6a3",
+ .data = &s5k6a3_drvdata,
+ },
+ {
+ .compatible = "samsung,s5k4e5",
+ .data = &s5k4e5_drvdata,
+ },
+ { }
+};
+
+const struct sensor_drv_data *exynos5_is_sensor_get_drvdata(
+ struct device_node *node)
+{
+ const struct of_device_id *of_id;
+
+ of_id = of_match_node(fimc_is_sensor_of_ids, node);
+ return of_id ? of_id->data : NULL;
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.h b/drivers/media/platform/exynos5-is/fimc-is-sensor.h
new file mode 100644
index 00000000000..0ba57333d0f
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.h
@@ -0,0 +1,65 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Arun Kumar K <arun.kk@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/of.h>
+#include <linux/types.h>
+
+#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */
+#define S5K6A3_SENSOR_WIDTH 1392
+#define S5K6A3_SENSOR_HEIGHT 1392
+
+#define S5K4E5_OPEN_TIMEOUT 2000 /* ms */
+#define S5K4E5_SENSOR_WIDTH 2560
+#define S5K4E5_SENSOR_HEIGHT 1920
+
+#define SENSOR_WIDTH_PADDING 16
+#define SENSOR_HEIGHT_PADDING 10
+
+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
+};
+
+struct sensor_drv_data {
+ enum fimc_is_sensor_id id;
+ /* sensor open timeout in ms */
+ unsigned short open_timeout;
+ char *setfile_name;
+};
+
+/**
+ * struct fimc_is_sensor - fimc-is sensor data structure
+ * @drvdata: a pointer to the sensor's parameters data structure
+ * @i2c_bus: ISP I2C bus index (0...1)
+ * @width: sensor active width
+ * @height: sensor active height
+ * @pixel_width: sensor effective pixel width (width + padding)
+ * @pixel_height: sensor effective pixel height (height + padding)
+ */
+struct fimc_is_sensor {
+ const struct sensor_drv_data *drvdata;
+ unsigned int i2c_bus;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pixel_width;
+ unsigned int pixel_height;
+};
+
+const struct sensor_drv_data *exynos5_is_sensor_get_drvdata(
+ struct device_node *node);
+
+#endif /* FIMC_IS_SENSOR_H_ */
diff --git a/drivers/media/platform/exynos5-is/fimc-is.h b/drivers/media/platform/exynos5-is/fimc-is.h
new file mode 100644
index 00000000000..136f367e9d8
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is.h
@@ -0,0 +1,160 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@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 "fimc-is-err.h"
+#include "fimc-is-core.h"
+#include "fimc-is-param.h"
+#include "fimc-is-pipeline.h"
+#include "fimc-is-interface.h"
+
+#define fimc_interface_to_is(p) container_of(p, struct fimc_is, interface)
+#define fimc_sensor_to_is(p) container_of(p, struct fimc_is, sensor)
+
+/*
+ * Macros used by media dev to get the subdev and vfd
+ * is - driver data from pdev
+ * pid - pipeline index
+ */
+#define fimc_is_isp_get_sd(is, pid) (&is->pipeline[pid].isp.subdev)
+#define fimc_is_isp_get_vfd(is, pid) (&is->pipeline[pid].isp.vfd)
+#define fimc_is_scc_get_sd(is, pid) \
+ (&is->pipeline[pid].scaler[SCALER_SCC].subdev)
+#define fimc_is_scc_get_vfd(is, pid) \
+ (&is->pipeline[pid].scaler[SCALER_SCC].vfd)
+#define fimc_is_scp_get_sd(is, pid) \
+ (&is->pipeline[pid].scaler[SCALER_SCP].subdev)
+#define fimc_is_scp_get_vfd(is, pid) \
+ (&is->pipeline[pid].scaler[SCALER_SCP].vfd)
+/*
+ * is - driver data from pdev
+ * sid - sensor index
+ */
+#define fimc_is_sensor_get_sd(is, sid) (&is->sensor[sid].subdev)
+
+
+/**
+ * struct fimc_is - fimc-is driver private data
+ * @pdev: pointer to FIMC-IS platform device
+ * @pdata: platform data for FIMC-IS
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @clock: FIMC-IS clocks
+ * @pmu_regs: PMU reg base address
+ * @num_pipelines: number of pipelines opened
+ * @minfo: internal memory organization info
+ * @drvdata: fimc-is driver data
+ * @sensor: FIMC-IS sensor context
+ * @pipeline: hardware pipeline context
+ * @interface: hardware interface context
+ */
+struct fimc_is {
+ struct platform_device *pdev;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct clk *clock[IS_CLK_MAX_NUM];
+ void __iomem *pmu_regs;
+ unsigned int num_pipelines;
+
+ struct fimc_is_meminfo minfo;
+
+ struct fimc_is_drvdata *drvdata;
+ struct fimc_is_sensor sensor[FIMC_IS_NUM_SENSORS];
+ struct fimc_is_pipeline pipeline[FIMC_IS_NUM_PIPELINES];
+ struct fimc_is_interface interface;
+};
+
+/* Queue operations for ISP */
+static inline void fimc_is_isp_wait_queue_add(struct fimc_is_isp *isp,
+ struct fimc_is_buf *buf)
+{
+ list_add_tail(&buf->list, &isp->wait_queue);
+ isp->wait_queue_cnt++;
+}
+
+static inline struct fimc_is_buf *fimc_is_isp_wait_queue_get(
+ struct fimc_is_isp *isp)
+{
+ struct fimc_is_buf *buf;
+ buf = list_entry(isp->wait_queue.next,
+ struct fimc_is_buf, list);
+ list_del(&buf->list);
+ isp->wait_queue_cnt--;
+ return buf;
+}
+
+static inline void fimc_is_isp_run_queue_add(struct fimc_is_isp *isp,
+ struct fimc_is_buf *buf)
+{
+ list_add_tail(&buf->list, &isp->run_queue);
+ isp->run_queue_cnt++;
+}
+
+static inline struct fimc_is_buf *fimc_is_isp_run_queue_get(
+ struct fimc_is_isp *isp)
+{
+ struct fimc_is_buf *buf;
+ buf = list_entry(isp->run_queue.next,
+ struct fimc_is_buf, list);
+ list_del(&buf->list);
+ isp->run_queue_cnt--;
+ return buf;
+}
+
+/* Queue operations for SCALER */
+static inline void fimc_is_scaler_wait_queue_add(struct fimc_is_scaler *scp,
+ struct fimc_is_buf *buf)
+{
+ list_add_tail(&buf->list, &scp->wait_queue);
+ scp->wait_queue_cnt++;
+}
+
+static inline struct fimc_is_buf *fimc_is_scaler_wait_queue_get(
+ struct fimc_is_scaler *scp)
+{
+ struct fimc_is_buf *buf;
+ buf = list_entry(scp->wait_queue.next,
+ struct fimc_is_buf, list);
+ list_del(&buf->list);
+ scp->wait_queue_cnt--;
+ return buf;
+}
+
+static inline void fimc_is_scaler_run_queue_add(struct fimc_is_scaler *scp,
+ struct fimc_is_buf *buf)
+{
+ list_add_tail(&buf->list, &scp->run_queue);
+ scp->run_queue_cnt++;
+}
+
+static inline struct fimc_is_buf *fimc_is_scaler_run_queue_get(
+ struct fimc_is_scaler *scp)
+{
+ struct fimc_is_buf *buf;
+ buf = list_entry(scp->run_queue.next,
+ struct fimc_is_buf, list);
+ list_del(&buf->list);
+ scp->run_queue_cnt--;
+ return buf;
+}
+
+static inline void pmu_is_write(u32 v, struct fimc_is *is, unsigned int offset)
+{
+ writel(v, is->pmu_regs + offset);
+}
+
+static inline u32 pmu_is_read(struct fimc_is *is, unsigned int offset)
+{
+ return readl(is->pmu_regs + offset);
+}
+
+#endif