diff options
author | Arun Kumar K <arun.kk@samsung.com> | 2013-11-05 17:43:19 +0530 |
---|---|---|
committer | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2014-11-27 03:35:09 -0800 |
commit | c28f5dd0b9b73130a4c93f33a7d1fd6296b2d8be (patch) | |
tree | 7a6915e2ac266af746558462d7f6bba41d81aeb8 | |
parent | 16c9bfd617dace172315cb0d50cfe8c075824586 (diff) | |
download | linux-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>
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(®ion->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 |