From: Marek Szyprowski Date: Thu, 6 Aug 2015 13:20:55 +0000 (+0200) Subject: fimc-is: Initial import from SM-N910 Android LL release code X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9c0c425509f6c1cfedb7933cc87b69e2d97f042f;p=platform%2Fkernel%2Flinux-exynos.git fimc-is: Initial import from SM-N910 Android LL release code Import fimc-is driver from SM-N910C_LL_Opensource (verified 32-bit Android kernel) from opensource.samsung.com Change-Id: I62eec9bc33a21c5742d2c5d5f79facfcb1ec59c9 Signed-off-by: Marek Szyprowski --- diff --git a/drivers/media/platform/exynos/fimc-is/Kconfig b/drivers/media/platform/exynos/fimc-is/Kconfig new file mode 100644 index 000000000000..2c2bd68f9b82 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/Kconfig @@ -0,0 +1,182 @@ +config VIDEO_EXYNOS_FIMC_IS + bool "Exynos FIMC-IS (Image Subsystem) driver" + depends on VIDEO_EXYNOS + select MEDIA_EXYNOS + select USE_VENDER_FEATURE + default n + help + This is a v4l2 driver for exynos FIMC-IS device. +config CAMERA_EEPROM_SUPPORT_REAR + bool "Enable eeprom for rear cam" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable eeprom for rear cam. +config CAMERA_EEPROM_SUPPORT_FRONT + bool "Enable eeprom for front cam" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable eeprom for front cam. +config COMPANION_USE + bool "Enable to companion" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable to companion. +config OIS_USE + bool "Enable to ois" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable to ois. +config OIS_FW_UPDATE_THREAD_USE + bool "Enable to ois fw update thread" + depends on OIS_USE + default n + help + Enable to ois fw update thread. +config AF_HOST_CONTROL + bool "Enable to af control" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable to af control. +config USE_VENDER_FEATURE + bool "Use vendor specific features" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Enable to use the vender. +config CAMERA_SENSOR_8B1 + bool "Use 8B1 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use 8B1 camera sensor. +config CAMERA_SENSOR_6D1 + bool "Use 6D1 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use 6D1 camera sensor. +config CAMERA_SENSOR_8B1_OBJ + bool "Use 8B1 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 8B1 camera sensor. +config CAMERA_SENSOR_6D1_OBJ + bool "Use 6D1 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 6D1 camera sensor. +config CAMERA_SENSOR_6B2_OBJ + bool "Use 6B2 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 6B2 camera sensor. +config CAMERA_SENSOR_6A3_OBJ + bool "Use 6A3 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 6A3 camera sensor. +config CAMERA_SENSOR_IMX135_OBJ + bool "Use IMX135 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build IMX135 camera sensor. +config CAMERA_SENSOR_IMX134_OBJ + bool "Use IMX134 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build IMX134 camera sensor. +config CAMERA_SENSOR_3L2_OBJ + bool "Use 3L2 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 3L2 camera sensor. +config CAMERA_SENSOR_2P2_OBJ + bool "Use 2P2 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 2P2 camera sensor. +config CAMERA_SENSOR_2P2_12M_OBJ + bool "Use 2P2_12M camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 2P2_12M camera sensor. +config CAMERA_SENSOR_2P3_OBJ + bool "Use 2P3 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 2P3 camera sensor. +config CAMERA_SENSOR_3H5_OBJ + bool "Use 3H5 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 3H5 camera sensor. +config CAMERA_SENSOR_3H7_OBJ + bool "Use 3H7 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 3H7 camera sensor. +config CAMERA_SENSOR_3H7_SUNNY_OBJ + bool "Use 3H7_SUNNY camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 3H7_SUNNY camera sensor. +config CAMERA_SENSOR_4E5_OBJ + bool "Use 4E5 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 4E5 camera sensor. +config CAMERA_SENSOR_IMX175_OBJ + bool "Use IMX175 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build IMX175 camera sensor. +config CAMERA_SENSOR_IMX219_OBJ + bool "Use IMX219 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build IMX219 camera sensor. +config CAMERA_SENSOR_IMX240_OBJ + bool "Use IMX240 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build IMX240 camera sensor. +config CAMERA_SENSOR_4H5_OBJ + bool "Use 4H5 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build 4H5 camera sensor. +config CAMERA_SENSOR_SR261_OBJ + bool "Use SR261 camera sensor" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build SR261 camera sensor. +config CAMERA_SENSOR_TEMP + bool "camera sensor temp" + depends on VIDEO_EXYNOS_FIMC_IS + default n + help + Use to build camera sensor temp. diff --git a/drivers/media/platform/exynos/fimc-is/Makefile b/drivers/media/platform/exynos/fimc-is/Makefile new file mode 100644 index 000000000000..efe18a5c3777 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/Makefile @@ -0,0 +1,62 @@ +fimc-is-objs := fimc-is-core.o \ + fimc-is-mem.o \ + fimc-is-framemgr.o \ + fimc-is-groupmgr.o \ + fimc-is-resourcemgr.o \ + fimc-is-video.o \ + fimc-is-video-sensor.o \ + fimc-is-video-3aa.o \ + fimc-is-video-3aac.o \ + fimc-is-video-isp.o \ + fimc-is-video-scc.o \ + fimc-is-video-scp.o \ + fimc-is-video-vdisc.o \ + fimc-is-video-vdiso.o \ + fimc-is-hw-csi.o \ + fimc-is-hw-ischain.o \ + fimc-is-subdev-ctrl.o \ + fimc-is-device-csi.o \ + fimc-is-device-flite.o \ + fimc-is-device-sensor.o \ + fimc-is-device-ischain.o \ + fimc-is-interface.o \ + fimc-is-time.o \ + fimc-is-dvfs.o \ + fimc-is-dt.o \ + fimc-is-clk-gate.o + +obj-$(CONFIG_USE_VENDER_FEATURE) := fimc-is-spi.o \ + fimc-is-sec-define.o \ + crc32.o + +obj-$(CONFIG_COMPANION_USE) += fimc-is-companion.o \ + fimc-is-fan53555.o \ + fimc-is-ncp6335b.o \ + fimc-is-companion-dt.o \ + fimc-is-device-companion.o \ + fimc-is-video-companion.o + +obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS) += fimc-is.o +obj-$(CONFIG_OIS_USE) += fimc-is-device-ois.o +obj-$(CONFIG_AF_HOST_CONTROL) += fimc-is-device-af.o +obj-$(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) += fimc-is-device-eeprom.o +obj-$(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) += fimc-is-device-eeprom.o + +obj-$(CONFIG_CAMERA_SENSOR_6B2_OBJ) += sensor/fimc-is-device-6b2.o +obj-$(CONFIG_CAMERA_SENSOR_8B1_OBJ) += sensor/fimc-is-device-8b1.o +obj-$(CONFIG_CAMERA_SENSOR_6D1_OBJ) += sensor/fimc-is-device-6d1.o +obj-$(CONFIG_CAMERA_SENSOR_IMX134_OBJ) += sensor/fimc-is-device-imx134.o +obj-$(CONFIG_CAMERA_SENSOR_IMX135_OBJ) += sensor/fimc-is-device-imx135.o +obj-$(CONFIG_CAMERA_SENSOR_3L2_OBJ) += sensor/fimc-is-device-3l2.o +obj-$(CONFIG_CAMERA_SENSOR_2P2_OBJ) += sensor/fimc-is-device-2p2.o +obj-$(CONFIG_CAMERA_SENSOR_2P2_12M_OBJ) += sensor/fimc-is-device-2p2_12m.o +obj-$(CONFIG_CAMERA_SENSOR_2P3_OBJ) += sensor/fimc-is-device-2p3.o +obj-$(CONFIG_CAMERA_SENSOR_3H5_OBJ) += sensor/fimc-is-device-3h5.o +obj-$(CONFIG_CAMERA_SENSOR_3H7_OBJ) += sensor/fimc-is-device-3h7.o +obj-$(CONFIG_CAMERA_SENSOR_3H7_SUNNY_OBJ) += sensor/fimc-is-device-3h7_sunny.o +obj-$(CONFIG_CAMERA_SENSOR_4E5_OBJ) += sensor/fimc-is-device-4e5.o +obj-$(CONFIG_CAMERA_SENSOR_6A3_OBJ) += sensor/fimc-is-device-6a3.o +obj-$(CONFIG_CAMERA_SENSOR_IMX175_OBJ) += sensor/fimc-is-device-imx175.o +obj-$(CONFIG_CAMERA_SENSOR_IMX240_OBJ) += sensor/fimc-is-device-imx240.o +obj-$(CONFIG_CAMERA_SENSOR_IMX219_OBJ) += sensor/fimc-is-device-imx219.o +obj-$(CONFIG_CAMERA_SENSOR_4H5_OBJ) += sensor/fimc-is-device-4h5.o \ No newline at end of file diff --git a/drivers/media/platform/exynos/fimc-is/crc32.c b/drivers/media/platform/exynos/fimc-is/crc32.c new file mode 100644 index 000000000000..5aae140f3915 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/crc32.c @@ -0,0 +1,107 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "crc32.h" + +/* #define DEBUG_CRC */ + +static void makeCRCtable(unsigned long *table, unsigned long id) +{ + unsigned long i, j, k; + + for(i = 0; i < 256; ++i) { + k = i; + + for(j = 0; j < 8; ++j) { + if(k & 1) + k = (k >> 1) ^ id; + else + k >>= 1; + } + + table[i] = k; + } +} + +unsigned long getCRC(volatile unsigned short *mem, signed long count, + volatile unsigned short *crcH, volatile unsigned short *crcL) +{ + unsigned char mem0, mem1; + int i; + unsigned long CRC = 0; +#ifndef DEBUG_CRC + unsigned long table[256]; +#else + unsigned long table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; +#endif + + /* duration : under 1 ms */ + makeCRCtable(table, 0xEDB88320); + + CRC = ~CRC; + for(i = 0; i < count; i++) { + mem0 = (unsigned char)(mem[i] & 0x00ff); + mem1 = (unsigned char)((mem[i] >> 8) & 0x00ff); + + CRC = table[(CRC ^ (mem0)) & 0xFF] ^ (CRC >> 8); + CRC = table[(CRC ^ (mem1)) & 0xFF] ^ (CRC >> 8); + } + CRC = ~CRC; + + /* + * high 2 byte of crc32 + * g_CAL[0page 31 addr.] or g_CAL[127page 31 addr.] + */ + if(crcH) + *crcH = (unsigned short)((CRC >> 16)&(0x0000ffff)); + /* + * low 2 byte of crc32 + * g_CAL[0page 30 addr.] or g_CAL[127page 30 addr.] + */ + if(crcL) + *crcL = (unsigned short)((CRC) & (0x0000ffff)); + + return CRC; +} diff --git a/drivers/media/platform/exynos/fimc-is/crc32.h b/drivers/media/platform/exynos/fimc-is/crc32.h new file mode 100644 index 000000000000..324793ccee59 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/crc32.h @@ -0,0 +1,19 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CRC32_H +#define CRC32_H + +/* unit of count is 2byte */ +unsigned long getCRC(volatile unsigned short *mem, signed long count, + volatile unsigned short *crcH, volatile unsigned short *crcL); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.c b/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.c new file mode 100644 index 000000000000..47b1b7181dba --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.c @@ -0,0 +1,263 @@ +/* + * Samsung Exynos SoC series FIMC-IS driver + * + * exynos fimc-is core functions + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "fimc-is-clk-gate.h" + +int fimc_is_clk_gate_init(struct fimc_is_core *core) +{ + struct fimc_is_clk_gate_ctrl *gate_ctrl; + + pr_info("%s\n", __func__); + + if (!core) { + err("core is NULL\n"); + return -EINVAL; + } + + gate_ctrl = &core->resourcemgr.clk_gate_ctrl; + memset(gate_ctrl, 0x0, sizeof(struct fimc_is_clk_gate_ctrl)); + + /* init spin_lock for clock gating */ + spin_lock_init(&gate_ctrl->lock); + core->resourcemgr.clk_gate_ctrl.gate_info = core->pdata->gate_info; + + /* ISSR53 is clock gating debugging region. + * High means clock on state. + * To prevent telling A5 wrong clock off state, + * clock on state should be set before clock off is set. + */ + writel(0xFFFFFFFF, core->ischain[0].interface->regs + ISSR53); + + return 0; +} + +int fimc_is_clk_gate_lock_set(struct fimc_is_core *core, u32 instance, u32 is_start) +{ + spin_lock(&core->resourcemgr.clk_gate_ctrl.lock); + core->resourcemgr.clk_gate_ctrl.msk_lock_by_ischain[instance] = is_start; + spin_unlock(&core->resourcemgr.clk_gate_ctrl.lock); + return 0; +} + +#if 0 +/* This function may be used when clk_enable api will be faster than now */ +int fimc_is_clk_gate_reg_set(struct fimc_is_core *core, + bool is_on, const char* gate_str, u32 clk_gate_id, + struct exynos_fimc_is_clk_gate_info *gate_info) +{ + struct platform_device *pdev = core->pdev; + if (is_on) { + if (gate_info->clk_on(pdev, gate_str) < 0) { + pr_err("%s: could not enable %s\n", __func__, gate_str); + return -EINVAL; + } + } else { + if (gate_info->clk_off(pdev, gate_str) < 0) { + pr_err("%s: could not disable %s\n", __func__, gate_str); + return -EINVAL; + } + } + return 0; +} +#endif + +int fimc_is_wrap_clk_gate_set(struct fimc_is_core *core, + int msk_group_id, bool is_on) +{ + int i; + + for (i = 0; i < FIMC_IS_GRP_MAX; i++) { + if (msk_group_id & (1 << i)) + fimc_is_clk_gate_set(core, i, is_on, true, false); + } + + return 0; +} + +inline bool fimc_is_group_otf(struct fimc_is_device_ischain *device, int group_id) +{ + struct fimc_is_group *group; + + switch (group_id) { + case GROUP_ID_3A0: + case GROUP_ID_3A1: + group = &device->group_3aa; + break; + case GROUP_ID_ISP: + group = &device->group_isp; + break; + case GROUP_ID_DIS: + group = &device->group_dis; + break; + default: + group = NULL; + pr_err("%s unresolved group id %d", __func__, group_id); + return false; + } + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) + return true; + else + return false; +} + +int fimc_is_clk_gate_set(struct fimc_is_core *core, + int group_id, bool is_on, bool skip_set_state, bool user_scenario) +{ + int ret = 0; + int cfg = 0; + int i; + struct fimc_is_clk_gate_ctrl *gate_ctrl; + struct exynos_fimc_is_clk_gate_info *gate_info; + u32 mask_on, mask_off, mask_cond_depend; + + gate_ctrl = &core->resourcemgr.clk_gate_ctrl; + gate_info = gate_ctrl->gate_info; + + pr_debug("%s in\n", __func__); + spin_lock(&gate_ctrl->lock); + + /* Set State */ + if (is_on) { + if (skip_set_state == false) { + (gate_ctrl->chk_on_off_cnt[group_id])++; /* for debuging */ + (gate_ctrl->msk_cnt[group_id])++; + set_bit(group_id, &gate_ctrl->msk_state); + } + gate_info->groups[group_id].mask_clk_on_mod = + gate_info->groups[group_id].mask_clk_on_org; + } else { + (gate_ctrl->chk_on_off_cnt[group_id])--; /* for debuging */ + (gate_ctrl->msk_cnt[group_id])--; + if ((gate_ctrl->msk_cnt[group_id]) < 0) { + pr_warn("%s msk_cnt[%d] is lower than zero !!\n", __func__, group_id); + (gate_ctrl->msk_cnt[group_id]) = 0; + } + if ((gate_ctrl->msk_cnt[group_id]) == 0) + clear_bit(group_id, &gate_ctrl->msk_state); + /* if there's some processing group shot, don't clock off */ + if (test_bit(group_id, &gate_ctrl->msk_state)) + goto exit; + gate_info->groups[group_id].mask_clk_off_self_mod = + gate_info->groups[group_id].mask_clk_off_self_org; + } + + /* Don't off!! when other instance opening/closing */ + if (is_on == false) { + for (i = 0; i < FIMC_IS_MAX_NODES; i++) { + if ((!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &core->ischain[i].state)) && + (gate_ctrl->msk_lock_by_ischain[i])) { + pr_info("%s lock(on) due to instance(%d)\n", __func__, i); + goto exit; + } + /* don't off! if there is at least this group that is OTF */ + if (fimc_is_group_otf(&core->ischain[i], group_id)) { + pr_debug("%s don't off!! this instance(%d) group(%d) is OTF\n", + __func__, i, group_id); + goto exit; + } + } + } + + /* Check user scenario */ + if (user_scenario && gate_info->user_clk_gate) { + if (fimc_is_set_user_clk_gate(group_id, + core, + is_on, + gate_ctrl->msk_state, + gate_info) < 0) { + pr_debug("%s user scenario is skip!! [%d] !!\n", __func__, group_id); + goto exit; + } + } + + /* Get the region for clock gating debug */ + cfg = readl(core->ischain[0].interface->regs + ISSR53); + + /* Get Mask of self-on/off */ + if (is_on) + mask_on = gate_info->groups[group_id].mask_clk_on_mod; + else + mask_off = gate_info->groups[group_id].mask_clk_off_self_mod; + + /* Clock on */ + if (is_on && ((gate_ctrl->msk_clk_on_off_state) != mask_on)) { + cfg |= (mask_on << 16); /* shortly before clock on */ + writel(cfg, core->ischain[0].interface->regs + ISSR53); + + ret = gate_info->clk_on_off(mask_on, is_on); + gate_ctrl->msk_clk_on_off_state |= mask_on; + + cfg |= (mask_on); /* after clock on */ + writel(cfg, core->ischain[0].interface->regs + ISSR53); + } + + /* Clock off and check dependancy (it's for only clock-off) */ + if (is_on == false) { + mask_cond_depend = gate_info->groups[group_id].mask_cond_for_depend; + /* check dependancy */ + if (mask_cond_depend > 0 && + (mask_cond_depend & (gate_ctrl->msk_state))) { + mask_off |= gate_info->groups[group_id].mask_clk_off_depend; + } + /* clock off */ + if (((gate_ctrl->msk_clk_on_off_state) & mask_off) > 0) { + cfg &= ~(mask_off << 16); /* shortly before clock off */ + writel(cfg, core->ischain[0].interface->regs + ISSR53); + + ret = gate_info->clk_on_off(mask_off, is_on); + gate_ctrl->msk_clk_on_off_state &= ~(mask_off); + + cfg &= ~(mask_off); /* after clock off */ + writel(cfg, core->ischain[0].interface->regs + ISSR53); + } + } +exit: + spin_unlock(&gate_ctrl->lock); + pr_debug("%s out\n", __func__); + + return ret; +} + +int fimc_is_set_user_clk_gate(u32 group_id, + struct fimc_is_core *core, + bool is_on, + unsigned long msk_state, + struct exynos_fimc_is_clk_gate_info *gate_info) +{ + u32 user_scenario_id = 0; + + /* deside what user scenario is */ +#if defined(ENABLE_FULL_BYPASS) + if (group_id == GROUP_ID_ISP) + user_scenario_id = CLK_GATE_FULL_BYPASS_SN; +#else + if (group_id == GROUP_ID_ISP) + user_scenario_id = CLK_GATE_NOT_FULL_BYPASS_SN; + +#endif + if (group_id == GROUP_ID_ISP && + test_bit(FIMC_IS_SUBDEV_START, + &core->ischain[0].dis.state)) + user_scenario_id = CLK_GATE_DIS_SN; + + if (gate_info->user_clk_gate(group_id, + is_on, + user_scenario_id, + msk_state, + gate_info) < 0) { + pr_err("%s user_clk_gate failed(%d) !!\n", __func__, group_id); + } + + return 0; +} + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.h b/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.h new file mode 100644 index 000000000000..79b9de665ee5 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-clk-gate.h @@ -0,0 +1,48 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_CLK_GATE_H +#define FIMC_IS_CLK_GATE_H + +#include +#include +#include +#include + +#include "fimc-is-time.h" +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-groupmgr.h" +#include "fimc-is-device-ischain.h" + +int fimc_is_clk_gate_init(struct fimc_is_core *core); +int fimc_is_clk_gate_lock_set(struct fimc_is_core *core, u32 instance, u32 is_start); +int fimc_is_clk_gate_reg_set(struct fimc_is_core *core, + bool is_on, const char* gate_str, u32 clk_gate_id, + struct exynos_fimc_is_clk_gate_info *gate_info); +/* For several groups */ +int fimc_is_wrap_clk_gate_set(struct fimc_is_core *core, + int msk_group_id, bool is_on); +/* For only single group */ +int fimc_is_clk_gate_set(struct fimc_is_core *core, + int group_id, bool is_on, bool skip_set_state, bool user_scenario); + +int fimc_is_set_user_clk_gate(u32 group_id, + struct fimc_is_core *core, + bool is_on, + unsigned long msk_state, + struct exynos_fimc_is_clk_gate_info *gate_info); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-cmd.h b/drivers/media/platform/exynos/fimc-is/fimc-is-cmd.h new file mode 100644 index 000000000000..f54961aeb014 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-cmd.h @@ -0,0 +1,275 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_CMD_H +#define FIMC_IS_CMD_H + +#include "fimc-is-config.h" + +#define IS_COMMAND_VER 132 /* IS COMMAND VERSION 1.32 */ + +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 /* 7 */, + HIC_STREAM_OFF /* 8 */, + HIC_SHOT, + HIC_GET_STATIC_METADATA /* 10 */, + HIC_SET_CAM_CONTROL, + HIC_GET_CAM_CONTROL, + HIC_SET_PARAMETER /* 13 */, + HIC_GET_PARAMETER, + HIC_SET_A5_MAP /* 15 */, + HIC_SET_A5_UNMAP /* 16 */, + HIC_GET_STATUS, + /* SENSOR PART*/ + HIC_OPEN_SENSOR, + HIC_CLOSE_SENSOR, + HIC_SIMMIAN_INIT /* 20 */, + HIC_SIMMIAN_WRITE, + HIC_SIMMIAN_READ, + HIC_POWER_DOWN, + HIC_GET_SET_FILE_ADDR, + HIC_LOAD_SET_FILE, + HIC_MSG_CONFIG, + HIC_MSG_TEST, + HIC_ISP_I2C_CONTROL, + HIC_CALIBRATE_ACTUATOR, + HIC_GET_IP_STATUS /* 30 */, + HIC_I2C_CONTROL_LOCK, +#if (SUPPORTED_IS_CMD_VER >= 132) + HIC_SYSTEM_CONTROL, + HIC_SENSOR_MODE_CHANGE, +#elif (SUPPORTED_IS_CMD_VER >= 131) + HIC_SENSOR_MODE_CHANGE, +#endif + HIC_ADJUST_SET_FILE, + HIC_CHECK_A5_TASK_CPU_USAGE, + HIC_COMMAND_END, + + /* 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, +#if (SUPPORTED_IS_CMD_VER >= 131) + IHC_REPORT_ERR, +#endif + IHC_COMMAND_END +}; + +/* supported command macros by F/W version */ +#define FW_HAS_SYS_CTRL_CMD (SUPPORTED_IS_CMD_VER >= 132) +#define FW_HAS_SENSOR_MODE_CMD (SUPPORTED_IS_CMD_VER >= 131) +#define FW_HAS_REPORT_ERR_CMD (SUPPORTED_IS_CMD_VER >= 131) + +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_PREVIEW = 0, /* 0: still preview */ + ISS_SUB_SCENARIO_VIDEO = 1, /* 1: video */ + ISS_SUB_SCENARIO_DUAL_STILL = 2, /* 2: dual still preview */ + ISS_SUB_SCENARIO_DUAL_VIDEO = 3, /* 3: dual video */ + ISS_SUB_SCENARIO_VIDEO_HIGH_SPEED = 4, /* 4: video high speed */ + ISS_SUB_SCENARIO_STILL_CAPTURE = 5, /* 5: still capture */ + ISS_SUB_SCENARIO_FHD_60FPS = 6, /* 6: video FHD 60fps */ + ISS_SUB_SCENARIO_UHD_30FPS = 7, /* 7: video UHD 30fps */ + ISS_SUB_SCENARIO_WVGA_300FPS = 8, /* 8: video WVGA 300fps */ + ISS_SUB_SCENARIO_STILL_PREVIEW_WDR = 9, + ISS_SUB_SCENARIO_VIDEO_WDR = 10, + ISS_SUB_SCENARIO_STILL_CAPTURE_WDR = 11, + ISS_SUB_SCENARIO_UHD_30FPS_WDR = 12, + ISS_SUB_SCENARIO_STILL_CAPTURE_ZOOM = 13, + ISS_SUB_SCENARIO_STILL_CAPTURE_ZOOM_OUTDOOR = 14, + ISS_SUB_SCENARIO_STILL_CAPTURE_ZOOM_INDOOR = 15, + ISS_SUB_SCENARIO_STILL_CAPTURE_WDR_ZOOM = 16, + ISS_SUB_SCENARIO_STILL_CAPTURE_WDR_ZOOM_OUTDOOR = 17, + ISS_SUB_SCENARIO_STILL_CAPTURE_WDR_ZOOM_INDOOR = 18, + ISS_SUB_SCENARIO_STILL_CAPTURE_LLS = 19, + ISS_SUB_SCENARIO_STILL_CAPTURE_WDR_LLS = 20, + ISS_SUB_END, + + /* These values will be deprecated */ + ISS_SUB_SCENARIO_FRONT_VT1 = 4, /* 4: front camera VT1 for 3G (Temporary) */ + ISS_SUB_SCENARIO_FRONT_VT2 = 5, /* 5: front camera VT2 for LTE (Temporary) */ + ISS_SUB_SCENARIO_FRONT_SMART_STAY = 6, /* 6: front camera smart stay (Temporary) */ + ISS_SUB_SCENARIO_FRONT_PANORAMA = 7, /* 7: front camera front panorama (Temporary) */ +}; + +enum is_system_control_id { + IS_SYS_CLOCK_GATE = 0, + IS_SYS_END = 1, +}; + +enum is_system_control_cmd { + SYS_CONTROL_DISABLE = 0, + SYS_CONTROL_ENABLE = 1, +}; + +enum is_msg_test_id { + IS_MSG_TEST_SYNC_LOG = 1, +}; + +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]; +}; + +#define HOST_SET_INT_BIT 0x00000001 +#define HOST_CLR_INT_BIT 0x00000001 +#define IS_SET_INT_BIT 0x00000001 +#define IS_CLR_INT_BIT 0x00000001 + +#define HOST_SET_INTERRUPT(base) (base->uiINTGR0 |= HOST_SET_INT_BIT) +#define HOST_CLR_INTERRUPT(base) (base->uiINTCR0 |= HOST_CLR_INT_BIT) +#define IS_SET_INTERRUPT(base) (base->uiINTGR1 |= IS_SET_INT_BIT) +#define IS_CLR_INTERRUPT(base) (base->uiINTCR1 |= IS_CLR_INT_BIT) + +struct is_common_reg { + u32 hicmd; + u32 hic_sensorid; + u32 hic_param1; + u32 hic_param2; + u32 hic_param3; + u32 hic_param4; + + u32 power_down_debug_number; + + u32 reserved1[2]; + + u32 ihcmd_iflag; + u32 ihcmd; + u32 ihc_sensorid; + u32 ihc_param1; + u32 ihc_param2; + u32 ihc_param3; + u32 ihc_param4; + + u32 reserved2[2]; + + u32 taa0c_iflag; + u32 taa0c_sensor_id; + u32 taa0c_param1; + u32 taa0c_param2; + u32 taa0c_param3; + + u32 reserved3[2]; + + u32 taa1c_iflag; + u32 taa1c_sensor_id; + u32 taa1c_param1; + u32 taa1c_param2; + u32 taa1c_param3; + + u32 reserved4[2]; + + u32 scc_iflag; + u32 scc_sensor_id; + u32 scc_param1; + u32 scc_param2; + u32 scc_param3; + + u32 reserved5[2]; + + u32 dis_iflag; + u32 dis_sensor_id; + u32 dis_param1; + u32 dis_param2; + u32 dis_param3; + + u32 reserved6[2]; + + u32 scp_iflag; + u32 scp_sensor_id; + u32 scp_param1; + u32 scp_param2; + u32 scp_param3; + + u32 reserved7[3]; + + u32 shot_iflag; + u32 shot_sensor_id; + u32 shot_param1; + u32 shot_param2; + u32 shot_param3; + + u32 grp1_done_frame_num; + + u32 fcount_sen3; + u32 fcount_sen2; + u32 fcount_sen1; + u32 fcount_sen0; +}; + +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/exynos/fimc-is/fimc-is-companion-dt.c b/drivers/media/platform/exynos/fimc-is/fimc-is-companion-dt.c new file mode 100644 index 000000000000..d52c9d4f8f0a --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-companion-dt.c @@ -0,0 +1,223 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif + +#include "fimc-is-dt.h" +#include "fimc-is-companion-dt.h" + +#ifdef CONFIG_OF +int fimc_is_sensor_parse_dt_with_companion(struct platform_device *pdev) +{ + int ret = 0; + u32 temp; + char *pprop; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + struct device *dev; + const char *name; + u32 id; + + BUG_ON(!pdev); + BUG_ON(!pdev->dev.of_node); + + dev = &pdev->dev; + dnode = dev->of_node; + + pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_sensor), GFP_KERNEL); + if (!pdata) { + pr_err("%s: no memory for platform data\n", __func__); + return -ENOMEM; + } + + pdata->gpio_cfg = exynos_fimc_is_sensor_pins_cfg; + pdata->iclk_cfg = exynos_fimc_is_sensor_iclk_cfg; + pdata->iclk_on = exynos_fimc_is_sensor_iclk_on; + pdata->iclk_off = exynos_fimc_is_sensor_iclk_off; + pdata->mclk_on = exynos_fimc_is_sensor_mclk_on; + pdata->mclk_off = exynos_fimc_is_sensor_mclk_off; + + ret = of_property_read_u32(dnode, "scenario", &pdata->scenario); + if (ret) { + err("scenario read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch); + if (ret) { + err("mclk_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "csi_ch", &pdata->csi_ch); + if (ret) { + err("csi_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "flite_ch", &pdata->flite_ch); + if (ret) { + err("flite_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "i2c_ch", &pdata->i2c_ch); + if (ret) { + err("i2c_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "i2c_addr", &pdata->i2c_addr); + if (ret) { + err("i2c_addr read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "is_bns", &pdata->is_bns); + if (ret) { + err("is_bns read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "id", &id); + if (ret) { + err("id read is fail(%d)", ret); + goto p_err; + } + pdata->id = id; + + DT_READ_U32(dnode, "flash_first_gpio", pdata->flash_first_gpio); + DT_READ_U32(dnode, "flash_second_gpio", pdata->flash_second_gpio); + + ret = of_property_read_string(dnode, "sensor_name", &name); + if (ret) { + err("sensor_name read is fail(%d)", ret); + goto p_err; + } + strcpy(pdata->sensor_name, name); + + ret = of_property_read_u32(dnode, "sensor_id", &pdata->sensor_id); + if (ret) { + err("sensor_id read is fail(%d)", ret); + goto p_err; + } + + dev->platform_data = pdata; + + if (id == SENSOR_POSITION_REAR) + ret = fimc_is_power_initpin(dev); + else { + ret = fimc_is_power_setpin(dev, id, pdata->sensor_id); + } + if (ret) + err("power_setpin(or initpin) failed(%d). id %d", ret, id); + + pdev->id = id; + + pdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pdata->pinctrl)) { + err("devm_pinctrl_get is fail"); + goto p_err; + } else { + ret = get_pin_lookup_state(dev, pdata); + if (ret < 0) { + err("fimc_is_get_pin_lookup_state is fail"); + goto p_err; + } + } + + return ret; +p_err: + kfree(pdata); + return ret; +} + +int fimc_is_companion_parse_dt(struct platform_device *pdev) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + struct device *dev; + + BUG_ON(!pdev); + BUG_ON(!pdev->dev.of_node); + + dev = &pdev->dev; + dnode = dev->of_node; + + pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_sensor), GFP_KERNEL); + if (!pdata) { + pr_err("%s: no memory for platform data\n", __func__); + return -ENOMEM; + } + + pdata->gpio_cfg = exynos_fimc_is_sensor_pins_cfg; + pdata->iclk_cfg = exynos_fimc_is_companion_iclk_cfg; + pdata->iclk_on = exynos_fimc_is_companion_iclk_on; + pdata->iclk_off = exynos_fimc_is_companion_iclk_off; + pdata->mclk_on = exynos_fimc_is_companion_mclk_on; + pdata->mclk_off = exynos_fimc_is_companion_mclk_off; + + ret = of_property_read_u32(dnode, "scenario", &pdata->scenario); + if (ret) { + err("scenario read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch); + if (ret) { + err("mclk_ch read is fail(%d)", ret); + goto p_err; + } + + pdata->companion_use_pmic = of_property_read_bool(dnode, "companion_use_pmic"); + if (!pdata->companion_use_pmic) { + err("use_pmic not use(%d)", pdata->companion_use_pmic); + } + + ret = of_property_read_u32(dnode, "sensor_id", &pdata->sensor_id); + if (ret) { + err("sensor_id read is fail(%d)", ret); + goto p_err; + } + + dev->platform_data = pdata; + + ret = fimc_is_power_setpin(dev, SENSOR_POSITION_REAR, pdata->sensor_id); + if (ret) + err("power_setpin failed(%d)", ret); + + pdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pdata->pinctrl)) { + err("devm_pinctrl_get is fail"); + goto p_err; + } else { + ret = get_pin_lookup_state(dev, pdata); + if (ret < 0) { + err("fimc_is_get_pin_lookup_state is fail"); + goto p_err; + } + } + + return ret; +p_err: + kfree(pdata); + return ret; +} +#endif + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-companion-dt.h b/drivers/media/platform/exynos/fimc-is/fimc-is-companion-dt.h new file mode 100644 index 000000000000..f3746c7cc89b --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-companion-dt.h @@ -0,0 +1,18 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_COMPANION_DT_H +#define FIMC_IS_COMPANION_DT_H + +int fimc_is_sensor_parse_dt_with_companion(struct platform_device *pdev); +int fimc_is_companion_parse_dt(struct platform_device *pdev); +#endif + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-companion.c b/drivers/media/platform/exynos/fimc-is/fimc-is-companion.c new file mode 100644 index 000000000000..691d0afc0faa --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-companion.c @@ -0,0 +1,1409 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "fimc-is-sec-define.h" +#include "fimc-is-companion.h" +#include "fimc-is-device-ischain.h" +#include "fimc-is-fan53555.h" +#ifdef USE_ION_ALLOC +#include +#include +#include +#endif + +#define COMP_FW_EVT0 "companion_fw_evt0.bin" +#define COMP_FW_EVT1 "companion_fw_evt1.bin" +#define COMP_SETFILE_MASTER "companion_master_setfile.bin" +#define COMP_SETFILE_MODE "companion_mode_setfile.bin" +#define COMP_CAL_DATA "cal" +#define COMP_LSC "lsc" +#define COMP_PDAF "pdaf" +#define COMP_COEF_CAL "coef" +#define COMP_DEFAULT_LSC "companion_default_lsc.bin" +#define COMP_DEFAULT_COEF "companion_default_coef.bin" + +#define COMP_SETFILE_VIRSION_SIZE 16 +#define COMP_MAGIC_NUMBER (0x73c1) +#define FIMC_IS_COMPANION_LSC_SIZE 6600 +#define FIMC_IS_COMPANION_PDAF_SIZE 512 +#define FIMC_IS_COMPANION_COEF_TOTAL_SIZE (4096 * 6) +#define FIMC_IS_COMPANION_LSC_I0_SIZE 2 +#define FIMC_IS_COMPANION_LSC_J0_SIZE 2 +#define FIMC_IS_COMPANION_LSC_A_SIZE 4 +#define FIMC_IS_COMPANION_LSC_K4_SIZE 4 +#define FIMC_IS_COMPANION_LSC_SCALE_SIZE 2 +#define FIMC_IS_COMPANION_WCOEF1_SIZE 10 +#define FIMC_IS_COMPANION_COEF_CAL_SIZE 4032 +#define FIMC_IS_COMPANION_AF_INF_SIZE 2 +#define FIMC_IS_COMPANION_AF_MACRO_SIZE 2 +#define LITTLE_ENDIAN 0 +#define BIG_ENDIAN 1 +extern bool companion_lsc_isvalid; +extern bool companion_coef_isvalid; +extern bool crc32_c1_check; +static u16 companion_ver; +static u32 concord_fw_size; +char companion_crc[10]; + +#if 1 +static int fimc_is_comp_spi_read(struct spi_device *spi, + void *buf, u16 rx_addr, size_t size) +{ + unsigned char req_data[4] = { 0x03, }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + req_data[1] = (rx_addr & 0xFF00) >> 8; + req_data[2] = (rx_addr & 0xFF); + + t_c.tx_buf = req_data; + t_c.len = 4; + t_c.cs_change = 1; + t_c.bits_per_word = 32; + + t_r.rx_buf = buf; + t_r.len = size; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + spi_message_add_tail(&t_r, &m); + + ret = spi_sync(spi, &m); + if (ret) { + err("spi sync error - can't read data"); + return -EIO; + } else + return 0; +} +#endif + +static int fimc_is_comp_spi_single_write(struct spi_device *spi, u16 addr, u16 data) +{ + int ret = 0; + u8 tx_buf[5]; + + tx_buf[0] = 0x02; /* write cmd */ + tx_buf[1] = (addr >> 8) & 0xFF; /* address */ + tx_buf[2] = (addr >> 0) & 0xFF; /* address */ + tx_buf[3] = (data >> 8) & 0xFF; /* data */ + tx_buf[4] = (data >> 0) & 0xFF; /* data */ + + ret = spi_write(spi, &tx_buf[0], 5); + if (ret) + err("spi sync error - can't read data"); + + return ret; +} + +/* Burst mode:
+ * Burst width: Maximun value is 512. + */ +static int fimc_is_comp_spi_burst_write(struct spi_device *spi, + u8 *buf, size_t size, size_t burst_width, int endian) +{ + int ret = 0; + u32 i = 0, j = 0; + u8 tx_buf[512]; + size_t burst_size; + + /* check multiples of 2 */ + burst_width = (burst_width + 2 - 1) / 2 * 2; + + burst_size = size / burst_width * burst_width; + + for (i = 0; i < burst_size; i += burst_width) { + tx_buf[0] = 0x02; /* write cmd */ + tx_buf[1] = 0x6F; /* address */ + tx_buf[2] = 0x12; /* address */ + if (endian == LITTLE_ENDIAN) { + for (j = 0; j < burst_width; j++) + tx_buf[j + 3] = *(buf + i + j); /* data for big endian */ + } else if (endian == BIG_ENDIAN) { + for (j = 0; j < burst_width; j += 2) { + tx_buf[j + 3] = *(buf + i + j + 1); /* data for little endian */ + tx_buf[j + 4] = *(buf + i + j); + } + } + + ret = spi_write(spi, &tx_buf[0], j + 3); + if (ret) { + err("spi write error - can't write data"); + goto p_err; + } + } + + tx_buf[0] = 0x02; /* write cmd */ + tx_buf[1] = 0x6F; /* address */ + tx_buf[2] = 0x12; /* address */ + if (endian == LITTLE_ENDIAN) { + for (j = 0; j < (size - burst_size); j ++) { + tx_buf[j + 3] = *(buf + i + j); /* data for big endian */ + } + } else if (endian == BIG_ENDIAN) { + for (j = 0; j < (size - burst_size); j += 2) { + tx_buf[j + 3] = *(buf + i + j + 1); /* data for little endian */ + tx_buf[j + 4] = *(buf + i + j); + } + } + + ret = spi_write(spi, &tx_buf[0], j + 3); + if (ret) + err("spi write error - can't write data"); + + +p_err: + return ret; +} + +#if 0 +static int fimc_is_comp_i2c_read(struct i2c_client *client, u16 addr, u16 *data) +{ + int err; + u8 rxbuf[2], txbuf[2]; + struct i2c_msg msg[2]; + + rxbuf[0] = (addr & 0xff00) >> 8; + rxbuf[1] = (addr & 0xff); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = rxbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = txbuf; + + err = i2c_transfer(client->adapter, msg, 2); + if (unlikely(err != 2)) { + pr_err("%s: register read fail\n", __func__); + return -EIO; + } + + *data = ((txbuf[0] << 8) | txbuf[1]); + return 0; +} +#endif + +#ifndef USE_SPI +static int fimc_is_comp_i2c_write(struct i2c_client *client ,u16 addr, u16 data) +{ + int retries = I2C_RETRY_COUNT; + int ret = 0, err = 0; + u8 buf[4] = {0,}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 4, + .buf = buf, + }; + + buf[0] = addr >> 8; + buf[1] = addr; + buf[2] = data >> 8; + buf[3] = data & 0xff; + +#if 0 + pr_info("%s : W(0x%02X%02X%02X%02X)\n",__func__, buf[0], buf[1], buf[2], buf[3]); +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + + usleep_range(10000,11000); + err = ret; + } while (--retries > 0); + + /* Retry occured */ + if (unlikely(retries < I2C_RETRY_COUNT)) { + pr_err("i2c_write: error %d, write (%04X, %04X), retry %d\n", + err, addr, data, I2C_RETRY_COUNT - retries); + } + + if (unlikely(ret != 1)) { + pr_err("I2C does not work\n\n"); + return -EIO; + } + + return 0; +} +#endif + +static int fimc_is_comp_single_write(struct fimc_is_core *core , u16 addr, u16 data) +{ + int ret = 0; +#ifdef USE_SPI + struct spi_device *spi = core->spi1; + ret = fimc_is_comp_spi_single_write(spi, addr, data); + if (ret) { + err("spi_single_write() fail"); + } +#else + struct i2c_client *client = core->client0; + fimc_is_s_int_comb_isp(core, true, INTMR2_INTMCIS22); /* interrupt on */ + ret = fimc_is_comp_i2c_write(client, addr, data); + if (ret) { + err("i2c write fail"); + } + fimc_is_s_int_comb_isp(core, false, INTMR2_INTMCIS22); /* interrupt off */ +#endif + + return ret; +} + +static int fimc_is_comp_single_read(struct fimc_is_core *core , u16 addr, u16 *data, size_t size) +{ + int ret = 0; +//#ifdef USE_SPI +#if 1 + struct spi_device *spi = core->spi1; + ret = fimc_is_comp_spi_read(spi, (void *)data, addr, size); + if (ret) { + err("spi_single_read() fail"); + } + *data = *data << 8 | *data >> 8; +#else + struct i2c_client *client = core->client0; + fimc_is_s_int_comb_isp(core, true, INTMR2_INTMCIS22); /* I2C0 interrupt on */ + ret = fimc_is_comp_i2c_read(client, addr, data); + if (ret) { + err("i2c read fail"); + } + fimc_is_s_int_comb_isp(core, false, INTMR2_INTMCIS22); /* I2C0 interrupt off */ +#endif + + return ret; +} + +static int fimc_is_comp_check_crc32(struct fimc_is_core *core, char *name) +{ + int ret = 0; + int retries = CRC_RETRY_COUNT; + u32 checksum, from_checksum = 0; + u16 addr1, addr2, read_addr, data_size1, data_size2; + u16 result_data, cmd_data, temp_data; + struct fimc_is_from_info *sysfs_finfo; + char *cal_buf; + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + fimc_is_sec_get_cal_buf(&cal_buf); + + if (!strcmp(name, COMP_LSC)) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_LSC_SIZE; + addr1 = 0x2001; + addr2 = 0xA000; + if (companion_lsc_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->lsc_gain_crc_addr]); + else + from_checksum = 0x043DC8F8; + } else if (!strcmp(name, COMP_PDAF)) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_PDAF_SIZE; + addr1 = 0x2000; + addr2 = 0xB900; + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->pdaf_crc_addr]); + } else if (!strcmp(name, COMP_FW_EVT1) || !strcmp(name, COMP_FW_EVT0)) { + concord_fw_size = concord_fw_size - 4; + data_size1 = (concord_fw_size >> 16) & 0xFFFF; + data_size2 = concord_fw_size & 0xFFFF; + addr1 = 0x0000; + addr2 = 0x0000; + from_checksum = *((u32 *)&companion_crc[0]); + } else if (!strcmp(name, COMP_COEF_CAL)) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x0000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef1_crc_addr]); + else + from_checksum = 0x98E85DAC; + } else if (!strcmp(name, "coef2")) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x1000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef2_crc_addr]); + else + from_checksum = 0x88358F49; + } else if (!strcmp(name, "coef3")) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x2000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef3_crc_addr]); + else + from_checksum = 0x138AF2AB; + } else if (!strcmp(name, "coef4")) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x3000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef4_crc_addr]); + else + from_checksum = 0x4447017A; + } else if (!strcmp(name, "coef5")) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x4000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef5_crc_addr]); + else + from_checksum = 0xDBB05433; + } else if (!strcmp(name, "coef6")) { + data_size1 = 0; + data_size2 = FIMC_IS_COMPANION_COEF_CAL_SIZE; + addr1 = 0x2001; + addr2 = 0x5000; + if (companion_coef_isvalid) + from_checksum = *((u32 *)&cal_buf[sysfs_finfo->coef6_crc_addr]); + else + from_checksum = 0x66064DFA; + } else { + err("wrong companion cal data name\n"); + return -EINVAL; + } + + ret = fimc_is_comp_single_write(core, 0x0024, 0x0000); + ret |= fimc_is_comp_single_write(core, 0x0026, 0x0000);//clear result register + ret |= fimc_is_comp_single_write(core, 0x0014, addr1); + ret |= fimc_is_comp_single_write(core, 0x0016, addr2);// source address + ret |= fimc_is_comp_single_write(core, 0x0018, data_size1); + ret |= fimc_is_comp_single_write(core, 0x001A, data_size2);//source size + ret |= fimc_is_comp_single_write(core, 0x000C, 0x000C); + ret |= fimc_is_comp_single_write(core, 0x6806, 0x0001);//interrupt on + if (ret) { + err("fimc_is_comp_check_crc32() i2c write fail"); + return -EIO; + } + + read_addr = 0x000C; + do { + fimc_is_comp_single_read(core, read_addr, &cmd_data, 2); + usleep_range(500, 500); + if (--retries < 0) { + err("Read register failed!!!!, data = 0x%04x\n", cmd_data); + break; + } + } while (cmd_data); + read_addr = 0x0024; + fimc_is_comp_single_read(core, read_addr, &result_data, 2); + read_addr = 0x0026; + fimc_is_comp_single_read(core, read_addr, &temp_data, 2); + + checksum = result_data << 16 | temp_data; + if (checksum != from_checksum) { + err("[%s] CRC check is failed. Checksum = 0x%08x, FROM checksum = 0x%08x\n", + name, checksum, from_checksum); + return -EIO; + } + + return ret; +} + +static int fimc_is_comp_load_binary(struct fimc_is_core *core, char *name) +{ + int ret = 0; + u32 size = 0; + const struct firmware *fw_blob = NULL; + static char fw_name[100]; + struct file *fp = NULL; + mm_segment_t old_fs; + long nread; + int fw_requested = 1; + u32 i; + u8 *buf = NULL; + u32 data, cal_addr; + char version_str[60]; + u16 addr1, addr2; + char companion_ver[12] = {0, }; + struct fimc_is_from_info *sysfs_finfo; +#ifdef USE_ION_ALLOC + struct ion_handle *handle = NULL; +#endif + int retry_count = 0; + + BUG_ON(!core); + BUG_ON(!core->pdev); + BUG_ON(!core->companion); + BUG_ON(!core->companion->pdev); + BUG_ON(!name); +#ifdef USE_ION_ALLOC + BUG_ON(!core->fimc_ion_client); +#endif + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + snprintf(fw_name, sizeof(fw_name), "%s%s",FIMC_IS_SETFILE_SDCARD_PATH, name); + fp = filp_open(fw_name, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { + goto request_fw; + } + + fw_requested = 0; + size = fp->f_path.dentry->d_inode->i_size; + pr_info("start read sdcard, file path %s, size %d Bytes\n", fw_name, size); + +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)size, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("fimc_is_comp_load_binary:failed to ioc_alloc\n"); + ret = -ENOMEM; + goto p_err; + } + + buf = (u8 *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("fimc_is_comp_load_binary:fail to ion_map_kernle\n"); + ret = -ENOMEM; + goto p_err; + } +#else + buf = vmalloc(size); + if (!buf) { + err("failed to allocate memory"); + ret = -ENOMEM; + goto p_err; + } +#endif + + nread = vfs_read(fp, (char __user *)buf, size, &fp->f_pos); + if (nread != size) { + err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto p_err; + } + if ((!strcmp(name, COMP_FW_EVT0)) || (!strcmp(name, sysfs_finfo->load_c1_fw_name))) { + strncpy(companion_crc, buf+nread-4, 4); + strncpy(companion_ver, buf+nread - 16, 11); + } + +request_fw: + if (fw_requested) { + snprintf(fw_name, sizeof(fw_name), "%s", name); + set_fs(old_fs); + retry_count = 3; + ret = request_firmware(&fw_blob, fw_name, &core->companion->pdev->dev); + while (--retry_count && ret == -EAGAIN) { + err("request_firmware retry(count:%d)", retry_count); + ret = request_firmware(&fw_blob, fw_name, &core->companion->pdev->dev); + } + + if (ret) { + err("request_firmware is fail(ret:%d)", ret); + ret = -EINVAL; + goto p_err; + } + + if (!fw_blob) { + err("fw_blob is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (!fw_blob->data) { + err("fw_blob->data is NULL"); + ret = -EINVAL; + goto p_err; + } + + size = fw_blob->size; +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)size, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("fimc_is_comp_load_binary:failed to ioc_alloc\n"); + ret = -ENOMEM; + goto p_err; + } + + buf = (u8 *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("fimc_is_comp_load_binary:fail to ion_map_kernle\n"); + ret = -ENOMEM; + goto p_err; + } +#else + buf = vmalloc(size); + if (!buf) { + err("failed to allocate memory"); + ret = -ENOMEM; + goto p_err; + } +#endif + memcpy((void *)buf, fw_blob->data, size); + if ((!strcmp(name, COMP_FW_EVT0)) || (!strcmp(name, sysfs_finfo->load_c1_fw_name))) { + memcpy((void *)companion_crc, fw_blob->data + size - 4, 4); + memcpy((void *)companion_ver, fw_blob->data + size - 16, 11); + } + } + + if (!strcmp(name, COMP_FW_EVT0)) { + ret = fimc_is_comp_spi_burst_write(core->spi1, buf, size, 256, LITTLE_ENDIAN); + if (ret) { + err("fimc_is_comp_spi_write() fail"); + goto p_err; + } + concord_fw_size = size; + info("%s version : %s\n", name, companion_ver); + } else if (!strcmp(name, sysfs_finfo->load_c1_fw_name)) { + ret = fimc_is_comp_spi_burst_write(core->spi1, buf, size, 256, LITTLE_ENDIAN); + if (ret) { + err("fimc_is_comp_spi_write() fail"); + goto p_err; + } + concord_fw_size = size; + info("%s version : %s\n", name, companion_ver); + } else if (!strcmp(name, COMP_DEFAULT_LSC)) { + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + cal_addr = MEM_GRAS_B_IMX240; + } else { + cal_addr = MEM_GRAS_B_2P2; + } + addr1 = (cal_addr >> 16) & 0xFFFF; + addr2 = cal_addr & 0xFFFF; + ret = fimc_is_comp_single_write(core, 0x6428, addr1); + if (ret) { + err("fimc_is_comp_load_cal() write fail"); + } + ret = fimc_is_comp_single_write(core, 0x642A, addr2); + if (ret) { + err("fimc_is_comp_load_cal() write fail"); + } + ret = fimc_is_comp_spi_burst_write(core->spi1, buf, size, 256, BIG_ENDIAN); + if (ret) { + err("fimc_is_comp_spi_write() fail"); + goto p_err; + } + } else if (!strcmp(name, COMP_DEFAULT_COEF)) { + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + cal_addr = MEM_XTALK_10_IMX240; + } else { + cal_addr = MEM_XTALK_10_2P2; + } + addr1 = (cal_addr >> 16) & 0xFFFF; + addr2 = cal_addr & 0xFFFF; + ret = fimc_is_comp_single_write(core, 0x6428, addr1); + if (ret) { + err("fimc_is_comp_load_cal() write fail"); + } + ret = fimc_is_comp_single_write(core, 0x642A, addr2); + if (ret) { + err("fimc_is_comp_load_cal() write fail"); + } + ret = fimc_is_comp_spi_burst_write(core->spi1, buf, size, 256, LITTLE_ENDIAN); + if (ret) { + err("fimc_is_comp_spi_write() fail"); + goto p_err; + } + } else { + u32 offset = size - COMP_SETFILE_VIRSION_SIZE; + for (i = 0; i < offset; i += 4) { + data = *(buf + i + 0) << 24 | + *(buf + i + 1) << 16 | + *(buf + i + 2) << 8 | + *(buf + i + 3) << 0; + if(!strcmp(name, sysfs_finfo->load_c1_mastersetf_name)) { + ret = fimc_is_comp_spi_single_write(core->spi1, (data >> 16), (u16)data); + if (ret) { + err("fimc_is_comp_spi_setf_write() fail"); + break; + } + } else { + ret = fimc_is_comp_single_write(core, (data >> 16), (u16)data); + if (ret) { + err("fimc_is_comp_write() fail"); + break; + } + } + } + + memcpy(version_str, buf + offset, COMP_SETFILE_VIRSION_SIZE); + version_str[COMP_SETFILE_VIRSION_SIZE] = '\0'; + + info("%s version : %s\n", name, version_str); + } + +p_err: +#ifdef USE_ION_ALLOC + if (!IS_ERR_OR_NULL(buf)) { + ion_unmap_kernel(core->fimc_ion_client, handle); + } + + if (!IS_ERR_OR_NULL(handle)) { + ion_free(core->fimc_ion_client, handle); + } +#else + if (buf) { + vfree(buf); + } +#endif + if (!fw_requested) { + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, current->files); + } + set_fs(old_fs); + } else { + if (!IS_ERR_OR_NULL(fw_blob)) { + release_firmware(fw_blob); + } + } + return ret; +} + +static int fimc_is_comp_load_cal(struct fimc_is_core *core, char *name) +{ + + int ret = 0, endian; + u32 data_size = 0, offset; + struct fimc_is_from_info *sysfs_finfo; + char *cal_buf; + u16 data1, data2; + u32 comp_addr = 0; + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + fimc_is_sec_get_cal_buf(&cal_buf); + pr_info("Camera: SPI write cal data, name = %s\n", name); + + if (!strcmp(name, COMP_LSC)) { + data_size = FIMC_IS_COMPANION_LSC_SIZE; + offset = sysfs_finfo->lsc_gain_start_addr; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = MEM_GRAS_B_IMX240; + } else { + comp_addr = MEM_GRAS_B_2P2; + } + endian = BIG_ENDIAN; + } else if (!strcmp(name, COMP_PDAF)) { + data_size = FIMC_IS_COMPANION_PDAF_SIZE; + offset = sysfs_finfo->pdaf_start_addr; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = MEM_AF_10_1_IMX240; + } else { + comp_addr = MEM_AF_10_1_2P2; + } + endian = LITTLE_ENDIAN; + } else if (!strcmp(name, COMP_COEF_CAL)) { + data_size = FIMC_IS_COMPANION_COEF_TOTAL_SIZE; + offset = sysfs_finfo->coef1_start; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = MEM_XTALK_10_IMX240; + } else { + comp_addr = MEM_XTALK_10_2P2; + } + endian = LITTLE_ENDIAN; + } else { + err("wrong companion cal data name\n"); + return -EINVAL; + } + data1 = comp_addr >> 16; + data2 = (u16)comp_addr; + ret = fimc_is_comp_single_write(core, 0x6428, data1); + if (ret) { + err("fimc_is_comp_load_cal() i2c write fail"); + } + ret = fimc_is_comp_single_write(core, 0x642A, data2); + if (ret) { + err("fimc_is_comp_load_cal() i2c write fail"); + } + + ret = fimc_is_comp_spi_burst_write(core->spi1, cal_buf + offset, data_size, 256, endian); + if (ret) { + err("fimc_is_comp_spi_write_cal() fail\n"); + } + return ret; +} + +#if 0 +static int fimc_is_comp_read_i2c_cal(struct fimc_is_core *core, u32 addr) +{ + int ret = 0; + u32 i,data_size = 0; + u16 data1, data2; + u8 read_data[2]; + + struct fimc_is_from_info *sysfs_finfo; + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + + fimc_is_s_int_comb_isp(core, true, INTMR2_INTMCIS22); /* interrupt on */ + if (addr == sysfs_finfo->lsc_i0_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_I0_SIZE; + data1 = 0x0EF8; + } else if (addr == sysfs_finfo->lsc_j0_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_J0_SIZE; + data1 = 0x0EFA; + } else if (addr == sysfs_finfo->lsc_a_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_A_SIZE; + data1 = 0x0F00; + } else if (addr == sysfs_finfo->lsc_k4_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_K4_SIZE; + data1 = 0x0F04; + } else if (addr == sysfs_finfo->lsc_scale_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_SCALE_SIZE; + data1 = 0x0F08; + } else if (addr == sysfs_finfo->wcoefficient1_addr) { + data_size = FIMC_IS_COMPANION_WCOEF1_SIZE; + data1 = 0x1420; + } else { + err("wrong companion cal data addr\n"); + return -EINVAL; + } + pr_info("===Camera: I2C read cal data, addr [0x%04x], size(%d)\n", addr,data_size); + ret = fimc_is_comp_i2c_write(core->client0, 0x642C, 0x4000); + if (ret) { + err("fimc_is_comp_load_i2c_cal() i2c write fail"); + } + ret = fimc_is_comp_i2c_write(core->client0, 0x642E, data1); + if (ret) { + err("fimc_is_comp_load_i2c_cal() i2c write fail"); + } + + for (i = 0; i < data_size; i += 2) { + fimc_is_comp_spi_read(core->spi1, (void *)read_data, data1, 2); + data2 = read_data[0] << 8 | read_data[1] << 0; +// pr_info("===Camera: I2C read addr[0x%04x],data[0x%04x]\n", data1,data2); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + break; + } + data1 += 2; + } + + fimc_is_s_int_comb_isp(core, false, INTMR2_INTMCIS22); /* interrupt off */ + + return ret; +} +#endif + +static int fimc_is_comp_load_i2c_cal(struct fimc_is_core *core, u32 addr) +{ + int ret = 0; + u8 *buf = NULL; + u32 i, j, data_size = 0; + u16 data1, data2, data3 = 0, data4 = 0xFFFF; + u32 comp_addr = 0; + + struct fimc_is_from_info *sysfs_finfo; + char *cal_buf; + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + fimc_is_sec_get_cal_buf(&cal_buf); + + if (addr == sysfs_finfo->lsc_i0_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_I0_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_uParabolicCenterX_IMX240; + } else { + comp_addr = grasTuning_uParabolicCenterX_2P2; + } + if (!companion_lsc_isvalid) + data3 = 0x780A; + } else if (addr == sysfs_finfo->lsc_j0_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_J0_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_uParabolicCenterY_IMX240; + } else { + comp_addr = grasTuning_uParabolicCenterY_2P2; + } + if (!companion_lsc_isvalid) + data3 = 0xEC05; + } else if (addr == sysfs_finfo->lsc_a_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_A_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_uBiQuadFactorA_IMX240; + } else { + comp_addr = grasTuning_uBiQuadFactorA_2P2; + } + if (!companion_lsc_isvalid) { + data3 = 0x6A2A; + data4 = 0x0100; + } + } else if (addr == sysfs_finfo->lsc_k4_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_K4_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_uBiQuadFactorB_IMX240; + } else { + comp_addr = grasTuning_uBiQuadFactorB_2P2; + } + if (!companion_lsc_isvalid) { + data3 = 0x0040; + data4 = 0x0000; + } + } else if (addr == sysfs_finfo->lsc_scale_gain_addr) { + data_size = FIMC_IS_COMPANION_LSC_SCALE_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_uBiQuadScaleShiftAdder_IMX240; + } else { + comp_addr = grasTuning_uBiQuadScaleShiftAdder_2P2; + } + if (!companion_lsc_isvalid) + data3 = 0x0600; + } else if (addr == sysfs_finfo->wcoefficient1_addr) { + data_size = FIMC_IS_COMPANION_WCOEF1_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = xtalkTuningParams_wcr_IMX240; + } else { + comp_addr = xtalkTuningParams_wcr_2P2; + } + } else if (addr == sysfs_finfo->af_inf_addr) { + data_size = FIMC_IS_COMPANION_AF_INF_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_actuatorPositionToShadingPowerLut_0_IMX240; + } else { + comp_addr = grasTuning_actuatorPositionToShadingPowerLut_0_2P2; + } + } else if (addr == sysfs_finfo->af_macro_addr) { + data_size = FIMC_IS_COMPANION_AF_MACRO_SIZE; + if (sysfs_finfo->sensor_id == COMPANION_SENSOR_IMX240) { + comp_addr = grasTuning_actuatorPositionToShadingPowerLut_9_IMX240; + } else { + comp_addr = grasTuning_actuatorPositionToShadingPowerLut_9_2P2; + } + } else { + err("wrong companion cal data name\n"); + return -EINVAL; + } + data1 = comp_addr >> 16; + data2 = (u16)comp_addr; + + ret = fimc_is_comp_single_write(core, 0x6028, data1); + if (ret) { + err("fimc_is_comp_load_i2c_cal() i2c write fail"); + } + ret = fimc_is_comp_single_write(core, 0x602A, data2); + if (ret) { + err("fimc_is_comp_load_i2c_cal() i2c write fail"); + } + + buf = cal_buf + addr; + if (addr == sysfs_finfo->af_inf_addr || addr == sysfs_finfo->af_macro_addr) { + data3 = *((u16 *)buf) / 2; + ret = fimc_is_comp_single_write(core, data2, data3); + } else { + if (addr == sysfs_finfo->wcoefficient1_addr) { + if (companion_coef_isvalid) { + for (i = 0; i < data_size; i += 2) { + data3 = (*(buf + i + 1) << 8) | (*(buf + i)); + ret = fimc_is_comp_single_write(core, data2, data3); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + break; + } + data2 += 2; + } + } else { + ret = fimc_is_comp_single_write(core, data2, 0x0203); + ret |= fimc_is_comp_single_write(core, data2 + 2, 0xB902); + ret |= fimc_is_comp_single_write(core, data2 + 4, 0xA903); + ret |= fimc_is_comp_single_write(core, data2 + 6, 0x2900); + ret |= fimc_is_comp_single_write(core, data2 + 8, 0x4900); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + } + } else { + if (companion_lsc_isvalid) { + j = data_size - 1; + for (i = 0; i < data_size; i += 2) { + data3 = (*(buf + j) << 8) | (*(buf + j - 1)); + ret = fimc_is_comp_single_write(core, data2, data3); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + break; + } + data2 += 2; + j -= 2; + } + } else { + ret = fimc_is_comp_single_write(core, data2, data3); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + if (data4 != 0xFFFF) { + data2 += 2; + ret = fimc_is_comp_single_write(core, data2, data4); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + } + } + } + } +// ret = fimc_is_comp_read_i2c_cal(core, addr); + return ret; +} + +int fimc_is_power_binning(struct fimc_is_core *core) +{ + int ret = 0; + int err_check = 0, vout_sel = 0; + u16 read_value = 0x80; + int vout = 0; + char buf[2]={0,}; + loff_t pos = 0; + char *dcdc_name; + struct dcdc_power *dcdc = &core->companion_dcdc; + + if (DCDC_VENDOR_NONE == dcdc->type) { + pr_err("%s: warning, DCDC power not exist\n", __func__); + return -ENODEV; + } else { + dcdc_name = (DCDC_VENDOR_FAN53555 == dcdc->type) ? "FAN" : + (DCDC_VENDOR_NCP6335B == dcdc->type) ? "NCP" : "Unkknown"; + } + + /*read BIN_INFO 0x5000 500C*/ +#if 0 + ret = fimc_is_comp_single_write(core, 0x602C, 0x5000); + if (ret) + err("fimc_is_comp_i2c_setf_write() fail"); + ret = fimc_is_comp_single_write(core, 0x602E, 0x500C); + if (ret) + err("fimc_is_comp_i2c_setf_write() fail"); +#endif + + ret = read_data_from_file(FIMC_IS_ISP_CV, buf, 1, &pos); + if(ret > 0) { + if (kstrtoint(buf, 10, &vout_sel)) + pr_err("%s: error, convert fail\n", __func__); + + vout = dcdc->get_vout_val(vout_sel); + ret = dcdc->set_vout(dcdc->client, vout); + if (ret < 0 ) + pr_err("%s: error, dcdc set_vout(%d). sel %d\n", __func__, ret, vout_sel); + + pr_info("[%s::%d][BIN_INFO::%s, sel %d] read path(%s), DCDC %s\n", + __FUNCTION__, __LINE__, buf, vout_sel, FIMC_IS_ISP_CV, dcdc_name); + return vout_sel; + } + + ret = fimc_is_comp_spi_single_write(core->spi1, 0x642C, 0x5000); + if (ret) { + err_check = 1; + err("spi_single_write() fail"); + } + + ret = fimc_is_comp_spi_single_write(core->spi1, 0x642E, 0x500C); + if (ret) { + err_check = 1; + err("spi_single_write() fail"); + } + + ret = fimc_is_comp_single_read(core, 0x6F12, &read_value, 2); + if (ret) { + err_check = 1; + err("fimc_is_comp_single_read() fail"); + } + + pr_info("[%s::%d][BIN_INFO::0x%04x]\n", __FUNCTION__, __LINE__, read_value); + + if (read_value & 0x3F) { + if (read_value & (1<get_vout_val(vout_sel); + ret = dcdc->set_vout(dcdc->client, vout); + if (ret < 0) + pr_err("%s: error, dcdc set_vout(%d), sel %d\n", __func__, ret, vout_sel); + + if (err_check == 0) { + ret = write_data_to_file(FIMC_IS_ISP_CV, buf, 1, &pos); + if (ret < 0) + pr_err("bin_info_file_write() fail(%s)",buf); + } + + pr_info("[%s::%d][BIN_INFO::0x%04x] buf(%s) write. sel %d, DCDC %s\n", + __FUNCTION__, __LINE__, read_value,buf, vout_sel, dcdc_name); + + return vout_sel; +} + +int fimc_is_comp_is_valid(struct fimc_is_core *core) +{ + int ret = 0; + u16 companion_id = 0, read_addr = 0; + + if (!core->spi1) { + pr_info("spi1 device is not available"); + goto exit; + } + + /* check validation(Read data must be 0x73C1) */ + read_addr = 0x0; + fimc_is_comp_single_read(core, read_addr, &companion_id, 2); + pr_info("Companion vaildation: 0x%04x\n", companion_id); +#if 0 +#if defined(CONFIG_SOC_EXYNOS5430) + if (companion_id != COMP_MAGIC_NUMBER) + ret = -EINVAL; +#endif +#endif +exit: + return ret; +} + +int fimc_is_comp_read_ver(struct fimc_is_core *core) +{ + int ret = 0; +#if 0 + u16 read_addr; +#endif + + if (!core->spi1) { + pr_info("spi1 device is not available"); + goto exit; + } + + /* check validation(Read data must be 0x73C1) */ +#if 0 + read_addr = 0x02; + fimc_is_comp_single_read(core, read_addr, &companion_ver, 2); +#else + companion_ver = 0x00B0; + pr_info("Default Companion Version is 0x00B0(EVT1)\n"); +#endif + + if (companion_ver == 0x00A0) { + pr_info("Companion version: 0x%04x\n", companion_ver); + ret = fimc_is_comp_single_write(core, 0x0256, 0x0001); + } else if (companion_ver == 0x00B0) { + pr_info("Companion version: 0x%04x\n", companion_ver); + ret = fimc_is_comp_single_write(core, 0x0122, 0x0001); + } + if (ret) + err("fimc_is_comp_read_ver() fail"); + +exit: + return ret; +} + +u8 fimc_is_comp_is_compare_ver(struct fimc_is_core *core) +{ + u8 ret = 0; + u32 from_ver = 0, def_ver = 0; + char *cal_buf; + char ver[3] = {'V', '0', '0'}; + + fimc_is_sec_get_cal_buf(&cal_buf); + + if (!core->spi1) { + pr_info("spi1 device is not available"); + goto exit; + } + + def_ver = ver[0] << 16 | ver[1] << 8 | ver[2]; + from_ver = cal_buf[96] << 16 | cal_buf[97] << 8 | cal_buf[98]; + if (from_ver == def_ver) { + return cal_buf[99]; + } else { + pr_err("FROM core version is invalid. version is %c%c%c%c\n", cal_buf[96], cal_buf[97], cal_buf[98], cal_buf[99]); + return 0; + } +exit: + return ret; +} + +int fimc_is_comp_loadcal(struct fimc_is_core *core) +{ + int ret = 0; + struct fimc_is_from_info *sysfs_finfo; + bool pdaf_valid = false; + int retry_count = 3; + + if (!crc32_c1_check) { + pr_debug("CRC check fail.Do not apply cal data."); + return 0; + } + + if (!core->spi1) { + pr_debug("spi1 device is not available"); + goto p_err; + } + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); +retry: + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->lsc_i0_gain_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->lsc_i0_gain_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->lsc_j0_gain_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail",sysfs_finfo->lsc_j0_gain_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->lsc_a_gain_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->lsc_a_gain_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->lsc_k4_gain_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->lsc_k4_gain_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->lsc_scale_gain_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->lsc_scale_gain_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->wcoefficient1_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->wcoefficient1_addr); + goto p_err; + } + usleep_range(1000, 1000); + } +#if 0 + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->af_inf_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->af_inf_addr); + goto p_err; + } + usleep_range(1000, 1000); + ret = fimc_is_comp_load_i2c_cal(core, sysfs_finfo->af_macro_addr); + if (ret) { + err("fimc_is_comp_load_binary(0x%04x) fail", sysfs_finfo->af_macro_addr); + goto p_err; + } + usleep_range(1000, 1000); + } +#endif + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + if (companion_lsc_isvalid) { + ret = fimc_is_comp_load_cal(core, COMP_LSC); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", COMP_LSC); + goto p_err; + } + pr_info("LSC from FROM loaded"); + } +#ifdef USE_DEFAULT_CAL + else { + ret = fimc_is_comp_load_binary(core, COMP_DEFAULT_LSC); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", COMP_DEFAULT_LSC); + goto p_err; + } + pr_info("Default LSC loaded"); + } +#endif + usleep_range(1000, 1000); + } else { + pr_info("Did not load LSC cal data"); + } + /*Workaround : If FROM has ver.V003, Skip PDAF Cal loading to companion.*/ + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + ret = fimc_is_comp_load_cal(core, COMP_PDAF); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", COMP_PDAF); + goto p_err; + } + pdaf_valid = true; + usleep_range(1000, 1000); + } + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + if (companion_coef_isvalid) { + ret = fimc_is_comp_load_cal(core, COMP_COEF_CAL); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", COMP_COEF_CAL); + goto p_err; + } + pr_info("COEF from FROM loaded"); + } +#ifdef USE_DEFAULT_CAL + else { + ret = fimc_is_comp_load_binary(core, COMP_DEFAULT_COEF); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", COMP_DEFAULT_COEF); + goto p_err; + } + pr_info("Default COEF loaded"); + } +#endif + usleep_range(1000, 1000); + } else { + pr_info("Did not load COEF cal data"); + } + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + ret = fimc_is_comp_check_crc32(core, COMP_LSC); + if (pdaf_valid) + ret |= fimc_is_comp_check_crc32(core, COMP_PDAF); + ret |= fimc_is_comp_check_crc32(core, COMP_COEF_CAL); + ret |= fimc_is_comp_check_crc32(core, "coef2"); + ret |= fimc_is_comp_check_crc32(core, "coef3"); + ret |= fimc_is_comp_check_crc32(core, "coef4"); + ret |= fimc_is_comp_check_crc32(core, "coef5"); + ret |= fimc_is_comp_check_crc32(core, "coef6"); + if ((ret < 0) && (retry_count > 0)) { + retry_count--; + goto retry; + } + } + usleep_range(5000, 5000); +p_err: + return 0; +} + +int fimc_is_comp_loadfirm(struct fimc_is_core *core) +{ + int ret = 0; + int retry_count = 3; + struct fimc_is_from_info *sysfs_finfo; + + if (!core->spi1) { + err("spi1 device is not available"); + goto p_err; + } + + ret = fimc_is_comp_read_ver(core); + if (ret) { + err("fimc_is_comp_read_ver fail"); + goto p_err; + } + msleep(1); + +retry: + ret = fimc_is_comp_single_write(core, 0x6042, 0x0001); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + + ret = fimc_is_comp_single_write(core, 0x6428, 0x0000); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + + ret = fimc_is_comp_single_write(core, 0x642A, 0x0000); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + if (companion_ver == 0x00A0) + ret = fimc_is_comp_load_binary(core, COMP_FW_EVT0); + else if (companion_ver == 0x00B0) + ret = fimc_is_comp_load_binary(core, sysfs_finfo->load_c1_fw_name); + if (ret) { + err("fimc_is_comp_load_firmware fail"); + goto p_err; + } + + ret = fimc_is_comp_single_write(core, 0x6014, 0x0001); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } + + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + if (companion_ver == 0x00A0) + ret = fimc_is_comp_check_crc32(core, COMP_FW_EVT0); + else if (companion_ver == 0x00B0) + ret = fimc_is_comp_check_crc32(core, COMP_FW_EVT1); + if ((ret < 0) && (retry_count > 0)) { + retry_count--; + goto retry; + } + } + usleep_range(5000, 5000); +p_err: + return ret; +} + +int fimc_is_comp_loadsetf(struct fimc_is_core *core) +{ + int ret = 0; + struct fimc_is_from_info *sysfs_finfo; + + if (!core->spi1) { + pr_debug("spi1 device is not available"); + goto p_err; + } + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + ret = fimc_is_comp_load_binary(core, sysfs_finfo->load_c1_mastersetf_name); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", sysfs_finfo->load_c1_mastersetf_name); + goto p_err; + } + + ret = fimc_is_comp_load_binary(core, sysfs_finfo->load_c1_modesetf_name); + if (ret) { + err("fimc_is_comp_load_binary(%s) fail", sysfs_finfo->load_c1_modesetf_name); + goto p_err; + } + usleep_range(5000, 5000); + /*Stream on concord*/ + ret = fimc_is_comp_single_write(core, 0x6800, 0x0001); + if (ret) { + err("fimc_is_comp_i2c_setf_write() fail"); + } +p_err: + return ret; +} + +void fimc_is_s_int_comb_isp(struct fimc_is_core *core,bool on, u32 ch) +{ + u32 cfg = ~0x0; + + if(on) { + cfg &= ~ch; + writel(cfg, core->regs + INTMR2); + } else { + cfg = 0xFFFFFFFF; + writel(cfg, core->regs + INTMR2); + } +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-companion.h b/drivers/media/platform/exynos/fimc-is/fimc-is-companion.h new file mode 100644 index 000000000000..3de736d62600 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-companion.h @@ -0,0 +1,55 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FIMC_IS_COMPANION_H +#define FIMC_IS_COMPANION_H + +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-companion_address.h" +#include "fimc-is-interface.h" + +#define CRC_RETRY_COUNT 40 + +#define FIMC_IS_ISP_CV "/data/ISP_CV" + +enum dcdc_vendor{ + DCDC_VENDOR_NONE = 0, + DCDC_VENDOR_FAN53555, + DCDC_VENDOR_NCP6335B, + DCDC_VENDOR_MAX, +}; + +struct dcdc_power { + enum dcdc_vendor type; + struct i2c_client *client; + + /* Get i2c register value to set vout of dcdc regulator */ + int (*get_vout_val)(int); + + /* Get voltage name of vout as string */ + const char *(*get_vout_str)(int); + + /* Set dcdc vout with i2c register value */ + int (*set_vout)(struct i2c_client *client, int vout); +}; + +int fimc_is_comp_is_valid(struct fimc_is_core *core); +int fimc_is_comp_loadfirm(struct fimc_is_core *core); +int fimc_is_comp_loadsetf(struct fimc_is_core *core); +int fimc_is_comp_loadcal(struct fimc_is_core *core); +u8 fimc_is_comp_is_compare_ver(struct fimc_is_core *core); +int fimc_is_power_binning(struct fimc_is_core *core); +void fimc_is_s_int_comb_isp(struct fimc_is_core *core,bool on, u32 ch); + +#endif /* FIMC_IS_COMPANION_H */ diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-companion_address.h b/drivers/media/platform/exynos/fimc-is/fimc-is-companion_address.h new file mode 100644 index 000000000000..a16bfad6cfc5 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-companion_address.h @@ -0,0 +1,26 @@ +/*2P2 Address*/ +#define MEM_GRAS_B_2P2 0x2001A000 // 0x00000000 0x1A00 0x2001A000 0x0020 +#define MEM_AF_10_1_2P2 0x2000B900 // 0x00000000 0x0200 0x2000B900 0x0020 +#define MEM_XTALK_10_2P2 0x20010000 // 0x00000000 0x0FC0 0x20010000 0x0020 +#define grasTuning_uParabolicCenterX_2P2 0x400008A8 // 0x0A68 0x0002 0x200008A8 0x0002 +#define grasTuning_uParabolicCenterY_2P2 0x400008AA // 0x05DC 0x0002 0x200008AA 0x0002 +#define grasTuning_uBiQuadFactorA_2P2 0x400008B4 // 0x00008000 0x0004 0x200008B4 0x0003 +#define grasTuning_uBiQuadFactorB_2P2 0x400008B8 // 0x00004000 0x0004 0x200008B8 0x0003 +#define grasTuning_uBiQuadScaleShiftAdder_2P2 0x400008BC // 0x0000 0x0002 0x200008BC 0x0002 +#define xtalkTuningParams_wcr_2P2 0x40000EA0 // 0xFFF7 0x0002 0x20000EA0 0x0002 +#define grasTuning_actuatorPositionToShadingPowerLut_0_2P2 0x40000984 // 0x0032 0x0002 0x2000097C 0x0002 +#define grasTuning_actuatorPositionToShadingPowerLut_9_2P2 0x40000996 // 0x00CF 0x0002 0x2000098E 0x0002 + +/*IMX240 Address*/ +#define MEM_GRAS_B_IMX240 0x2001A000 // 0x00000000 0x1A00 0x2001A000 0x0020 +#define MEM_AF_10_1_IMX240 0x2000B900 // 0x00000000 0x0200 0x2000B900 0x0020 +#define MEM_XTALK_10_IMX240 0x20010000 // 0x00000000 0x0FC0 0x20010000 0x0020 +#define grasTuning_uParabolicCenterX_IMX240 0x400008A8 // 0x0A68 0x0002 0x200008A8 0x0002 +#define grasTuning_uParabolicCenterY_IMX240 0x400008AA // 0x05DC 0x0002 0x200008AA 0x0002 +#define grasTuning_uBiQuadFactorA_IMX240 0x400008B4 // 0x00008000 0x0004 0x200008B4 0x0003 +#define grasTuning_uBiQuadFactorB_IMX240 0x400008B8 // 0x00004000 0x0004 0x200008B8 0x0003 +#define grasTuning_uBiQuadScaleShiftAdder_IMX240 0x400008BC // 0x0000 0x0002 0x200008BC 0x0002 +#define xtalkTuningParams_wcr_IMX240 0x40000EC0 // 0xFFF7 0x0002 0x20000EC0 0x0002 +#define grasTuning_actuatorPositionToShadingPowerLut_0_IMX240 0x40000984 // 0x0032 0x0002 0x2000097C 0x0002 +#define grasTuning_actuatorPositionToShadingPowerLut_9_IMX240 0x40000996 // 0x00CF 0x0002 0x2000098E 0x0002 + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-config.h b/drivers/media/platform/exynos/fimc-is/fimc-is-config.h new file mode 100644 index 000000000000..1e83b215c907 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-config.h @@ -0,0 +1,375 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_CONFIG_H +#define FIMC_IS_CONFIG_H + +/* configuration - default post processing */ +#define ENABLE_SETFILE +#define ENABLE_DRC +/* #define ENABLE_ODC */ +/* #define ENABLE_VDIS */ +/* #define ENABLE_TDNR */ +#define ENABLE_FD +#define ENABLE_CLOCK_GATE +/* #define HAS_FW_CLOCK_GATE */ +/* #define ENABLE_CACHE */ +#define ENABLE_FULL_BYPASS +#define ENABLE_FAST_SHOT +#define USE_OWN_FAULT_HANDLER +/* #define ENABLE_MIF_400 */ +#define ENABLE_DTP +#define USE_ION_ALLOC + +#define CSI_VERSION_0000_0000 0x0 +#define CSI_VERSION_0310_0100 0x03100100 + +#define FIMC_IS_VERSION_000 0x0 +#define FIMC_IS_VERSION_250 0x250 + +#if defined(CONFIG_PM_DEVFREQ) +#define ENABLE_DVFS +#define START_DVFS_LEVEL FIMC_IS_SN_MAX +#endif + +#if defined(CONFIG_SOC_EXYNOS5430) +#undef ENABLE_SETFILE +#define SUPPORTED_IS_CMD_VER 132 +#define TARGET_SPI_CH_FOR_PERI 0 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0000_0000 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 +#define HAS_FW_CLOCK_GATE + +#elif defined(CONFIG_SOC_EXYNOS5433) +#define SUPPORTED_IS_CMD_VER 132 +#define TARGET_SPI_CH_FOR_PERI 0 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0000_0000 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 +#define HAS_FW_CLOCK_GATE +#define MAX_ZOOM_LEVEL 8 + +#elif defined(CONFIG_SOC_EXYNOS5422) +#undef ENABLE_SETFILE +#define SUPPORTED_IS_CMD_VER 132 +#define TARGET_SPI_CH_FOR_PERI 0 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0000_0000 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 + +#elif defined(CONFIG_SOC_EXYNOS5260) +#undef ENABLE_SETFILE +#undef ENABLE_DRC +#undef ENABLE_FULL_BYPASS +#define SUPPORTED_EARLY_BUF_DONE +#define SUPPORTED_IS_CMD_VER 131 +#define TARGET_SPI_CH_FOR_PERI 1 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0310_0100 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 +#define SCALER_PARALLEL_MODE + +#elif defined(CONFIG_SOC_EXYNOS4415) +#undef ENABLE_SETFILE +#undef ENABLE_DRC +#define SUPPORTED_IS_CMD_VER 132 +#define TARGET_SPI_CH_FOR_PERI 1 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0310_0100 +#define FIMC_IS_VERSION FIMC_IS_VERSION_250 + +#elif defined(CONFIG_SOC_EXYNOS3470) +#undef ENABLE_SETFILE +#undef ENABLE_DRC +#undef ENABLE_FULL_BYPASS +#define SUPPORTED_EARLY_BUF_DONE +#define ENABLE_IFLAG +#define SUPPORTED_IS_CMD_VER 131 +#define TARGET_SPI_CH_FOR_PERI 1 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0000_0000 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 +#define SCALER_PARALLEL_MODE + +#elif defined(CONFIG_SOC_EXYNOS3472) +#undef ENABLE_SETFILE +#undef ENABLE_DRC +#undef ENABLE_DVFS +#undef ENABLE_FULL_BYPASS +#define ENABLE_IFLAG +#define SUPPORTED_IS_CMD_VER 131 +#define TARGET_SPI_CH_FOR_PERI 1 +#define FIMC_IS_CSI_VERSION CSI_VERSION_0310_0100 +#define FIMC_IS_VERSION FIMC_IS_VERSION_000 + +#else +#error fimc-is driver can NOT support this platform + +#endif + +#if !defined(MAX_ZOOM_LEVEL) +/* default max zoom lv is 4 */ +#define MAX_ZOOM_LEVEL 4 +#endif + +/* notifier for MIF throttling */ +#if defined(CONFIG_ARM_EXYNOS5433_BUS_DEVFREQ) && defined(CONFIG_CPU_THERMAL_IPA) +#define EXYNOS_MIF_ADD_NOTIFIER(nb) \ + exynos_mif_add_notifier(nb) +#else +#define EXYNOS_MIF_ADD_NOTIFIER(nb) +#endif + +#if defined(CONFIG_ARM_EXYNOS4415_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif +#if defined(CONFIG_ARM_EXYNOS5260_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif +#if defined(CONFIG_ARM_EXYNOS3470_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif +#if defined(CONFIG_ARM_EXYNOS5422_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif +#if defined(CONFIG_ARM_EXYNOS5430_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif +#if defined(CONFIG_ARM_EXYNOS5433_BUS_DEVFREQ) +#define CONFIG_FIMC_IS_BUS_DEVFREQ +#endif + +/* + * ----------------------------------------------------------------------------- + * Debug Message Configuration + * ----------------------------------------------------------------------------- + */ + +/* #define DEBUG */ +#define DBG_VIDEO +#define DBG_DEVICE +#define DBG_PER_FRAME +/* #define DBG_STREAMING */ +#define DEBUG_INSTANCE 0xF +#define BUG_ON_ENABLE +/* #define FIXED_FPS_DEBUG */ +#define FIXED_FPS_VALUE 10 +/* #define DBG_CSIISR */ +/* #define DBG_FLITEISR */ +#define FW_DEBUG +#define RESERVED_MEM +/* #define SCALER_CROP_DZOOM */ +/* #define USE_ADVANCED_DZOOM */ +/* #define TASKLET_MSG */ +/* #define PRINT_CAPABILITY */ +/* #define PRINT_BUFADDR */ +/* #define PRINT_DZOOM */ +/* #define PRINT_PARAM */ +/* #define PRINT_I2CCMD */ +#define ISDRV_VERSION 244 + +#if (defined(BAYER_CROP_DZOOM) && defined(SCALER_CROP_DZOOM)) +#error BAYER_CROP_DZOOM and SCALER_CROP_DZOOM can''t be enable together +#endif + +/* + * driver version extension + */ +#ifdef ENABLE_CLOCK_GATE +#define get_drv_clock_gate() 0x1 +#else +#define get_drv_clock_gate() 0x0 +#endif +#ifdef ENABLE_DVFS +#define get_drv_dvfs() 0x2 +#else +#define get_drv_dvfs() 0x0 +#endif + +#define GET_3AA_ID(video) ((video->id < 14) ? 0 : 1) +#define GET_3AAC_ID(video) ((video->id < FIMC_IS_VIDEO_3A1_NUM) ? 0 : 1) + +/* sync log with HAL, FW */ +#define log_sync(sync_id) \ + pr_info("[@]FIMC_IS_SYNC %d\n", sync_id) + +#ifdef err +#undef err +#endif +#define err(fmt, args...) \ + pr_err("[@][ERR]%s:%d: " fmt "\n", __func__, __LINE__, ##args) + +/* multi-stream */ +#define merr(fmt, object, args...) \ + pr_err("[@][%d][ERR]%s:%d: " fmt "\n", object->instance, __func__, __LINE__, ##args) + +/* multi-stream & runtime error */ +#define mrerr(fmt, object, frame, args...) \ + pr_err("[@][%d:F%d][ERR]%s:%d: " fmt "\n", object->instance, frame->fcount, __func__, __LINE__, ##args) + +/* multi-stream & group error */ +#define mgerr(fmt, object, group, args...) \ + pr_err("[@][%d][GP%d][ERR]%s:%d:" fmt "\n", object->instance, group->id, __func__, __LINE__, ##args) + +#ifdef warn +#undef warn +#endif +#define warn(fmt, args...) \ + pr_warning("[@][WRN] " fmt "\n", ##args) + +#define mwarn(fmt, this, args...) \ + pr_warning("[@][%d][WRN] " fmt "\n", this->instance, ##args) + +#define mgwarn(fmt, object, group, args...) \ + pr_warning("[@][%d][GP%d][WRN]" fmt "\n", object->instance, group->id, ##args) + +#define info(fmt, args...) \ + pr_info("[@]" fmt, ##args) + +#define minfo(fmt, object, args...) \ + pr_info("[@][%d]" fmt, object->instance, ##args) + +#define mrinfo(fmt, object, frame, args...) \ + pr_info("[@][%d:F%d]" fmt, object->instance, frame->fcount, ##args) + +#define mrdbg(fmt, object, frame, args...) \ + printk(KERN_DEBUG "[@][%d:F%d]" fmt, object->instance, frame->fcount, ##args) + +#define mdbg_common(prefix, fmt, instance, args...) \ + do { \ + if ((1<<(instance)) & DEBUG_INSTANCE) \ + pr_info("[@]" prefix fmt, instance, ##args); \ + } while (0) + +#if (defined(DEBUG) && defined(DBG_VIDEO)) +#define dbg(fmt, args...) + +#define dbg_warning(fmt, args...) \ + printk(KERN_INFO "%s[WAR] Warning! " fmt, __func__, ##args) + +/* debug message for video node */ +#define mdbgv_vid(fmt, args...) \ + pr_info("[@][VID:V] " fmt, ##args) + +#define mdbgv_sensor(fmt, this, args...) \ + mdbg_common("[%d][SS%d:V] ", fmt, this->video->id, this->instance, ##args) + +#define mdbgv_3aa(fmt, this, args...) \ + mdbg_common("[%d][3A%d:V] ", fmt, GET_3AA_ID(this->video), this->instance, ##args) + +#define mdbgv_3aac(fmt, this, args...) \ + mdbg_common("[%d][3A%dC:V] ", fmt, GET_3AAC_ID(this->video), this->instance, ##args) + +#define dbg_isp(fmt, args...) \ + printk(KERN_INFO "[ISP] " fmt, ##args) + +#define mdbgv_isp(fmt, this, args...) \ + mdbg_common("[%d][ISP:V] ", fmt, this->instance, ##args) + +#define dbg_scp(fmt, args...) \ + printk(KERN_INFO "[SCP] " fmt, ##args) + +#define mdbgv_scp(fmt, this, args...) \ + mdbg_common("[%d][SCP:V] ", fmt, this->instance, ##args) + +#define dbg_scc(fmt, args...) \ + printk(KERN_INFO "[SCC] " fmt, ##args) + +#define mdbgv_scc(fmt, this, args...) \ + mdbg_common("[%d][SCC:V] ", fmt, this->instance, ##args) + +#define dbg_vdisc(fmt, args...) \ + printk(KERN_INFO "[VDC] " fmt, ##args) + +#define mdbgv_vdc(fmt, this, args...) \ + mdbg_common("[%d][VDC:V] ", fmt, this->instance, ##args) + +#define dbg_vdiso(fmt, args...) \ + printk(KERN_INFO "[VDO] " fmt, ##args) + +#define mdbgv_vdo(fmt, this, args...) \ + mdbg_common("[%d][VDO:V] ", fmt, this->instance, ##args) +#else +#define dbg(fmt, args...) + +/* debug message for video node */ +#define mdbgv_vid(fmt, this, args...) +#define dbg_sensor(fmt, args...) +#define mdbgv_sensor(fmt, this, args...) +#define mdbgv_3aa(fmt, this, args...) +#define mdbgv_3aac(fmt, this, args...) +#define dbg_isp(fmt, args...) +#define mdbgv_isp(fmt, this, args...) +#define dbg_scp(fmt, args...) +#define mdbgv_scp(fmt, this, args...) +#define dbg_scc(fmt, args...) +#define mdbgv_scc(fmt, this, args...) +#define dbg_vdisc(fmt, args...) +#define mdbgv_vdc(fmt, this, args...) +#define dbg_vdiso(fmt, args...) +#define mdbgv_vdo(fmt, this, args...) +#endif + +#if (defined(DEBUG) && defined(DBG_DEVICE)) +/* debug message for device */ +#define mdbgd_sensor(fmt, this, args...) \ + mdbg_common("[%d][SEN:D] ", fmt, this->instance, ##args) + +#define mdbgd_front(fmt, this, args...) \ + mdbg_common("[%d][FRT:D] ", fmt, this->instance, ##args) + +#define mdbgd_back(fmt, this, args...) \ + mdbg_common("[%d][BAK:D] ", fmt, this->instance, ##args) + +#define mdbgd_3a0(fmt, this, args...) \ + mdbg_common("[%d][3A0:D] ", fmt, this->instance, ##args) + +#define mdbgd_3a1(fmt, this, args...) \ + mdbg_common("[%d][3A1:D] ", fmt, this->instance, ##args) + +#define mdbgd_isp(fmt, this, args...) \ + mdbg_common("[%d][ISP:D] ", fmt, this->instance, ##args) + +#define mdbgd_ischain(fmt, this, args...) \ + mdbg_common("[%d][ISC:D] ", fmt, this->instance, ##args) + +#define mdbgd_group(fmt, group, args...) \ + mdbg_common("[%d][GP%d:D] ", fmt, group->device->instance, group->id, ##args) + +#define dbg_core(fmt, args...) \ + pr_info("[@][COR] " fmt, ##args) +#else +/* debug message for device */ +#define mdbgd_sensor(fmt, this, args...) +#define mdbgd_front(fmt, this, args...) +#define mdbgd_back(fmt, this, args...) +#define mdbgd_isp(fmt, this, args...) +#define dbg_ischain(fmt, args...) +#define mdbgd_ischain(fmt, this, args...) +#define dbg_core(fmt, args...) +#define dbg_interface(fmt, args...) +#define dbg_frame(fmt, args...) +#define mdbgd_group(fmt, group, args...) +#endif + +#if (defined(DEBUG) && defined(DBG_STREAMING)) +#define dbg_interface(fmt, args...) \ + pr_info("[@][ITF] " fmt, ##args) +#define dbg_frame(fmt, args...) \ + pr_info("[@][FRM] " fmt, ##args) +#else +#define dbg_interface(fmt, args...) +#define dbg_frame(fmt, args...) +#endif + +#if (defined(DEBUG) && defined(DBG_PER_FRAME)) +#define mdbg_pframe(fmt, object, frame, args...) \ + pr_info("[@][%d:F%d]" fmt, object->instance, frame->fcount, ##args) +#else +#define mdbg_pframe(fmt, object, frame, args...) +#endif + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-core.c b/drivers/media/platform/exynos/fimc-is/fimc-is-core.c new file mode 100644 index 000000000000..e01672cfe6ab --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-core.c @@ -0,0 +1,2413 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#endif + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-framemgr.h" +#include "fimc-is-dt.h" +#include "fimc-is-resourcemgr.h" +#include "fimc-is-clk-gate.h" +#include "fimc-is-dvfs.h" +#include "fimc-is-fan53555.h" +#include "fimc-is-ncp6335b.h" + +#include "sensor/fimc-is-device-2p2.h" +#include "sensor/fimc-is-device-3h5.h" +#include "sensor/fimc-is-device-3h7.h" +#include "sensor/fimc-is-device-3h7_sunny.h" +#include "sensor/fimc-is-device-3l2.h" +#include "sensor/fimc-is-device-4e5.h" +#include "sensor/fimc-is-device-6a3.h" +#include "sensor/fimc-is-device-6b2.h" +#include "sensor/fimc-is-device-8b1.h" +#include "sensor/fimc-is-device-6d1.h" +#include "sensor/fimc-is-device-imx134.h" +#include "sensor/fimc-is-device-imx135.h" +#include "sensor/fimc-is-device-imx175.h" +#include "sensor/fimc-is-device-imx240.h" +#include "sensor/fimc-is-device-imx219.h" +#include "sensor/fimc-is-device-4h5.h" +#include "sensor/fimc-is-device-3l2.h" +#include "sensor/fimc-is-device-2p2.h" +#include "sensor/fimc-is-device-2p2_12m.h" +#include "sensor/fimc-is-device-2p3.h" +#ifdef CONFIG_USE_VENDER_FEATURE +#include "fimc-is-sec-define.h" +#ifdef CONFIG_OIS_USE +#include "fimc-is-device-ois.h" +#endif +#endif +#ifdef CONFIG_AF_HOST_CONTROL +#include "fimc-is-device-af.h" +#endif + +#ifdef USE_OWN_FAULT_HANDLER +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) +#include +#else +#include +#endif +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) +#define PM_QOS_CAM_THROUGHPUT PM_QOS_RESERVED +#endif + +struct fimc_is_from_info *sysfs_finfo = NULL; +struct fimc_is_from_info *sysfs_pinfo = NULL; + +#ifdef CONFIG_USE_VENDER_FEATURE +struct class *camera_class = NULL; +struct device *camera_front_dev; +struct device *camera_rear_dev; +#ifdef CONFIG_OIS_USE +struct device *camera_ois_dev; +#endif +#endif + +struct device *fimc_is_dev = NULL; +struct fimc_is_core *sysfs_core; + +#ifdef CONFIG_USE_VENDER_FEATURE +extern bool crc32_fw_check; +extern bool crc32_check; +extern bool crc32_check_factory; +extern bool fw_version_crc_check; +extern bool is_latest_cam_module; +extern bool is_final_cam_module; +#if defined(CONFIG_SOC_EXYNOS5433) +extern bool is_right_prj_name; +#endif +#ifdef CONFIG_COMPANION_USE +extern bool crc32_c1_fw_check; +extern bool crc32_c1_check; +extern bool crc32_c1_check_factory; +#endif /* CONFIG_COMPANION_USE */ +#endif + +extern struct pm_qos_request exynos_isp_qos_int; +extern struct pm_qos_request exynos_isp_qos_mem; +extern struct pm_qos_request exynos_isp_qos_cam; +extern struct pm_qos_request exynos_isp_qos_disp; + +extern int fimc_is_3a0_video_probe(void *data); +extern int fimc_is_3a1_video_probe(void *data); +extern int fimc_is_isp_video_probe(void *data); +extern int fimc_is_scc_video_probe(void *data); +extern int fimc_is_scp_video_probe(void *data); +extern int fimc_is_vdc_video_probe(void *data); +extern int fimc_is_vdo_video_probe(void *data); +extern int fimc_is_3a0c_video_probe(void *data); +extern int fimc_is_3a1c_video_probe(void *data); + +/* sysfs global variable for debug */ +struct fimc_is_sysfs_debug sysfs_debug; + +static int fimc_is_ischain_allocmem(struct fimc_is_core *this) +{ + int ret = 0; + void *fw_cookie; + size_t fw_size = +#ifdef ENABLE_ODC + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + +#endif +#ifdef ENABLE_VDIS + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF + +#endif +#ifdef ENABLE_TDNR + SIZE_DNR_INTERNAL_BUF * NUM_DNR_INTERNAL_BUF + +#endif + FIMC_IS_A5_MEM_SIZE; + + fw_size = PAGE_ALIGN(fw_size); + dbg_core("Allocating memory for FIMC-IS firmware.\n"); + + fw_cookie = vb2_ion_private_alloc(this->mem.alloc_ctx, fw_size, 1, 0); + + if (IS_ERR(fw_cookie)) { + err("Allocating bitprocessor buffer failed"); + fw_cookie = NULL; + ret = -ENOMEM; + goto exit; + } + + ret = vb2_ion_dma_address(fw_cookie, &this->minfo.dvaddr); + if ((ret < 0) || (this->minfo.dvaddr & FIMC_IS_FW_BASE_MASK)) { + err("The base memory is not aligned to 64MB."); + vb2_ion_private_free(fw_cookie); + this->minfo.dvaddr = 0; + fw_cookie = NULL; + ret = -EIO; + goto exit; + } + dbg_core("Device vaddr = %08x , size = %08x\n", + this->minfo.dvaddr, FIMC_IS_A5_MEM_SIZE); + + this->minfo.kvaddr = (u32)vb2_ion_private_vaddr(fw_cookie); + if (IS_ERR((void *)this->minfo.kvaddr)) { + err("Bitprocessor memory remap failed"); + vb2_ion_private_free(fw_cookie); + this->minfo.kvaddr = 0; + fw_cookie = NULL; + ret = -EIO; + goto exit; + } + + vb2_ion_sync_for_device(fw_cookie, 0, fw_size, DMA_BIDIRECTIONAL); + +exit: + info("[COR] Device virtual for internal: %08x\n", this->minfo.kvaddr); + this->minfo.fw_cookie = fw_cookie; + + return ret; +} + +static int fimc_is_ishcain_initmem(struct fimc_is_core *this) +{ + int ret = 0; + u32 offset; + + dbg_core("fimc_is_init_mem - ION\n"); + + ret = fimc_is_ischain_allocmem(this); + if (ret) { + err("Couldn't alloc for FIMC-IS firmware\n"); + ret = -ENOMEM; + goto exit; + } + + offset = FW_SHARED_OFFSET; + this->minfo.dvaddr_fshared = this->minfo.dvaddr + offset; + this->minfo.kvaddr_fshared = this->minfo.kvaddr + offset; + + offset = FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE; + this->minfo.dvaddr_region = this->minfo.dvaddr + offset; + this->minfo.kvaddr_region = this->minfo.kvaddr + offset; + + offset = FIMC_IS_A5_MEM_SIZE; +#ifdef ENABLE_ODC + this->minfo.dvaddr_odc = this->minfo.dvaddr + offset; + this->minfo.kvaddr_odc = this->minfo.kvaddr + offset; + offset += (SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF); +#else + this->minfo.dvaddr_odc = 0; + this->minfo.kvaddr_odc = 0; +#endif + +#ifdef ENABLE_VDIS + this->minfo.dvaddr_dis = this->minfo.dvaddr + offset; + this->minfo.kvaddr_dis = this->minfo.kvaddr + offset; + offset += (SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF); +#else + this->minfo.dvaddr_dis = 0; + this->minfo.kvaddr_dis = 0; +#endif + +#ifdef ENABLE_TDNR + this->minfo.dvaddr_3dnr = this->minfo.dvaddr + offset; + this->minfo.kvaddr_3dnr = this->minfo.kvaddr + offset; + offset += (SIZE_DNR_INTERNAL_BUF * NUM_DNR_INTERNAL_BUF); +#else + this->minfo.dvaddr_3dnr = 0; + this->minfo.kvaddr_3dnr = 0; +#endif + + dbg_core("fimc_is_init_mem done\n"); + +exit: + return ret; +} + +#if defined(CONFIG_ARM_EXYNOS5433_BUS_DEVFREQ) && defined(CONFIG_CPU_THERMAL_IPA) +static int fimc_is_mif_throttling_notifier(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct fimc_is_core *core = NULL; + struct fimc_is_device_sensor *device = NULL; + int i; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is null"); + goto exit; + } + + for (i = 0; i < FIMC_IS_MAX_NODES; i++) { + if (test_bit(FIMC_IS_SENSOR_OPEN, &core->sensor[i].state)) { + device = &core->sensor[i]; + break; + } + } + + if (device && !test_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state)) + /* Set DTP */ + set_bit(FIMC_IS_MIF_THROTTLING_STOP, &device->force_stop); + else + err("any sensor is not opened"); + +exit: + err("MIF: cause of mif_throttling, mif_qos is [%lu]!!!\n", val); + + return NOTIFY_OK; +} + +static struct notifier_block exynos_fimc_is_mif_throttling_nb = { + .notifier_call = fimc_is_mif_throttling_notifier, +}; +#endif + +#ifdef CONFIG_USE_VENDER_FEATURE +static ssize_t camera_front_sensorid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_platform_fimc_is_sensor *sensor = dev_get_drvdata(dev); + + dev_info(dev, "%s: E", __func__); + + if (unlikely(!sensor)) { + dev_err(dev, "%s: sensor null\n", __func__); + return -EFAULT; + } + + return sprintf(buf, "%d\n", sensor->sensor_id); +} + +static ssize_t camera_rear_sensorid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct exynos_platform_fimc_is_sensor *sensor = dev_get_drvdata(dev); + + dev_info(dev, "%s: E", __func__); + + if (unlikely(!sensor)) { + dev_err(dev, "%s: sensor null\n", __func__); + return -EFAULT; + } + + return sprintf(buf, "%d\n", sensor->sensor_id); +} + +static DEVICE_ATTR(front_sensorid, S_IRUGO, camera_front_sensorid_show, NULL); +static DEVICE_ATTR(rear_sensorid, S_IRUGO, camera_rear_sensorid_show, NULL); + +int fimc_is_get_sensor_data(struct device *dev, char *maker, char *name, int position) +{ + struct exynos_platform_fimc_is_sensor *sensor = dev_get_drvdata(dev); + struct fimc_is_core *core; + struct fimc_is_device_sensor *device; + int i; + + if (unlikely(!sensor)) { + dev_err(dev, "%s: sensor null\n", __func__); + return -EFAULT; + } + + if (!fimc_is_dev) { + dev_err(dev, "%s: fimc_is_dev is not yet probed", __func__); + return -ENODEV; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + dev_err(dev, "%s: core is NULL", __func__); + return -EINVAL; + } + + if (position == SENSOR_POSITION_FRONT || position == SENSOR_POSITION_REAR) { + device = &core->sensor[position]; + } else { + dev_err(dev, "%s: position value is wrong", __func__); + return -EINVAL; + } + + for (i = 0; i < atomic_read(&core->resourcemgr.rsccount_module); i++) { + if (sensor->sensor_id == device->module_enum[i].id) { + if (maker != NULL) + sprintf(maker, "%s", device->module_enum[i].sensor_maker ? + device->module_enum[i].sensor_maker : "UNKNOWN"); + if (name != NULL) + sprintf(name, "%s", device->module_enum[i].sensor_name ? + device->module_enum[i].sensor_name : "UNKNOWN"); + return 0; + } + } + + dev_err(dev, "%s: there's no matched sensor id", __func__); + + return -ENODEV; +} + +static ssize_t camera_front_camtype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char sensor_maker[50]; + char sensor_name[50]; + int ret; + + ret = fimc_is_get_sensor_data(dev, sensor_maker, sensor_name, SENSOR_POSITION_FRONT); + + if (ret < 0) + return sprintf(buf, "UNKNOWN_UNKNOWN_FIMC_IS\n"); + else + return sprintf(buf, "%s_%s_FIMC_IS\n", sensor_maker, sensor_name); +} + +static ssize_t camera_front_camfw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char sensor_name[50]; + int ret; + + ret = fimc_is_get_sensor_data(dev, NULL, sensor_name, SENSOR_POSITION_FRONT); + + if (ret < 0) + return sprintf(buf, "UNKNOWN UNKNOWN\n"); + else + return sprintf(buf, "%s N\n", sensor_name); +} + +static DEVICE_ATTR(front_camtype, S_IRUGO, + camera_front_camtype_show, NULL); +static DEVICE_ATTR(front_camfw, S_IRUGO, camera_front_camfw_show, NULL); + +static struct fimc_is_from_info *pinfo = NULL; +static struct fimc_is_from_info *finfo = NULL; + +int read_from_firmware_version(void) +{ + char fw_name[100]; + char setf_name[100]; +#ifdef CONFIG_COMPANION_USE + char master_setf_name[100]; + char mode_setf_name[100]; +#endif + struct device *is_dev = &sysfs_core->ischain[0].pdev->dev; + + fimc_is_sec_get_sysfs_pinfo(&pinfo); + fimc_is_sec_get_sysfs_finfo(&finfo); + + if (!finfo->is_caldata_read) { + if (finfo->bin_start_addr != 0x80000) { + //fimc_is_sec_set_camid(CAMERA_DUAL_FRONT); +#if defined(CONFIG_PM_RUNTIME) + pr_debug("pm_runtime_suspended = %d\n", + pm_runtime_suspended(is_dev)); + pm_runtime_get_sync(is_dev); +#else + fimc_is_runtime_resume(is_dev); + printk(KERN_INFO "%s - fimc_is runtime resume complete\n", __func__); +#endif + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + fimc_is_sec_fw_sel_eeprom(is_dev, fw_name, setf_name, SENSOR_POSITION_REAR, false); +#else + fimc_is_sec_fw_sel(sysfs_core, is_dev, fw_name, setf_name, false); +#endif +#ifdef CONFIG_COMPANION_USE + fimc_is_sec_concord_fw_sel(sysfs_core, is_dev, fw_name, master_setf_name, mode_setf_name); +#endif +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_put_sync(is_dev); + pr_debug("pm_runtime_suspended = %d\n", + pm_runtime_suspended(is_dev)); +#else + fimc_is_runtime_suspend(is_dev); +#endif + } + } + return 0; +} + +static ssize_t camera_rear_camtype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char sensor_maker[50]; + char sensor_name[50]; + int ret; + + ret = fimc_is_get_sensor_data(dev, sensor_maker, sensor_name, SENSOR_POSITION_REAR); + + if (ret < 0) + return sprintf(buf, "UNKNOWN_UNKNOWN_FIMC_IS\n"); + else + return sprintf(buf, "%s_%s_FIMC_IS\n", sensor_maker, sensor_name); +} + +static ssize_t camera_rear_camfw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char command_ack[20] = {0, }; + char *loaded_fw; + + read_from_firmware_version(); + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + loaded_fw = pinfo->header_ver; +#else + fimc_is_sec_get_loaded_fw(&loaded_fw); +#endif + + if(fw_version_crc_check) { + if (crc32_fw_check && crc32_check_factory +#ifdef CONFIG_COMPANION_USE + && crc32_c1_fw_check && crc32_c1_check_factory +#endif + ) { + return sprintf(buf, "%s %s\n", finfo->header_ver, loaded_fw); + } else { + strcpy(command_ack, "NG_"); + if (!crc32_fw_check) + strcat(command_ack, "FW"); + if (!crc32_check_factory) + strcat(command_ack, "CD"); +#ifdef CONFIG_COMPANION_USE + if (!crc32_c1_fw_check) + strcat(command_ack, "FW1"); + if (!crc32_c1_check_factory) + strcat(command_ack, "CD1"); +#endif + if (finfo->header_ver[3] != 'L') + strcat(command_ack, "_Q"); + return sprintf(buf, "%s %s\n", finfo->header_ver, command_ack); + } + } else { + strcpy(command_ack, "NG_"); +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + strcat(command_ack, "CD"); +#else + strcat(command_ack, "FWCD"); +#endif +#ifdef CONFIG_COMPANION_USE + strcat(command_ack, "FW1CD1"); +#endif + return sprintf(buf, "%s %s\n", finfo->header_ver, command_ack); + } +} + +static ssize_t camera_rear_camfw_full_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char command_ack[20] = {0, }; + char *loaded_fw; + + read_from_firmware_version(); + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + loaded_fw = pinfo->header_ver; +#else + fimc_is_sec_get_loaded_fw(&loaded_fw); +#endif + + if(fw_version_crc_check) { + if (crc32_fw_check && crc32_check_factory +#ifdef CONFIG_COMPANION_USE + && crc32_c1_fw_check && crc32_c1_check_factory +#endif + ) { + return sprintf(buf, "%s %s %s\n", finfo->header_ver, pinfo->header_ver, loaded_fw); + } else { + strcpy(command_ack, "NG_"); + if (!crc32_fw_check) + strcat(command_ack, "FW"); + if (!crc32_check_factory) + strcat(command_ack, "CD"); +#ifdef CONFIG_COMPANION_USE + if (!crc32_c1_fw_check) + strcat(command_ack, "FW1"); + if (!crc32_c1_check_factory) + strcat(command_ack, "CD1"); +#endif + if (finfo->header_ver[3] != 'L') + strcat(command_ack, "_Q"); + return sprintf(buf, "%s %s %s\n", finfo->header_ver, pinfo->header_ver, command_ack); + } + } else { + strcpy(command_ack, "NG_"); +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + strcat(command_ack, "CD"); +#else + strcat(command_ack, "FWCD"); +#endif +#ifdef CONFIG_COMPANION_USE + strcat(command_ack, "FW1CD1"); +#endif + return sprintf(buf, "%s %s %s\n", finfo->header_ver, pinfo->header_ver, command_ack); + } +} + +static ssize_t camera_rear_checkfw_user_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + read_from_firmware_version(); + + if(fw_version_crc_check) { + if (crc32_fw_check && crc32_check_factory +#ifdef CONFIG_COMPANION_USE + && crc32_c1_fw_check && crc32_c1_check_factory +#endif + ) { + if (!is_latest_cam_module +#if defined(CONFIG_SOC_EXYNOS5433) + || !is_right_prj_name +#endif + ) { + return sprintf(buf, "%s\n", "NG"); + } else { + return sprintf(buf, "%s\n", "OK"); + } + } else { + return sprintf(buf, "%s\n", "NG"); + } + } else { + return sprintf(buf, "%s\n", "NG"); + } +} + +static ssize_t camera_rear_checkfw_factory_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + read_from_firmware_version(); + + if(fw_version_crc_check) { + if (crc32_fw_check && crc32_check_factory +#ifdef CONFIG_COMPANION_USE + && crc32_c1_fw_check && crc32_c1_check_factory +#endif + ) { + if (!is_final_cam_module +#if defined(CONFIG_SOC_EXYNOS5433) + || !is_right_prj_name +#endif + ) { + return sprintf(buf, "%s\n", "NG"); + } else { + return sprintf(buf, "%s\n", "OK"); + } + } else { + return sprintf(buf, "%s\n", "NG"); + } + } else { + return sprintf(buf, "%s\n", "NG"); + } +} + +#ifdef CONFIG_COMPANION_USE +static ssize_t camera_rear_companionfw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *loaded_c1_fw; + + read_from_firmware_version(); + fimc_is_sec_get_loaded_c1_fw(&loaded_c1_fw); + + return sprintf(buf, "%s %s\n", + finfo->concord_header_ver, loaded_c1_fw); +} + +static ssize_t camera_rear_companionfw_full_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *loaded_c1_fw; + + read_from_firmware_version(); + fimc_is_sec_get_loaded_c1_fw(&loaded_c1_fw); + + return sprintf(buf, "%s %s %s\n", + finfo->concord_header_ver, pinfo->concord_header_ver, loaded_c1_fw); +} +#endif + +static ssize_t camera_rear_camfw_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t ret = -EINVAL; + + if ((size == 1 || size == 2) && (buf[0] == 'F' || buf[0] == 'f')) { + fimc_is_sec_set_force_caldata_dump(true); + ret = size; + } else { + fimc_is_sec_set_force_caldata_dump(false); + } + return ret; +} + +static ssize_t camera_rear_calcheck_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char rear_sensor[10] = {0, }; +#ifdef CONFIG_COMPANION_USE + char rear_companion[10] = {0, }; +#endif + + read_from_firmware_version(); + + if (crc32_check_factory) + strcpy(rear_sensor, "Normal"); + else + strcpy(rear_sensor, "Abnormal"); + +#ifdef CONFIG_COMPANION_USE + if (crc32_c1_check_factory) + strcpy(rear_companion, "Normal"); + else + strcpy(rear_companion, "Abnormal"); + + return sprintf(buf, "%s %s %s\n", rear_sensor, rear_companion, "Null"); +#else + return sprintf(buf, "%s %s\n", rear_sensor, "Null"); +#endif +} + +#ifdef CONFIG_COMPANION_USE +static ssize_t camera_isp_core_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int sel; + + if (DCDC_VENDOR_NONE == sysfs_core->companion_dcdc.type) + return sprintf(buf, "none\n"); + + sel = fimc_is_power_binning(sysfs_core); + return sprintf(buf, "%s\n", sysfs_core->companion_dcdc.get_vout_str(sel)); +} +#endif + +#ifdef CONFIG_OIS_USE +static ssize_t camera_ois_power_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + +{ + switch (buf[0]) { + case '0': + fimc_is_ois_gpio_off(sysfs_core->companion); + break; + case '1': + fimc_is_ois_gpio_on(sysfs_core->companion); + msleep(150); + break; + default: + pr_debug("%s: %c\n", __func__, buf[0]); + break; + } + + return count; +} + +static ssize_t camera_ois_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result_total = 0; + bool result_offset = 0, result_selftest = 0; + int selftest_ret = 0; + long raw_data_x = 0, raw_data_y = 0; + + fimc_is_ois_offset_test(sysfs_core, &raw_data_x, &raw_data_y); + msleep(50); + selftest_ret = fimc_is_ois_self_test(sysfs_core); + + if (selftest_ret == 0x0) { + result_selftest = true; + } else { + result_selftest = false; + } + + if (abs(raw_data_x) > 35000 || abs(raw_data_y) > 35000) { + result_offset = false; + } else { + result_offset = true; + } + + if (result_offset && result_selftest) { + result_total = 0; + } else if (!result_offset && !result_selftest) { + result_total = 3; + } else if (!result_offset) { + result_total = 1; + } else if (!result_selftest) { + result_total = 2; + } + + if (raw_data_x < 0 && raw_data_y < 0) { + return sprintf(buf, "%d,-%ld.%03ld,-%ld.%03ld\n", result_total, abs(raw_data_x /1000), abs(raw_data_x % 1000), + abs(raw_data_y /1000), abs(raw_data_y % 1000)); + } else if (raw_data_x < 0) { + return sprintf(buf, "%d,-%ld.%03ld,%ld.%03ld\n", result_total, abs(raw_data_x /1000), abs(raw_data_x % 1000), + raw_data_y /1000, raw_data_y % 1000); + } else if (raw_data_y < 0) { + return sprintf(buf, "%d,%ld.%03ld,-%ld.%03ld\n", result_total, raw_data_x /1000, raw_data_x % 1000, + abs(raw_data_y /1000), abs(raw_data_y % 1000)); + } else { + return sprintf(buf, "%d,%ld.%03ld,%ld.%03ld\n", result_total, raw_data_x /1000, raw_data_x % 1000, + raw_data_y /1000, raw_data_y % 1000); + } +} + +static ssize_t camera_ois_rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + long raw_data_x = 0, raw_data_y = 0; + + fimc_is_ois_get_offset_data(sysfs_core, &raw_data_x, &raw_data_y); + + if (raw_data_x < 0 && raw_data_y < 0) { + return sprintf(buf, "-%ld.%03ld,-%ld.%03ld\n", abs(raw_data_x /1000), abs(raw_data_x % 1000), + abs(raw_data_y /1000), abs(raw_data_y % 1000)); + } else if (raw_data_x < 0) { + return sprintf(buf, "-%ld.%03ld,%ld.%03ld\n", abs(raw_data_x /1000), abs(raw_data_x % 1000), + raw_data_y /1000, raw_data_y % 1000); + } else if (raw_data_y < 0) { + return sprintf(buf, "%ld.%03ld,-%ld.%03ld\n", raw_data_x /1000, raw_data_x % 1000, + abs(raw_data_y /1000), abs(raw_data_y % 1000)); + } else { + return sprintf(buf, "%ld.%03ld,%ld.%03ld\n", raw_data_x /1000, raw_data_x % 1000, + raw_data_y /1000, raw_data_y % 1000); + } +} + +static ssize_t camera_ois_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fimc_is_ois_info *ois_minfo = NULL; + struct fimc_is_ois_info *ois_pinfo = NULL; + u8 checksum = 0, caldata = 0; + + if (!sysfs_core->running_rear_camera) { + fimc_is_ois_gpio_on(sysfs_core->companion); + msleep(150); + if (!sysfs_core->ois_ver_read) { + fimc_is_ois_check_fw(sysfs_core); + } + + fimc_is_ois_fw_status(sysfs_core, &checksum, &caldata); + + if (!sysfs_core->running_rear_camera) { + fimc_is_ois_gpio_off(sysfs_core->companion); + } + } + + fimc_is_ois_get_module_version(&ois_minfo); + fimc_is_ois_get_phone_version(&ois_pinfo); + + if (checksum != 0x00) { + return sprintf(buf, "%s %s\n", "NG_FW2", "NULL"); + } else if (caldata != 0x00) { + return sprintf(buf, "%s %s\n", "NG_CD2", ois_pinfo->header_ver); + } else { + return sprintf(buf, "%s %s\n", ois_minfo->header_ver, ois_pinfo->header_ver); + } +} + +static ssize_t camera_ois_diff_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result = 0; + int x_diff = 0, y_diff = 0; + + result = fimc_is_ois_diff_test(sysfs_core, &x_diff, &y_diff); + + return sprintf(buf, "%d,%d,%d\n", result == true ? 0 : 1, x_diff, y_diff); +} + +static ssize_t camera_ois_fw_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE + fimc_is_ois_init_thread(sysfs_core); +#else + fimc_is_ois_gpio_on(sysfs_core->companion); + msleep(150); + + fimc_is_ois_fw_update(sysfs_core); + fimc_is_ois_gpio_off(sysfs_core->companion); +#endif + + return sprintf(buf, "%s\n", "Ois update done."); +} + +static ssize_t camera_ois_exif_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fimc_is_ois_info *ois_minfo = NULL; + struct fimc_is_ois_info *ois_pinfo = NULL; + struct fimc_is_ois_info *ois_uinfo = NULL; + struct fimc_is_ois_exif *ois_exif = NULL; + + fimc_is_ois_get_module_version(&ois_minfo); + fimc_is_ois_get_phone_version(&ois_pinfo); + fimc_is_ois_get_user_version(&ois_uinfo); + fimc_is_ois_get_exif_data(&ois_exif); + + return sprintf(buf, "%s %s %s %d %d", ois_minfo->header_ver, ois_pinfo->header_ver, + ois_uinfo->header_ver, ois_exif->error_data, ois_exif->status_data); +} +#endif + +static DEVICE_ATTR(rear_camtype, S_IRUGO, + camera_rear_camtype_show, NULL); +static DEVICE_ATTR(rear_camfw, S_IRUGO, + camera_rear_camfw_show, camera_rear_camfw_write); +static DEVICE_ATTR(rear_camfw_full, S_IRUGO, + camera_rear_camfw_full_show, NULL); +#ifdef CONFIG_COMPANION_USE +static DEVICE_ATTR(rear_companionfw, S_IRUGO, + camera_rear_companionfw_show, NULL); +static DEVICE_ATTR(rear_companionfw_full, S_IRUGO, + camera_rear_companionfw_full_show, NULL); +#endif +static DEVICE_ATTR(rear_calcheck, S_IRUGO, + camera_rear_calcheck_show, NULL); +static DEVICE_ATTR(rear_checkfw_user, S_IRUGO, + camera_rear_checkfw_user_show, NULL); +static DEVICE_ATTR(rear_checkfw_factory, S_IRUGO, + camera_rear_checkfw_factory_show, NULL); +#ifdef CONFIG_COMPANION_USE +static DEVICE_ATTR(isp_core, S_IRUGO, + camera_isp_core_show, NULL); +#endif +#ifdef CONFIG_OIS_USE +static DEVICE_ATTR(selftest, S_IRUGO, + camera_ois_selftest_show, NULL); +static DEVICE_ATTR(ois_power, S_IWUSR, + NULL, camera_ois_power_store); +static DEVICE_ATTR(ois_rawdata, S_IRUGO, + camera_ois_rawdata_show, NULL); +static DEVICE_ATTR(oisfw, S_IRUGO, + camera_ois_version_show, NULL); +static DEVICE_ATTR(ois_diff, S_IRUGO, + camera_ois_diff_show, NULL); +static DEVICE_ATTR(fw_update, S_IRUGO, + camera_ois_fw_update_show, NULL); +static DEVICE_ATTR(ois_exif, S_IRUGO, + camera_ois_exif_show, NULL); +#endif +#endif /* CONFIG_USE_VENDER_FEATURE */ + +static int fimc_is_suspend(struct device *dev) +{ + pr_debug("FIMC_IS Suspend\n"); + return 0; +} + +static int fimc_is_resume(struct device *dev) +{ + pr_debug("FIMC_IS Resume\n"); + return 0; +} + +#ifdef USE_OWN_FAULT_HANDLER +static void fimc_is_print_minfo(struct fimc_is_minfo *minfo) +{ + if (minfo) { + err("dvaddr : 0x%08x, size : %d", minfo->dvaddr, minfo->size); + err("dvaddr_debug : 0x%08x, dvaddr_region : 0x%08x", minfo->dvaddr_debug, minfo->dvaddr_region); + err("dvaddr_shared : 0x%08x, dvaddr_odc : 0x%08x", minfo->dvaddr_shared, minfo->dvaddr_odc); + err("dvaddr_dis : 0x%08x, dvaddr_3dnr : 0x%08x", minfo->dvaddr_dis, minfo->dvaddr_3dnr); + } else { + err("core->minfo is NULL"); + } +} + +static void __fimc_is_fault_handler(struct device *dev) +{ + u32 i, j, k; + struct fimc_is_core *core; + struct fimc_is_device_sensor *sensor; + struct fimc_is_device_ischain *ischain; + struct fimc_is_framemgr *framemgr; + + core = dev_get_drvdata(dev); + if (core) { + + fimc_is_print_minfo(&core->minfo); + fimc_is_hw_logdump(&core->interface); + /* dump FW page table 1nd(~16KB), 2nd(16KB~32KB) */ + fimc_is_hw_memdump(&core->interface, + core->minfo.kvaddr + 0x010F8000 /* TTB_BASE ~ 32KB */, + core->minfo.kvaddr + 0x010F8000 + 0x8000); + + /* REAR SENSOR */ + sensor = &core->sensor[0]; + if (test_bit(FIMC_IS_SENSOR_OPEN, &sensor->state)) { + framemgr = &sensor->vctx->q_dst->framemgr; + for (i = 0; i < FRAMEMGR_MAX_REQUEST; ++i) { + pr_err("LITE0 BUF[%d][0] = %X, 0x%08X, 0x%08X\n", i, + (u32)framemgr->frame[i].memory, + framemgr->frame[i].dvaddr_buffer[0], + framemgr->frame[i].kvaddr_buffer[0]); + } + } + + /* FRONT SENSOR */ + sensor = &core->sensor[1]; + if (test_bit(FIMC_IS_SENSOR_OPEN, &sensor->state)) { + framemgr = &sensor->vctx->q_dst->framemgr; + for (i = 0; i < FRAMEMGR_MAX_REQUEST; ++i) { + pr_err("LITE1 BUF[%d][0] = %X, 0x%08X. 0x%08X\n", i, + (u32)framemgr->frame[i].memory, + framemgr->frame[i].dvaddr_buffer[0], + framemgr->frame[i].kvaddr_buffer[0]); + } + } + + /* ISCHAIN */ + for (i = 0; i < FIMC_IS_MAX_NODES; i++) { + if (test_bit(FIMC_IS_ISCHAIN_OPEN, &(core->ischain[i].state))) { + ischain = &core->ischain[i]; + /* 3AA */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->group_3aa.leader.state)) { + framemgr = &ischain->group_3aa.leader.vctx->q_src->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[3AA:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* 3AAC */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->taac.state)) { + framemgr = &ischain->taac.leader->vctx->q_dst->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[3AAC:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* 3AAP */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->taap.state)) { + framemgr = &ischain->taap.leader->vctx->q_dst->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[3AAP:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* ISP */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->group_isp.leader.state)) { + framemgr = &ischain->group_isp.leader.vctx->q_src->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[ISP:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* SCC */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->scc.state)) { + framemgr = &ischain->scc.vctx->q_dst->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[SCC:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* VDC */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->dis.state)) { + framemgr = &ischain->dis.vctx->q_dst->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[VDC:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* VDO */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->group_dis.leader.state)) { + framemgr = &ischain->group_dis.leader.vctx->q_src->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[VDO:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + /* SCP */ + if (test_bit(FIMC_IS_SUBDEV_START, &ischain->scp.state)) { + framemgr = &ischain->scp.vctx->q_dst->framemgr; + for (j = 0; j < framemgr->frame_cnt; ++j) { + for (k = 0; k < framemgr->frame[j].planes; k++) { + pr_err("[SCP:%d] BUF[%d][%d] = %X, 0x%08X, 0x%08X\n", + i, j, k, + (u32)framemgr->frame[j].memory, + framemgr->frame[j].dvaddr_buffer[k], + framemgr->frame[j].kvaddr_buffer[k]); + } + } + } + } + } + } else { + pr_err("failed to get core\n"); + } +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) +#define SECT_ORDER 20 +#define LPAGE_ORDER 16 +#define SPAGE_ORDER 12 + +#define lv1ent_page(sent) ((*(sent) & 3) == 1) + +#define lv1ent_offset(iova) ((iova) >> SECT_ORDER) +#define lv2ent_offset(iova) (((iova) & 0xFF000) >> SPAGE_ORDER) +#define lv2table_base(sent) (*(sent) & 0xFFFFFC00) + +static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) +{ + return pgtable + lv1ent_offset(iova); +} + +static unsigned long *page_entry(unsigned long *sent, unsigned long iova) +{ + return (unsigned long *)__va(lv2table_base(sent)) + lv2ent_offset(iova); +} + +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { + "PAGE FAULT", + "AR MULTI-HIT FAULT", + "AW MULTI-HIT FAULT", + "BUS ERROR", + "AR SECURITY PROTECTION FAULT", + "AR ACCESS PROTECTION FAULT", + "AW SECURITY PROTECTION FAULT", + "AW ACCESS PROTECTION FAULT", + "UNKNOWN FAULT" +}; + +static int fimc_is_fault_handler(struct device *dev, const char *mmuname, + enum exynos_sysmmu_inttype itype, + unsigned long pgtable_base, + unsigned long fault_addr) +{ + unsigned long *ent; + + if ((itype >= SYSMMU_FAULTS_NUM) || (itype < SYSMMU_PAGEFAULT)) + itype = SYSMMU_FAULT_UNKNOWN; + + pr_err("%s occured at 0x%lx by '%s'(Page table base: 0x%lx)\n", + sysmmu_fault_name[itype], fault_addr, mmuname, pgtable_base); + + ent = section_entry(__va(pgtable_base), fault_addr); + pr_err("\tLv1 entry: 0x%lx\n", *ent); + + if (lv1ent_page(ent)) { + ent = page_entry(ent, fault_addr); + pr_err("\t Lv2 entry: 0x%lx\n", *ent); + } + + __fimc_is_fault_handler(dev); + + pr_err("Generating Kernel OOPS... because it is unrecoverable.\n"); + + BUG(); + + return 0; +} +#else +static int fimc_is_fault_handler(struct iommu_domain *domain, + struct device *dev, + unsigned long fault_addr, + int fault_flag, + void *token) +{ + pr_err("\n"); + pr_err("Device virtual(0x%X) is invalid access\n", (u32)fault_addr); + + __fimc_is_fault_handler(dev); + + return -EINVAL; +} +#endif +#endif /* USE_OWN_FAULT_HANDLER */ + +static ssize_t show_clk_gate_mode(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sysfs_debug.clk_gate_mode); +} + +static ssize_t store_clk_gate_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef HAS_FW_CLOCK_GATE + switch (buf[0]) { + case '0': + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_HOST; + break; + case '1': + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_FW; + break; + default: + pr_debug("%s: %c\n", __func__, buf[0]); + break; + } +#endif + return count; +} + +static ssize_t show_en_clk_gate(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sysfs_debug.en_clk_gate); +} + +static ssize_t store_en_clk_gate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef ENABLE_CLOCK_GATE + switch (buf[0]) { + case '0': + sysfs_debug.en_clk_gate = false; + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_HOST; + break; + case '1': + sysfs_debug.en_clk_gate = true; + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_HOST; + break; + default: + pr_debug("%s: %c\n", __func__, buf[0]); + break; + } +#endif + return count; +} + +static ssize_t show_en_dvfs(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sysfs_debug.en_dvfs); +} + +static ssize_t store_en_dvfs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef ENABLE_DVFS + struct fimc_is_core *core = + (struct fimc_is_core *)platform_get_drvdata(to_platform_device(dev)); + struct fimc_is_resourcemgr *resourcemgr; + int i; + + BUG_ON(!core); + + resourcemgr = &core->resourcemgr; + + switch (buf[0]) { + case '0': + sysfs_debug.en_dvfs = false; + /* update dvfs lever to max */ + mutex_lock(&resourcemgr->dvfs_ctrl.lock); + for (i = 0; i < FIMC_IS_MAX_NODES; i++) { + if (test_bit(FIMC_IS_ISCHAIN_OPEN, &((core->ischain[i]).state))) + fimc_is_set_dvfs(&(core->ischain[i]), FIMC_IS_SN_MAX); + } + fimc_is_dvfs_init(resourcemgr); + resourcemgr->dvfs_ctrl.static_ctrl->cur_scenario_id = FIMC_IS_SN_MAX; + mutex_unlock(&resourcemgr->dvfs_ctrl.lock); + break; + case '1': + /* It can not re-define static scenario */ + sysfs_debug.en_dvfs = true; + break; + default: + pr_debug("%s: %c\n", __func__, buf[0]); + break; + } +#endif + return count; +} + +static DEVICE_ATTR(en_clk_gate, 0644, show_en_clk_gate, store_en_clk_gate); +static DEVICE_ATTR(clk_gate_mode, 0644, show_clk_gate_mode, store_clk_gate_mode); +static DEVICE_ATTR(en_dvfs, 0644, show_en_dvfs, store_en_dvfs); + +static struct attribute *fimc_is_debug_entries[] = { + &dev_attr_en_clk_gate.attr, + &dev_attr_clk_gate_mode.attr, + &dev_attr_en_dvfs.attr, + NULL, +}; +static struct attribute_group fimc_is_debug_attr_group = { + .name = "debug", + .attrs = fimc_is_debug_entries, +}; + +static int fimc_is_probe(struct platform_device *pdev) +{ + struct exynos_platform_fimc_is *pdata; + struct resource *mem_res; + struct resource *regs_res; + struct fimc_is_core *core; + int ret = -ENODEV; + + info("%s:start\n", __func__); + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { +#ifdef CONFIG_OF + pdata = fimc_is_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) +#endif + return PTR_ERR(pdata); + } + + core = kzalloc(sizeof(struct fimc_is_core), GFP_KERNEL); + if (!core) { + err("core is NULL"); + return -ENOMEM; + } + + fimc_is_dev = &pdev->dev; + ret = dev_set_drvdata(fimc_is_dev, core); + if (ret) { + err("dev_set_drvdata is fail(%d)", ret); + kfree(core); + return ret; + } + +#ifdef CONFIG_COMPANION_USE + core->companion_spi_channel = pdata->companion_spi_channel; + core->use_two_spi_line = pdata->use_two_spi_line; +#endif +#ifdef CONFIG_USE_VENDER_FEATURE + core->use_sensor_dynamic_voltage_mode = pdata->use_sensor_dynamic_voltage_mode; +#ifdef CONFIG_OIS_USE + core->use_ois = pdata->use_ois; +#endif /* CONFIG_OIS_USE */ + core->use_ois_hsi2c = pdata->use_ois_hsi2c; + core->use_module_check = pdata->use_module_check; +#endif + +#ifdef USE_ION_ALLOC + core->fimc_ion_client = ion_client_create(ion_exynos, "fimc-is"); +#endif + core->pdev = pdev; + core->pdata = pdata; + core->id = pdev->id; + core->debug_cnt = 0; + core->running_rear_camera = false; + core->running_front_camera = false; + + device_init_wakeup(&pdev->dev, true); + + /* init mutex for spi read */ + mutex_init(&core->spi_lock); + + /* for mideaserver force down */ + atomic_set(&core->rsccount, 0); + clear_bit(FIMC_IS_ISCHAIN_POWER_ON, &core->state); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "Failed to get io memory region\n"); + goto p_err1; + } + + regs_res = request_mem_region(mem_res->start, resource_size(mem_res), + pdev->name); + if (!regs_res) { + dev_err(&pdev->dev, "Failed to request io memory region\n"); + goto p_err1; + } + + core->regs_res = regs_res; + core->regs = ioremap_nocache(mem_res->start, resource_size(mem_res)); + if (!core->regs) { + dev_err(&pdev->dev, "Failed to remap io region\n"); + goto p_err2; + } + + core->irq = platform_get_irq(pdev, 0); + if (core->irq < 0) { + dev_err(&pdev->dev, "Failed to get irq\n"); + goto p_err3; + } + + ret = fimc_is_mem_probe(&core->mem, core->pdev); + if (ret) { + err("fimc_is_mem_probe is fail(%d)", ret); + goto p_err3; + } + + fimc_is_interface_probe(&core->interface, + (u32)core->regs, + core->irq, + core); + + fimc_is_resource_probe(&core->resourcemgr, core); + + /* group initialization */ + fimc_is_groupmgr_probe(&core->groupmgr); + +#if defined(CONFIG_CAMERA_SENSOR_6B2_OBJ) + ret = sensor_6b2_probe(NULL, NULL); + if (ret) { + err("sensor_6b2_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_8B1_OBJ) + ret = sensor_8b1_probe(NULL, NULL); + if (ret) { + err("sensor_8b1_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_6D1_OBJ) + ret = sensor_6d1_probe(NULL, NULL); + if (ret) { + err("sensor_6d1_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_6A3_OBJ) + ret = sensor_6a3_probe(NULL, NULL); + if (ret) { + err("sensor_6a3_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_IMX134_OBJ) + ret = sensor_imx134_probe(NULL, NULL); + if (ret) { + err("sensor_imx134_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_IMX135_OBJ) + ret = sensor_imx135_probe(NULL, NULL); + if (ret) { + err("sensor_imx135_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_3L2_OBJ) + ret = sensor_3l2_probe(NULL, NULL); + if (ret) { + err("sensor_3l2_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_2P2_OBJ) + ret = sensor_2p2_probe(NULL, NULL); + if (ret) { + err("sensor_2p2_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_2P2_12M_OBJ) + ret = sensor_2p2_12m_probe(NULL, NULL); + if (ret) { + err("sensor_2p2_12m_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_2P3_OBJ) + ret = sensor_2p3_probe(NULL, NULL); + if (ret) { + err("sensor_2p3_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_3H5_OBJ) + ret = sensor_3h5_probe(NULL, NULL); + if (ret) { + err("sensor_3h5_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_3H7_OBJ) + ret = sensor_3h7_probe(NULL, NULL); + if (ret) { + err("sensor_3h7_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_3H7_SUNNY_OBJ) + ret = sensor_3h7_sunny_probe(NULL, NULL); + if (ret) { + err("sensor_3h7_sunny_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_4E5_OBJ) + ret = sensor_4e5_probe(NULL, NULL); + if (ret) { + err("sensor_4e5_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_IMX175_OBJ) + ret = sensor_imx175_probe(NULL, NULL); + if (ret) { + err("sensor_imx175_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_IMX240_OBJ) + ret = sensor_imx240_probe(NULL, NULL); + if (ret) { + err("sensor_imx175_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_IMX219_OBJ) + ret = sensor_imx219_probe(NULL, NULL); + if (ret) { + err("sensor_imx219_probe is fail(%d)", ret); + goto p_err3; + } +#endif + +#if defined(CONFIG_CAMERA_SENSOR_4H5_OBJ) + ret = sensor_4h5_probe(NULL, NULL); + if (ret) { + err("sensor_4h5_probe is fail(%d)", ret); + goto p_err3; + } +#endif + + /* device entity - ischain0 */ + fimc_is_ischain_probe(&core->ischain[0], + &core->interface, + &core->resourcemgr, + &core->groupmgr, + &core->mem, + core->pdev, + 0, + (u32)core->regs); + + /* device entity - ischain1 */ + fimc_is_ischain_probe(&core->ischain[1], + &core->interface, + &core->resourcemgr, + &core->groupmgr, + &core->mem, + core->pdev, + 1, + (u32)core->regs); + + /* device entity - ischain2 */ + fimc_is_ischain_probe(&core->ischain[2], + &core->interface, + &core->resourcemgr, + &core->groupmgr, + &core->mem, + core->pdev, + 2, + (u32)core->regs); + + ret = v4l2_device_register(&pdev->dev, &core->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "failed to register fimc-is v4l2 device\n"); + goto p_err4; + } + + /* video entity - 3a0 */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, 3a0)) + fimc_is_3a0_video_probe(core); + + /* video entity - 3a0 capture */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, 3a0)) + fimc_is_3a0c_video_probe(core); + + /* video entity - 3a1 */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, 3a1)) + fimc_is_3a1_video_probe(core); + + /* video entity - 3a1 capture */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, 3a1)) + fimc_is_3a1c_video_probe(core); + + /* video entity - isp */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, isp)) + fimc_is_isp_video_probe(core); + + /*front video entity - scalerC */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, scc)) + fimc_is_scc_video_probe(core); + + /* back video entity - scalerP*/ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, scp)) + fimc_is_scp_video_probe(core); + + if (GET_FIMC_IS_NUM_OF_SUBIP(core, dis)) { + /* vdis video entity - vdis capture*/ + fimc_is_vdc_video_probe(core); + /* vdis video entity - vdis output*/ + fimc_is_vdo_video_probe(core); + } + + platform_set_drvdata(pdev, core); + + ret = fimc_is_ishcain_initmem(core); + if (ret) { + err("fimc_is_ishcain_initmem is fail(%d)", ret); + goto p_err4; + } + + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#if defined(CONFIG_VIDEOBUF2_ION) + if (core->mem.alloc_ctx) + vb2_ion_attach_iommu(core->mem.alloc_ctx); +#endif +#endif + EXYNOS_MIF_ADD_NOTIFIER(&exynos_fimc_is_mif_throttling_nb); + +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_enable(&pdev->dev); +#endif + +#ifdef CONFIG_USE_VENDER_FEATURE + if (camera_class == NULL) { + camera_class = class_create(THIS_MODULE, "camera"); + if (IS_ERR(camera_class)) { + pr_err("Failed to create class(camera)!\n"); + ret = PTR_ERR(camera_class); + goto p_err5; + } + } + + camera_front_dev = device_create(camera_class, NULL, 0, NULL, "front"); + if (IS_ERR(camera_front_dev)) { + printk(KERN_ERR "failed to create front device!\n"); + } else { + if (device_create_file(camera_front_dev, + &dev_attr_front_sensorid) < 0) { + printk(KERN_ERR "failed to create front device file, %s\n", + dev_attr_front_sensorid.attr.name); + } + + if (device_create_file(camera_front_dev, + &dev_attr_front_camtype) + < 0) { + printk(KERN_ERR + "failed to create front device file, %s\n", + dev_attr_front_camtype.attr.name); + } + if (device_create_file(camera_front_dev, + &dev_attr_front_camfw) < 0) { + printk(KERN_ERR + "failed to create front device file, %s\n", + dev_attr_front_camfw.attr.name); + } + } + camera_rear_dev = device_create(camera_class, NULL, 1, NULL, "rear"); + if (IS_ERR(camera_rear_dev)) { + printk(KERN_ERR "failed to create rear device!\n"); + } else { + if (device_create_file(camera_rear_dev, + &dev_attr_rear_sensorid) < 0) { + printk(KERN_ERR "failed to create rear device file, %s\n", + dev_attr_rear_sensorid.attr.name); + } + + if (device_create_file(camera_rear_dev, &dev_attr_rear_camtype) + < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_camtype.attr.name); + } + if (device_create_file(camera_rear_dev, + &dev_attr_rear_camfw) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_camfw.attr.name); + } + if (device_create_file(camera_rear_dev, + &dev_attr_rear_camfw_full) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_camfw_full.attr.name); + } + if (device_create_file(camera_rear_dev, + &dev_attr_rear_checkfw_user) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_checkfw_user.attr.name); + } + if (device_create_file(camera_rear_dev, + &dev_attr_rear_checkfw_factory) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_checkfw_factory.attr.name); + } +#ifdef CONFIG_COMPANION_USE + if (device_create_file(camera_rear_dev, + &dev_attr_rear_companionfw) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_companionfw.attr.name); + } + if (device_create_file(camera_rear_dev, + &dev_attr_rear_companionfw_full) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_companionfw_full.attr.name); + } +#endif + if (device_create_file(camera_rear_dev, + &dev_attr_rear_calcheck) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_rear_calcheck.attr.name); + } +#ifdef CONFIG_COMPANION_USE + if (device_create_file(camera_rear_dev, + &dev_attr_isp_core) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_isp_core.attr.name); + } +#endif +#ifdef CONFIG_OIS_USE + if (device_create_file(camera_rear_dev, + &dev_attr_fw_update) < 0) { + printk(KERN_ERR + "failed to create rear device file, %s\n", + dev_attr_fw_update.attr.name); + } +#endif + } + +#ifdef CONFIG_OIS_USE + camera_ois_dev = device_create(camera_class, NULL, 2, NULL, "ois"); + if (IS_ERR(camera_ois_dev)) { + printk(KERN_ERR "failed to create ois device!\n"); + } else { + if (device_create_file(camera_ois_dev, + &dev_attr_selftest) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_selftest.attr.name); + } + if (device_create_file(camera_ois_dev, + &dev_attr_ois_power) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_ois_power.attr.name); + } + if (device_create_file(camera_ois_dev, + &dev_attr_ois_rawdata) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_ois_rawdata.attr.name); + } + if (device_create_file(camera_ois_dev, + &dev_attr_oisfw) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_oisfw.attr.name); + } + if (device_create_file(camera_ois_dev, + &dev_attr_ois_diff) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_ois_diff.attr.name); + } + if (device_create_file(camera_ois_dev, + &dev_attr_ois_exif) < 0) { + printk(KERN_ERR + "failed to create ois device file, %s\n", + dev_attr_ois_exif.attr.name); + } + } +#endif + + sysfs_core = core; +#endif + +#ifdef USE_OWN_FAULT_HANDLER +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) + exynos_sysmmu_set_fault_handler(fimc_is_dev, fimc_is_fault_handler); +#else + iovmm_set_fault_handler(fimc_is_dev, fimc_is_fault_handler, NULL); +#endif +#endif + + dbg("%s : fimc_is_front_%d probe success\n", __func__, pdev->id); + + /* set sysfs for debuging */ + sysfs_debug.en_clk_gate = 0; + sysfs_debug.en_dvfs = 1; +#ifdef ENABLE_CLOCK_GATE + sysfs_debug.en_clk_gate = 1; +#ifdef HAS_FW_CLOCK_GATE + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_FW; +#else + sysfs_debug.clk_gate_mode = CLOCK_GATE_MODE_HOST; +#endif +#endif + ret = sysfs_create_group(&core->pdev->dev.kobj, &fimc_is_debug_attr_group); + +#ifdef ENABLE_DVFS + { + struct fimc_is_resourcemgr *resourcemgr; + resourcemgr = &core->resourcemgr; + /* dvfs controller init */ + ret = fimc_is_dvfs_init(resourcemgr); + if (ret) + err("%s: fimc_is_dvfs_init failed!\n", __func__); + } +#endif + + info("%s:end\n", __func__); + return 0; + +#ifdef CONFIG_USE_VENDER_FEATURE +p_err5: +#if defined(CONFIG_PM_RUNTIME) + __pm_runtime_disable(&pdev->dev, false); +#endif +#endif /* CONFIG_USE_VENDER_FEATURE */ +p_err4: + v4l2_device_unregister(&core->v4l2_dev); +p_err3: + iounmap(core->regs); +p_err2: + release_mem_region(regs_res->start, resource_size(regs_res)); +p_err1: + kfree(core); + return ret; +} + +static int fimc_is_remove(struct platform_device *pdev) +{ + dbg("%s\n", __func__); + + if (camera_front_dev) { + device_remove_file(camera_front_dev, &dev_attr_front_sensorid); + device_remove_file(camera_front_dev, &dev_attr_front_camtype); + device_remove_file(camera_front_dev, &dev_attr_front_camfw); + } + + if (camera_rear_dev) { + device_remove_file(camera_rear_dev, &dev_attr_rear_sensorid); + device_remove_file(camera_rear_dev, &dev_attr_rear_camtype); + device_remove_file(camera_rear_dev, &dev_attr_rear_camfw); + device_remove_file(camera_rear_dev, &dev_attr_rear_camfw_full); + device_remove_file(camera_rear_dev, &dev_attr_rear_checkfw_user); + device_remove_file(camera_rear_dev, &dev_attr_rear_checkfw_factory); +#ifdef CONFIG_COMPANION_USE + device_remove_file(camera_rear_dev, &dev_attr_rear_companionfw); + device_remove_file(camera_rear_dev, &dev_attr_rear_companionfw_full); +#endif + device_remove_file(camera_rear_dev, &dev_attr_rear_calcheck); +#ifdef CONFIG_COMPANION_USE + device_remove_file(camera_rear_dev, &dev_attr_isp_core); +#endif +#ifdef CONFIG_OIS_USE + device_remove_file(camera_ois_dev, &dev_attr_fw_update); +#endif + } + +#ifdef CONFIG_OIS_USE + if (camera_ois_dev) { + device_remove_file(camera_ois_dev, &dev_attr_selftest); + device_remove_file(camera_ois_dev, &dev_attr_ois_power); + device_remove_file(camera_ois_dev, &dev_attr_ois_rawdata); + device_remove_file(camera_ois_dev, &dev_attr_oisfw); + device_remove_file(camera_ois_dev, &dev_attr_ois_diff); + device_remove_file(camera_ois_dev, &dev_attr_ois_exif); + } +#endif + + if (camera_class) { + if (camera_front_dev) + device_destroy(camera_class, camera_front_dev->devt); + + if (camera_rear_dev) + device_destroy(camera_class, camera_rear_dev->devt); + +#ifdef CONFIG_OIS_USE + if (camera_ois_dev) + device_destroy(camera_class, camera_ois_dev->devt); +#endif + } + + class_destroy(camera_class); + + return 0; +} + +static const struct dev_pm_ops fimc_is_pm_ops = { + .suspend = fimc_is_suspend, + .resume = fimc_is_resume, + .runtime_suspend = fimc_is_runtime_suspend, + .runtime_resume = fimc_is_runtime_resume, +}; + +#ifdef CONFIG_USE_VENDER_FEATURE +#if defined(CONFIG_COMPANION_USE) +static int fimc_is_i2c0_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_core *core; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) + goto probe_defer; + + core->client0 = client; + + pr_info("%s %s: fimc_is_i2c0 driver probed!\n", + dev_driver_string(&client->dev), dev_name(&client->dev)); + + return 0; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + BUG(); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; +} + +static int fimc_is_i2c0_remove(struct i2c_client *client) +{ + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id fimc_is_i2c0_dt_ids[] = { + { .compatible = "samsung,fimc_is_i2c0",}, + {}, +}; +MODULE_DEVICE_TABLE(of, fimc_is_i2c0_dt_ids); +#endif + +static const struct i2c_device_id fimc_is_i2c0_id[] = { + {"fimc_is_i2c0", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, fimc_is_i2c0_id); + +static struct i2c_driver fimc_is_i2c0_driver = { + .driver = { + .name = "fimc_is_i2c0", + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = fimc_is_i2c0_dt_ids, +#endif + }, + .probe = fimc_is_i2c0_probe, + .remove = fimc_is_i2c0_remove, + .id_table = fimc_is_i2c0_id, +}; +module_i2c_driver(fimc_is_i2c0_driver); +#endif + +#if defined(CONFIG_OF) && defined(CONFIG_COMPANION_USE) +static int of_fimc_is_spi_dt(struct device *dev, struct fimc_is_spi_gpio *spi_gpio, struct fimc_is_core *core) +{ + struct device_node *np; + int ret; + + np = of_find_compatible_node(NULL,NULL,"samsung,fimc_is_spi1"); + if(np == NULL) { + pr_err("compatible: fail to read, spi_parse_dt\n"); + return -ENODEV; + } + + ret = of_property_read_string(np, "fimc_is_spi_sclk", (const char **) &spi_gpio->spi_sclk); + if (ret) { + pr_err("spi gpio: fail to read, spi_parse_dt\n"); + return -ENODEV; + } + + ret = of_property_read_string(np, "fimc_is_spi_ssn",(const char **) &spi_gpio->spi_ssn); + if (ret) { + pr_err("spi gpio: fail to read, spi_parse_dt\n"); + return -ENODEV; + } + + ret = of_property_read_string(np, "fimc_is_spi_miso",(const char **) &spi_gpio->spi_miso); + if (ret) { + pr_err("spi gpio: fail to read, spi_parse_dt\n"); + return -ENODEV; + } + + ret = of_property_read_string(np, "fimc_is_spi_mois",(const char **) &spi_gpio->spi_mois); + if (ret) { + pr_err("spi gpio: fail to read, spi_parse_dt\n"); + return -ENODEV; + } + + pr_info("sclk = %s, ssn = %s, miso = %s, mois = %s spi_channel:(%d)\n", spi_gpio->spi_sclk, spi_gpio->spi_ssn, spi_gpio->spi_miso, spi_gpio->spi_mois,core->companion_spi_channel); + + return 0; +} +#endif +#endif + +#ifdef CONFIG_OF +static int fimc_is_spi_probe(struct spi_device *spi) +{ + int ret = 0; + struct fimc_is_core *core; + + BUG_ON(!fimc_is_dev); + + dbg_core("%s\n", __func__); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + spi->mode = SPI_MODE_0; + + /* spi->bits_per_word = 16; */ + if (spi_setup(spi)) { + pr_err("failed to setup spi for fimc_is_spi\n"); + ret = -EINVAL; + goto exit; + } + + if (!strncmp(spi->modalias, "fimc_is_spi0", 12)) + core->spi0 = spi; + + if (!strncmp(spi->modalias, "fimc_is_spi1", 12)) { + core->spi1 = spi; +#ifdef CONFIG_COMPANION_USE + ret = of_fimc_is_spi_dt(&spi->dev,&core->spi_gpio, core); + if (ret) { + pr_err("[%s] of_fimc_is_spi_dt parse dt failed\n", __func__); + return ret; + } +#endif + } + +exit: + return ret; +} + +static int fimc_is_spi_remove(struct spi_device *spi) +{ + return 0; +} + +#if defined(CONFIG_USE_VENDER_FEATURE) && defined(CONFIG_COMPANION_USE) +static int fimc_is_fan53555_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_core *core; + int ret = 0; +#ifdef CONFIG_SOC_EXYNOS5422 + struct regulator *regulator = NULL; + const char power_name[] = "CAM_IO_1.8V_AP"; +#endif + struct device_node *np; + int gpio_comp_en; + + BUG_ON(!fimc_is_dev); + + pr_info("%s start\n",__func__); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + np = of_find_compatible_node(NULL, NULL, "samsung,fimc_is_fan53555"); + if(np == NULL) { + pr_err("compatible: fail to read, fan_parse_dt\n"); + return -ENODEV; + } + + gpio_comp_en = of_get_named_gpio(np, "comp_en", 0); + if (!gpio_is_valid(gpio_comp_en)) + pr_err("failed to get comp en gpio\n"); + + ret = gpio_request(gpio_comp_en,"COMP_EN"); + if (ret < 0 ) + pr_err("gpio_request_error(%d)\n",ret); + + gpio_direction_output(gpio_comp_en,1); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pr_err("%s: SMBUS Byte Data not Supported\n", __func__); + ret = -EIO; + goto err; + } + + core->companion_dcdc.client = client; + core->companion_dcdc.type = DCDC_VENDOR_FAN53555; + core->companion_dcdc.get_vout_val = fan53555_get_vout_val; + core->companion_dcdc.get_vout_str = fan53555_get_vout_str; + core->companion_dcdc.set_vout = fan53555_set_vsel0_vout; + +#ifdef CONFIG_SOC_EXYNOS5422 + regulator = regulator_get(NULL, power_name); + if (IS_ERR(regulator)) { + pr_err("%s : regulator_get(%s) fail\n", __func__, power_name); + return PTR_ERR(regulator); + } + + if (regulator_is_enabled(regulator)) { + pr_info("%s regulator is already enabled\n", power_name); + } else { + ret = regulator_enable(regulator); + if (unlikely(ret)) { + pr_err("%s : regulator_enable(%s) fail\n", __func__, power_name); + goto err; + } + } + usleep_range(1000, 1000); +#endif + + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, VSEL0_INIT_VAL); + if (ret < 0) { + pr_err("%s: write error = %d , try again\n", __func__, ret); + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, VSEL0_INIT_VAL); + if (ret < 0) + pr_err("%s: write 2nd error = %d\n", __func__, ret); + } + + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if (ret < 0) { + pr_err("%s: read error = %d , try again\n", __func__, ret); + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if (ret < 0) + pr_err("%s: read 2nd error = %d\n", __func__, ret); + } + pr_err("[%s::%d]fan53555 [Read :: %x ,%x]\n\n", __func__, __LINE__, ret,VSEL0_INIT_VAL); + +#ifdef CONFIG_SOC_EXYNOS5422 + ret = regulator_disable(regulator); + if (unlikely(ret)) { + pr_err("%s: regulator_disable(%s) fail\n", __func__, power_name); + goto err; + } + regulator_put(regulator); +#endif + gpio_direction_output(gpio_comp_en,0); + gpio_free(gpio_comp_en); + + pr_info(" %s end\n",__func__); + + return 0; + +err: + gpio_direction_output(gpio_comp_en, 0); + gpio_free(gpio_comp_en); + +#ifdef CONFIG_SOC_EXYNOS5422 + if (!IS_ERR_OR_NULL(regulator)) { + ret = regulator_disable(regulator); + if (unlikely(ret)) { + pr_err("%s: regulator_disable(%s) fail\n", __func__, power_name); + } + regulator_put(regulator); + } +#endif + return ret; +} + +static int fimc_is_fan53555_remove(struct i2c_client *client) +{ + return 0; +} + +static int fimc_is_ncp6335b_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_core *core; + int ret = 0; + + struct device_node *np; + int gpio_comp_en; + + BUG_ON(!fimc_is_dev); + + pr_info("%s start\n",__func__); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + pr_err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + np = of_find_compatible_node(NULL, NULL, "samsung,fimc_is_ncp6335b"); + if(np == NULL) { + pr_err("compatible: fail to read, fan_parse_dt\n"); + return -ENODEV; + } + + gpio_comp_en = of_get_named_gpio(np, "comp_en", 0); + if (!gpio_is_valid(gpio_comp_en)) + pr_err("failed to get comp en gpio\n"); + + ret = gpio_request(gpio_comp_en,"COMP_EN"); + if (ret < 0 ) + pr_err("gpio_request_error(%d)\n",ret); + + gpio_direction_output(gpio_comp_en,1); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pr_err("%s: SMBUS Byte Data not Supported\n", __func__); + ret = -EIO; + goto err; + } + + core->companion_dcdc.client = client; + core->companion_dcdc.type = DCDC_VENDOR_NCP6335B; + core->companion_dcdc.get_vout_val = ncp6335b_get_vout_val; + core->companion_dcdc.get_vout_str = ncp6335b_get_vout_str; + core->companion_dcdc.set_vout = ncp6335b_set_voltage; + + ret = ncp6335b_set_voltage(client, 0xC0); + if (ret < 0) { + pr_err("%s: error, fail to set voltage\n", __func__); + goto err; + } + + ret = ncp6335b_read_voltage(client); + if (ret < 0) { + pr_err("%s: error, fail to read voltage\n", __func__); + goto err; + } + + pr_info("%s %s: ncp6335b probed\n", + dev_driver_string(&client->dev), dev_name(&client->dev)); + +err: + gpio_direction_output(gpio_comp_en,0); + gpio_free(gpio_comp_en); + + return ret; +} + +static int fimc_is_ncp6335b_remove(struct i2c_client *client) +{ + return 0; +} +#endif + +static const struct of_device_id exynos_fimc_is_match[] = { + { + .compatible = "samsung,exynos5-fimc-is", + }, + { + .compatible = "samsung,fimc_is_spi0", + }, + { + .compatible = "samsung,fimc_is_spi1", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_fimc_is_match); + +static struct spi_driver fimc_is_spi0_driver = { + .driver = { + .name = "fimc_is_spi0", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .of_match_table = exynos_fimc_is_match, + }, + .probe = fimc_is_spi_probe, + .remove = fimc_is_spi_remove, +}; + +module_spi_driver(fimc_is_spi0_driver); + +static struct spi_driver fimc_is_spi1_driver = { + .driver = { + .name = "fimc_is_spi1", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .of_match_table = exynos_fimc_is_match, + }, + .probe = fimc_is_spi_probe, + .remove = fimc_is_spi_remove, +}; + +module_spi_driver(fimc_is_spi1_driver); + +#ifdef CONFIG_COMPANION_USE +static struct of_device_id fan53555_dt_ids[] = { + { .compatible = "samsung,fimc_is_fan53555",}, + {}, +}; +MODULE_DEVICE_TABLE(of, fan53555_dt_ids); + +static const struct i2c_device_id fan53555_id[] = { + {"fimc_is_fan53555", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, fan53555_id); + +static struct i2c_driver fan53555_driver = { + .driver = { + .name = "fimc_is_fan53555", + .owner = THIS_MODULE, + .of_match_table = fan53555_dt_ids, + }, + .probe = fimc_is_fan53555_probe, + .remove = fimc_is_fan53555_remove, + .id_table = fan53555_id, +}; +module_i2c_driver(fan53555_driver); + +static struct of_device_id ncp6335b_dt_ids[] = { + { .compatible = "samsung,fimc_is_ncp6335b",}, + {}, +}; +MODULE_DEVICE_TABLE(of, ncp6335b_dt_ids); + +static const struct i2c_device_id ncp6335b_id[] = { + {"fimc_is_ncp6335b", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ncp6335b_id); + +static struct i2c_driver ncp6335b_driver = { + .driver = { + .name = "fimc_is_ncp6335b", + .owner = THIS_MODULE, + .of_match_table = ncp6335b_dt_ids, + }, + .probe = fimc_is_ncp6335b_probe, + .remove = fimc_is_ncp6335b_remove, + .id_table = ncp6335b_id, +}; +module_i2c_driver(ncp6335b_driver); +#endif + +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 = exynos_fimc_is_match, + } +}; + +module_platform_driver(fimc_is_driver); +#else +static struct platform_driver fimc_is_driver = { + .probe = fimc_is_probe, + .remove = __devexit_p(fimc_is_remove), + .driver = { + .name = FIMC_IS_DRV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_pm_ops, + } +}; + +static int __init fimc_is_init(void) +{ + int ret = platform_driver_register(&fimc_is_driver); + if (ret) + err("platform_driver_register failed: %d\n", ret); + return ret; +} + +static void __exit fimc_is_exit(void) +{ + platform_driver_unregister(&fimc_is_driver); +} +module_init(fimc_is_init); +module_exit(fimc_is_exit); +#endif + +MODULE_AUTHOR("Jiyoung Shin"); +MODULE_DESCRIPTION("Exynos FIMC_IS2 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-core.h b/drivers/media/platform/exynos/fimc-is/fimc-is-core.h new file mode 100644 index 000000000000..9f22844755a7 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-core.h @@ -0,0 +1,357 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_CORE_H +#define FIMC_IS_CORE_H + +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_VIDEOBUF2_CMA_PHYS) +#include +#elif defined(CONFIG_VIDEOBUF2_ION) +#include +#endif +#ifdef CONFIG_COMPANION_USE +#include +#include +#include +#include "fimc-is-companion.h" +#ifdef CONFIG_SOC_EXYNOS5422 +#include +#include +#endif +#endif + +#include "fimc-is-param.h" +#include "fimc-is-interface.h" +#include "fimc-is-framemgr.h" +#include "fimc-is-resourcemgr.h" +#include "fimc-is-device-sensor.h" +#include "fimc-is-device-ischain.h" +#include "fimc-is-device-companion.h" + +#include "fimc-is-video.h" +#include "fimc-is-mem.h" + +#define FIMC_IS_DRV_NAME "exynos-fimc-is" + +#define FIMC_IS_COMMAND_TIMEOUT (3*HZ) +#define FIMC_IS_STARTUP_TIMEOUT (3*HZ) +#define FIMC_IS_COMPANION_TIMEOUT (1*HZ) + +#define FIMC_IS_SHUTDOWN_TIMEOUT (10*HZ) +#define FIMC_IS_FLITE_STOP_TIMEOUT (3*HZ) + +#define FIMC_IS_SENSOR_MAX_ENTITIES (1) +#define FIMC_IS_SENSOR_PAD_SOURCE_FRONT (0) +#define FIMC_IS_SENSOR_PADS_NUM (1) + +#define FIMC_IS_FRONT_MAX_ENTITIES (1) +#define FIMC_IS_FRONT_PAD_SINK (0) +#define FIMC_IS_FRONT_PAD_SOURCE_BACK (1) +#define FIMC_IS_FRONT_PAD_SOURCE_BAYER (2) +#define FIMC_IS_FRONT_PAD_SOURCE_SCALERC (3) +#define FIMC_IS_FRONT_PADS_NUM (4) + +#define FIMC_IS_BACK_MAX_ENTITIES (1) +#define FIMC_IS_BACK_PAD_SINK (0) +#define FIMC_IS_BACK_PAD_SOURCE_3DNR (1) +#define FIMC_IS_BACK_PAD_SOURCE_SCALERP (2) +#define FIMC_IS_BACK_PADS_NUM (3) + +#define FIMC_IS_MAX_SENSOR_NAME_LEN (16) + +#define FIMC_IS_A5_MEM_SIZE (0x01400000) +#define FIMC_IS_REGION_SIZE (0x00005000) +#define FIMC_IS_SETFILE_SIZE (0x00140000) +#define FIMC_IS_DEBUG_REGION_ADDR (0x01340000) +#define FIMC_IS_SHARED_REGION_ADDR (0x013C0000) + +#define FIMC_IS_FW_BASE_MASK ((1 << 26) - 1) + +#define FW_SHARED_OFFSET FIMC_IS_SHARED_REGION_ADDR +#define DEBUG_CNT (0x0007D000) /* 500KB */ +#define DEBUG_OFFSET FIMC_IS_DEBUG_REGION_ADDR +#define DEBUGCTL_OFFSET (DEBUG_OFFSET + DEBUG_CNT) + +#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 (1) +#define NUM_DNR_INTERNAL_BUF (2) + +#define GATE_IP_ISP (0) +#define GATE_IP_DRC (1) +#define GATE_IP_FD (2) +#define GATE_IP_SCC (3) +#define GATE_IP_SCP (4) +#define GATE_IP_ODC (0) +#define GATE_IP_DIS (1) +#define GATE_IP_DNR (2) +#if defined(CONFIG_SOC_EXYNOS5422) +#define DVFS_L0 (600000) +#define DVFS_L1 (500000) +#define DVFS_L1_1 (480000) +#define DVFS_L1_2 (460000) +#define DVFS_L1_3 (440000) + +#define DVFS_MIF_L0 (825000) +#define DVFS_MIF_L1 (728000) +#define DVFS_MIF_L2 (633000) +#define DVFS_MIF_L3 (543000) +#define DVFS_MIF_L4 (413000) +#define DVFS_MIF_L5 (275000) + +#define I2C_L0 (108000000) +#define I2C_L1 (36000000) +#define I2C_L1_1 (54000000) +#define I2C_L2 (21600000) +#define DVFS_SKIP_FRAME_NUM (5) +#elif defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#define DVFS_L0 (600000) +#define DVFS_L1 (500000) +#define DVFS_L1_1 (480000) +#define DVFS_L1_2 (460000) +#define DVFS_L1_3 (440000) + +#define DVFS_MIF_L0 (800000) +#define DVFS_MIF_L1 (733000) +#define DVFS_MIF_L2 (667000) +#define DVFS_MIF_L3 (533000) +#define DVFS_MIF_L4 (400000) +#define DVFS_MIF_L5 (266000) + +#define I2C_L0 (83000000) +#define I2C_L1 (36000000) +#define I2C_L1_1 (54000000) +#define I2C_L2 (21600000) +#define DVFS_SKIP_FRAME_NUM (5) +#elif defined(CONFIG_SOC_EXYNOS3470) || defined(CONFIG_SOC_EXYNOS3472) ||defined(CONFIG_SOC_EXYNOS5260) || defined(CONFIG_SOC_EXYNOS4415) +#define DVFS_L0 (266000) +#define DVFS_MIF_L0 (400000) +#define I2C_L0 (108000000) +#define I2C_L1 (36000000) +#define I2C_L1_1 (54000000) +#define I2C_L2 (21600000) +#endif + +#define I2C_RETRY_COUNT 5 + +#define GET_FIMC_IS_NUM_OF_SUBIP(core, subip) \ + (core->pdata->subip_info->_ ## subip.valid) +#define GET_FIMC_IS_NUM_OF_SUBIP2(device, subip) \ + (((struct fimc_is_core *)device->interface->core)->pdata->subip_info->_ ## subip.valid) +#define GET_FIMC_IS_VER_OF_SUBIP(core, subip) \ + ((core)->pdata->subip_info->_##subip.version) +#define GET_FIMC_IS_VER_OF_SUBIP2(device, subip) \ + (((struct fimc_is_core *)device->interface->core)->pdata->subip_info->_ ## subip.version) +#define GET_FIMC_IS_ADDR_OF_SUBIP(core, subip) \ + ((core)->pdata->subip_info->_##subip.base_addr) +#define GET_FIMC_IS_ADDR_OF_SUBIP2(device, subip) \ + (((struct fimc_is_core *)device->interface->core)->pdata->subip_info->_ ## subip.base_addr) + +enum fimc_is_debug_device { + FIMC_IS_DEBUG_MAIN = 0, + FIMC_IS_DEBUG_EC, + FIMC_IS_DEBUG_SENSOR, + FIMC_IS_DEBUG_ISP, + FIMC_IS_DEBUG_DRC, + FIMC_IS_DEBUG_FD, + FIMC_IS_DEBUG_SDK, + FIMC_IS_DEBUG_SCALERC, + FIMC_IS_DEBUG_ODC, + FIMC_IS_DEBUG_DIS, + FIMC_IS_DEBUG_TDNR, + FIMC_IS_DEBUG_SCALERP +}; + +enum fimc_is_debug_target { + FIMC_IS_DEBUG_UART = 0, + FIMC_IS_DEBUG_MEMORY, + FIMC_IS_DEBUG_DCC3 +}; + +enum fimc_is_front_input_entity { + FIMC_IS_FRONT_INPUT_NONE = 0, + FIMC_IS_FRONT_INPUT_SENSOR, +}; + +enum fimc_is_front_output_entity { + FIMC_IS_FRONT_OUTPUT_NONE = 0, + FIMC_IS_FRONT_OUTPUT_BACK, + FIMC_IS_FRONT_OUTPUT_BAYER, + FIMC_IS_FRONT_OUTPUT_SCALERC, +}; + +enum fimc_is_back_input_entity { + FIMC_IS_BACK_INPUT_NONE = 0, + FIMC_IS_BACK_INPUT_FRONT, +}; + +enum fimc_is_back_output_entity { + FIMC_IS_BACK_OUTPUT_NONE = 0, + FIMC_IS_BACK_OUTPUT_3DNR, + FIMC_IS_BACK_OUTPUT_SCALERP, +}; + +enum fimc_is_front_state { + FIMC_IS_FRONT_ST_POWERED = 0, + FIMC_IS_FRONT_ST_STREAMING, + FIMC_IS_FRONT_ST_SUSPENDED, +}; + +enum fimc_is_clck_gate_mode { + CLOCK_GATE_MODE_HOST = 0, + CLOCK_GATE_MODE_FW, +}; + +struct fimc_is_sysfs_debug { + unsigned int en_dvfs; + unsigned int en_clk_gate; + unsigned int clk_gate_mode; +}; + +#ifdef CONFIG_COMPANION_USE +struct fimc_is_spi_gpio { + char *spi_sclk; + char *spi_ssn; + char *spi_miso; + char *spi_mois; +}; +#endif + +struct fimc_is_core { + struct platform_device *pdev; + struct resource *regs_res; + void __iomem *regs; + int irq; + u32 id; + u32 debug_cnt; + atomic_t rsccount; + unsigned long state; + + /* depended on isp */ + struct exynos_platform_fimc_is *pdata; + + struct fimc_is_resourcemgr resourcemgr; + struct fimc_is_groupmgr groupmgr; + + struct fimc_is_minfo minfo; + struct fimc_is_mem mem; + struct fimc_is_interface interface; + + struct fimc_is_device_sensor sensor[FIMC_IS_MAX_NODES]; + struct fimc_is_device_ischain ischain[FIMC_IS_MAX_NODES]; + struct fimc_is_device_companion *companion; + + struct v4l2_device v4l2_dev; + + /* 0-bayer, 1-scalerC, 2-3DNR, 3-scalerP */ + struct fimc_is_video video_3a0; + struct fimc_is_video video_3a1; + struct fimc_is_video video_isp; + struct fimc_is_video video_scc; + struct fimc_is_video video_scp; + struct fimc_is_video video_vdc; + struct fimc_is_video video_vdo; + struct fimc_is_video video_3a0c; + struct fimc_is_video video_3a1c; + + /* spi */ + struct spi_device *spi0; + struct spi_device *spi1; + +#if defined(CONFIG_COMPANION_USE) + struct i2c_client *client0; +#endif +#if defined(CONFIG_OIS_USE) + struct i2c_client *client1; +#endif +#ifdef CONFIG_AF_HOST_CONTROL + struct i2c_client *client2; +#endif + struct i2c_client *eeprom_client0; + struct i2c_client *eeprom_client1; + +#ifdef CONFIG_COMPANION_USE + struct dcdc_power companion_dcdc; + struct fimc_is_spi_gpio spi_gpio; + u32 companion_spi_channel; + bool use_two_spi_line; +#endif + u32 use_sensor_dynamic_voltage_mode; + struct mutex spi_lock; +#ifdef CONFIG_OIS_USE + bool use_ois; + int pin_ois_en; + bool ois_ver_read; +#endif /* CONFIG_OIS_USE */ + bool use_ois_hsi2c; + bool use_module_check; +#ifdef USE_ION_ALLOC + struct ion_client *fimc_ion_client; +#endif + bool running_rear_camera; + bool running_front_camera; +}; + +#if defined(CONFIG_VIDEOBUF2_CMA_PHYS) +extern const struct fimc_is_vb2 fimc_is_vb2_cma; +#elif defined(CONFIG_VIDEOBUF2_ION) +extern const struct fimc_is_vb2 fimc_is_vb2_ion; +#endif + +extern struct device *fimc_is_dev; + +void fimc_is_mem_suspend(void *alloc_ctxes); +void fimc_is_mem_resume(void *alloc_ctxes); +void fimc_is_mem_cache_clean(const void *start_addr, unsigned long size); +void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size); +int fimc_is_init_set(struct fimc_is_core *dev , u32 val); +int fimc_is_load_fw(struct fimc_is_core *dev); +int fimc_is_load_setfile(struct fimc_is_core *dev); +int fimc_is_otf_close(struct fimc_is_device_ischain *ischain); +int fimc_is_spi_reset(void *buf, u32 rx_addr, size_t size); +int fimc_is_spi_read(void *buf, u32 rx_addr, size_t size); +int fimc_is_runtime_suspend(struct device *dev); +int fimc_is_runtime_resume(struct device *dev); + +#define CALL_POPS(s, op, args...) (((s)->pdata->op) ? ((s)->pdata->op(args)) : -EPERM) + +#endif /* FIMC_IS_CORE_H_ */ diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.c new file mode 100644 index 000000000000..91ed4b94be43 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.c @@ -0,0 +1,469 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-interface.h" +//#include "fimc-is-sec-define.h" +#include "fimc-is-device-ischain.h" +#include "fimc-is-dt.h" +#include "fimc-is-device-af.h" + +#define FIMC_IS_AF_DEV_NAME "exynos-fimc-is-af" +static int af_noise_count; + +static struct remove_af_noise af_sensor_interface = { + .af_pdata = NULL, + .af_func = NULL, +}; + +static void fimc_is_af_i2c_config(struct i2c_client *client, bool onoff) +{ + struct device *i2c_dev = client->dev.parent->parent; + struct pinctrl *pinctrl_i2c = NULL; + + info("(%s):onoff(%d)\n", __func__, onoff); + if (onoff) { + /* ON */ + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + + pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "on_i2c"); + if (IS_ERR_OR_NULL(pinctrl_i2c)) { + printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__); + } else { + devm_pinctrl_put(pinctrl_i2c); + } + } else { + /* OFF */ + pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "off_i2c"); + if (IS_ERR_OR_NULL(pinctrl_i2c)) { + printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__); + } else { + devm_pinctrl_put(pinctrl_i2c); + } + + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + } +} + +int fimc_is_af_i2c_read(struct i2c_client *client, u16 addr, u16 *data) +{ + int err; + u8 rxbuf[2], txbuf[2]; + struct i2c_msg msg[2]; + + txbuf[0] = (addr & 0xff00) >> 8; + txbuf[1] = (addr & 0xff); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = txbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rxbuf; + + err = i2c_transfer(client->adapter, msg, 2); + if (unlikely(err != 2)) { + err("%s: register read fail err = %d\n", __func__, err); + return -EIO; + } + + *data = ((rxbuf[0] << 8) | rxbuf[1]); + return 0; +} + +int fimc_is_af_i2c_write(struct i2c_client *client ,u16 addr, u16 data) +{ + int retries = I2C_RETRY_COUNT; + int ret = 0, err = 0; + u8 buf[4] = {0,}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 4, + .buf = buf, + }; + + buf[0] = addr >> 8; + buf[1] = addr; + buf[2] = data >> 8; + buf[3] = data & 0xff; + +#if 0 + pr_info("%s : W(0x%02X%02X%02X%02X)\n",__func__, buf[0], buf[1], buf[2], buf[3]); +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + + usleep_range(10000,11000); + err = ret; + } while (--retries > 0); + + /* Retry occured */ + if (unlikely(retries < I2C_RETRY_COUNT)) { + err("i2c_write: error %d, write (%04X, %04X), retry %d\n", + err, addr, data, I2C_RETRY_COUNT - retries); + } + + if (unlikely(ret != 1)) { + err("I2C does not work\n\n"); + return -EIO; + } + + return 0; +} + +int fimc_is_af_ldo_enable(char *name, bool on) +{ + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + struct regulator *regulator = NULL; + struct platform_device *pdev = NULL; + int ret = 0; + + BUG_ON(!core); + BUG_ON(!core->pdev); + + pdev = core->pdev; + + regulator = regulator_get(&pdev->dev, name); + if (IS_ERR_OR_NULL(regulator)) { + err("%s : regulator_get(%s) fail\n", __func__, name); + return -EINVAL; + } + + if (on) { + if (regulator_is_enabled(regulator)) { + pr_info("%s: regulator is already enabled\n", name); + ret = 0; + } else { + ret = regulator_enable(regulator); + if (ret) { + err("%s : regulator_enable(%s) fail\n", __func__, name); + ret = -EINVAL; + } + } + } else { + if (!regulator_is_enabled(regulator)) { + pr_info("%s: regulator is already disabled\n", name); + ret = 0; + } else { + ret = regulator_disable(regulator); + if (ret) { + err("%s : regulator_disable(%s) fail\n", __func__, name); + ret = -EINVAL; + } + } + } + + regulator_put(regulator); + + return ret; +} + +int fimc_is_af_power(struct fimc_is_device_af *af_device, bool onoff) +{ + int ret = 0; +#ifdef CONFIG_OIS_USE + int pin_ois_en = af_device->core->pin_ois_en; +#endif + + /*CAM_AF_2.8V_AP*/ + ret = fimc_is_af_ldo_enable("CAM_AF_2.8V_AP", onoff); + if (ret) { + err("failed to power control CAM_AF_2.8V_AP, onoff = %d", onoff); + return -EINVAL; + } + +#ifdef CONFIG_OIS_USE + /* OIS_VDD_2.8V */ + if (gpio_is_valid(pin_ois_en)) { + if (onoff) { + gpio_request_one(pin_ois_en, GPIOF_OUT_INIT_HIGH, "CAM_GPIO_OUTPUT_HIGH"); + } else { + gpio_request_one(pin_ois_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + } + gpio_free(pin_ois_en); + } + + /* OIS_VM_2.8V */ + ret = fimc_is_af_ldo_enable("OIS_VM_2.8V", onoff); + if (ret) { + err("failed to power control OIS_VM_2.8V, onoff = %d", onoff); + return -EINVAL; + } +#endif + + /*CAM_IO_1.8V_AP*/ + ret = fimc_is_af_ldo_enable("CAM_IO_1.8V_AP", onoff); + if (ret) { + err("failed to power control CAM_IO_1.8V_AP, onoff = %d", onoff); + return -EINVAL; + } + + usleep_range(5000,5000); + return ret; +} + +bool fimc_is_check_regulator_status(char *name) +{ + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + struct regulator *regulator = NULL; + struct platform_device *pdev = NULL; + int ret = 0; + + BUG_ON(!core); + BUG_ON(!core->pdev); + + pdev = core->pdev; + + regulator = regulator_get(&pdev->dev, name); + if (IS_ERR_OR_NULL(regulator)) { + err("%s : regulator_get(%s) fail\n", __func__, name); + return false; + } + if (regulator_is_enabled(regulator)) { + ret = true; + } else { + ret = false; + } + + regulator_put(regulator); + return ret; +} + +int16_t fimc_is_af_enable(void *device, bool onoff) +{ + int ret = 0; + struct fimc_is_device_af *af_device = (struct fimc_is_device_af *)device; + struct fimc_is_core *core; + bool af_regulator = false, io_regulator = false; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -ENODEV; + } + + pr_info("af_noise : running_rear_camera = %d, onoff = %d\n", core->running_rear_camera, onoff); + if (!core->running_rear_camera) { + if (core->use_ois_hsi2c) { + fimc_is_af_i2c_config(af_device->client, true); + } + + if (onoff) { + fimc_is_af_power(af_device, true); + ret = fimc_is_af_i2c_write(af_device->client, 0x02, 0x00); + if (ret) { + err("i2c write fail\n"); + goto power_off; + } + + ret = fimc_is_af_i2c_write(af_device->client, 0x00, 0x00); + if (ret) { + err("i2c write fail\n"); + goto power_off; + } + + ret = fimc_is_af_i2c_write(af_device->client, 0x01, 0x00); + if (ret) { + err("i2c write fail\n"); + goto power_off; + } + af_noise_count++; + pr_info("af_noise : count = %d\n", af_noise_count); + } else { + /* Check the Power Pins */ + af_regulator = fimc_is_check_regulator_status("CAM_AF_2.8V_AP"); + io_regulator = fimc_is_check_regulator_status("CAM_IO_1.8V_AP"); + + if (af_regulator && io_regulator) { + ret = fimc_is_af_i2c_write(af_device->client, 0x02, 0x40); + if (ret) { + err("i2c write fail\n"); + } + fimc_is_af_power(af_device, false); + } else { + pr_info("already power off.(%d)\n", __LINE__); + } + } + + if (core->use_ois_hsi2c) { + fimc_is_af_i2c_config(af_device->client, false); + } + } + + return ret; + +power_off: + if (!core->running_rear_camera) { + if (core->use_ois_hsi2c) { + fimc_is_af_i2c_config(af_device->client, false); + } + + af_regulator = fimc_is_check_regulator_status("CAM_AF_2.8V_AP"); + io_regulator = fimc_is_check_regulator_status("CAM_IO_1.8V_AP"); + if (af_regulator && io_regulator) { + fimc_is_af_power(af_device, false); + } else { + pr_info("already power off.(%d)\n", __LINE__); + } + } + return ret; +} + +int16_t fimc_is_af_move_lens(struct fimc_is_core *core) +{ + int ret = 0; + struct i2c_client *client = core->client2; + + pr_info("fimc_is_af_move_lens : running_rear_camera = %d\n", core->running_rear_camera); + if (!core->running_rear_camera) { + ret = fimc_is_af_i2c_write(client, 0x00, 0x80); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_af_i2c_write(client, 0x01, 0x00); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_af_i2c_write(client, 0x02, 0x00); + if (ret) { + err("i2c write fail\n"); + } + } + + return ret; +} + +#ifdef CONFIG_SENSORS_SSP_BBD +extern int remove_af_noise_register(struct remove_af_noise *af_cam); +extern void remove_af_noise_unregister(struct remove_af_noise *af_cam); +#endif +static int fimc_is_af_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_device_af *device; + struct fimc_is_core *core; +#ifdef CONFIG_SENSORS_SSP_BBD + int ret; +#endif + + if (fimc_is_dev == NULL) { + warn("fimc_is_dev is not yet probed"); + client->dev.init_name = FIMC_IS_AF_DEV_NAME; + return -EPROBE_DEFER; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) + return -EINVAL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err("No I2C functionality found\n"); + return -ENODEV; + } + + device = kzalloc(sizeof(struct fimc_is_device_af), GFP_KERNEL); + if (!device) { + err("fimc_is_device_companion is NULL"); + return -ENOMEM; + } + + core->client2 = client; + device->client = client; + device->core = core; + af_noise_count = 0; + + af_sensor_interface.af_pdata = device; + af_sensor_interface.af_func = &fimc_is_af_enable; +#ifdef CONFIG_SENSORS_SSP_BBD + ret = remove_af_noise_register(&af_sensor_interface); + if (ret) + err("reduce_af_noise_register failed: %d\n", ret); +#endif + i2c_set_clientdata(client, device); + + return 0; +} + +static int fimc_is_af_remove(struct i2c_client *client) +{ +#ifdef CONFIG_SENSORS_SSP_BBD + remove_af_noise_unregister(&af_sensor_interface); +#endif + return 0; +} + +static const struct i2c_device_id af_id[] = { + {FIMC_IS_AF_DEV_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, af_id); + +#ifdef CONFIG_OF +static struct of_device_id af_dt_ids[] = { + { .compatible = "samsung,af",}, + {}, +}; +#endif + +static struct i2c_driver af_i2c_driver = { + .driver = { + .name = FIMC_IS_AF_DEV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = af_dt_ids, +#endif + }, + .probe = fimc_is_af_probe, + .remove = fimc_is_af_remove, + .id_table = af_id, +}; + +module_i2c_driver(af_i2c_driver); + +MODULE_DESCRIPTION("AF driver for remove noise"); +MODULE_AUTHOR("kyoungho yun "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.h new file mode 100644 index 000000000000..9cd2e17c3e56 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-af.h @@ -0,0 +1,30 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS AF driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct fimc_is_device_af { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + unsigned long state; + struct exynos_platform_fimc_is_sensor *pdata; + struct i2c_client *client; + struct fimc_is_core *core; +}; + +struct remove_af_noise { + void *af_pdata; + int16_t (*af_func)(void *, bool); +}; + +int fimc_is_af_i2c_read(struct i2c_client *client, u16 addr, u16 *data); +int fimc_is_af_i2c_write(struct i2c_client *client ,u16 addr, u16 data); +int16_t fimc_is_af_enable(void *device, bool onoff); +int16_t fimc_is_af_move_lens(struct fimc_is_core *core); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.c new file mode 100644 index 000000000000..eeac2eb2a970 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.c @@ -0,0 +1,802 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-video.h" +#include "fimc-is-dt.h" +#include "fimc-is-device-companion.h" +#include "fimc-is-sec-define.h" +#if defined(CONFIG_OIS_USE) +#include "fimc-is-device-ois.h" +#endif +#ifdef CONFIG_COMPANION_USE +#include "fimc-is-companion-dt.h" +#endif +extern int fimc_is_comp_video_probe(void *data); + +int fimc_is_companion_wait(struct fimc_is_device_companion *device) +{ + int ret = 0; + + ret = wait_event_timeout(device->init_wait_queue, + (device->companion_status == FIMC_IS_COMPANION_OPENDONE), + FIMC_IS_COMPANION_TIMEOUT); + if (ret) { + ret = 0; + } else { + err("timeout"); + device->companion_status = FIMC_IS_COMPANION_IDLE; + ret = -ETIME; + } + + return ret; +} + +static void fimc_is_companion_wakeup(struct fimc_is_device_companion *device) +{ + wake_up(&device->init_wait_queue); +} + +static int fimc_is_companion_mclk_on(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct platform_device *pdev; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdev = device->pdev; + pdata = device->pdata; + + if (test_bit(FIMC_IS_COMPANION_MCLK_ON, &device->state)) { + err("%s : already clk on", __func__); + goto p_err; + } + + if (!pdata->mclk_on) { + err("mclk_on is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->mclk_on(pdev, pdata->scenario, pdata->mclk_ch); + if (ret) { + err("mclk_on is fail(%d)", ret); + goto p_err; + } + + set_bit(FIMC_IS_COMPANION_MCLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_companion_mclk_off(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct platform_device *pdev; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdev = device->pdev; + pdata = device->pdata; + + if (!test_bit(FIMC_IS_COMPANION_MCLK_ON, &device->state)) { + err("%s : already clk off", __func__); + goto p_err; + } + + if (!pdata->mclk_off) { + err("mclk_off is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->mclk_off(pdev, pdata->scenario, pdata->mclk_ch); + if (ret) { + err("mclk_off is fail(%d)", ret); + goto p_err; + } + + clear_bit(FIMC_IS_COMPANION_MCLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_companion_iclk_on(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct platform_device *pdev; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdev = device->pdev; + pdata = device->pdata; + + if (test_bit(FIMC_IS_COMPANION_ICLK_ON, &device->state)) { + err("%s : already clk on", __func__); + goto p_err; + } + + if (!pdata->iclk_cfg) { + err("iclk_cfg is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (!pdata->iclk_on) { + err("iclk_on is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->iclk_cfg(pdev, pdata->scenario, 0); + if (ret) { + err("iclk_cfg is fail(%d)", ret); + goto p_err; + } + + ret = pdata->iclk_on(pdev, pdata->scenario, 0); + if (ret) { + err("iclk_on is fail(%d)", ret); + goto p_err; + } + + set_bit(FIMC_IS_COMPANION_ICLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_companion_iclk_off(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct platform_device *pdev; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdev = device->pdev; + pdata = device->pdata; + + if (!test_bit(FIMC_IS_COMPANION_ICLK_ON, &device->state)) { + err("%s : already clk off", __func__); + goto p_err; + } + + if (!pdata->iclk_off) { + err("iclk_off is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->iclk_off(pdev, pdata->scenario, 0); + if (ret) { + err("iclk_off is fail(%d)", ret); + goto p_err; + } + + clear_bit(FIMC_IS_COMPANION_ICLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_companion_gpio_on(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + struct fimc_is_from_info *sysfs_finfo; + struct exynos_sensor_pin (*pin_ctrls)[2][GPIO_CTRL_MAX]; +#endif + struct fimc_is_core *core; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + pin_ctrls = pdata->pin_ctrls; +#endif + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + if (test_bit(FIMC_IS_COMPANION_GPIO_ON, &device->state)) { + err("%s : already gpio on", __func__); + goto p_err; + } + + if (!pdata->gpio_cfg) { + err("gpio_cfg is NULL"); + ret = -EINVAL; + goto p_err; + } + + core->running_rear_camera = true; + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + if(core->use_sensor_dynamic_voltage_mode) { + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + if (pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][0].name != NULL && + !strcmp(pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][0].name, "CAM_SEN_A2.8V_AP")) { + if (sysfs_finfo->header_ver[1] == '1' && sysfs_finfo->header_ver[2] == '6' && sysfs_finfo->header_ver[4] == 'L') { + pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][0].voltage = 2950000; + info("LSI Sensor EVT2.4 voltage(%d)\n", pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][0].voltage); + } else { + pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][0].voltage = 2800000; + } + } + +#if defined(CONFIG_SOC_EXYNOS5433) + if (pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][1].name != NULL && + !strcmp(pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][1].name, "CAM_SEN_CORE_1.2V_AP")) { + if (sysfs_finfo->header_ver[1] == '1' && sysfs_finfo->header_ver[2] == '6' && sysfs_finfo->header_ver[4] == 'S') { + pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][1].voltage = 1050000; + info("SONY Sensor voltage(%d)\n", pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][1].voltage); + } else { + pin_ctrls[pdata->scenario][GPIO_SCENARIO_ON][1].voltage = 1200000; + } + } +#endif + } +#endif + + ret = pdata->gpio_cfg(device->pdev, pdata->scenario, GPIO_SCENARIO_ON); + if (ret) { + err("gpio_cfg is fail(%d)", ret); + goto p_err; + } + + set_bit(FIMC_IS_COMPANION_GPIO_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_companion_gpio_off(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!test_bit(FIMC_IS_COMPANION_GPIO_ON, &device->state)) { + err("%s : already gpio off", __func__); + goto p_err; + } + + if (!pdata->gpio_cfg) { + err("gpio_cfg is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->gpio_cfg(device->pdev, pdata->scenario, GPIO_SCENARIO_OFF); + if (ret) { + err("gpio_cfg is fail(%d)", ret); + goto p_err; + } + + clear_bit(FIMC_IS_COMPANION_GPIO_ON, &device->state); + +p_err: + core->running_rear_camera = false; + + return ret; +} + + +int fimc_is_companion_open(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct fimc_is_core *core; + /* Workaround for Host to use ISP-SPI. Will be removed later.*/ + struct fimc_is_spi_gpio *spi_gpio; + static char companion_fw_name[100]; + static char master_setf_name[100]; + static char mode_setf_name[100]; +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + static char fw_name[100]; + static char setf_name[100]; +#endif + + BUG_ON(!device); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + spi_gpio = &core->spi_gpio; + + if (test_bit(FIMC_IS_COMPANION_OPEN, &device->state)) { + err("already open"); + ret = -EMFILE; + goto p_err; + } + + device->companion_status = FIMC_IS_COMPANION_OPENNING; + core->running_rear_camera = true; +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_get_sync(&device->pdev->dev); +#else + fimc_is_companion_runtime_resume(&device->pdev->dev); +#endif +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + ret = fimc_is_sec_fw_sel(core, &device->pdev->dev, fw_name, setf_name, false); + if (ret < 0) { + err("failed to select firmware (%d)", ret); + goto p_err_pm; + } +#endif + ret = fimc_is_sec_concord_fw_sel(core, &device->pdev->dev, companion_fw_name, master_setf_name, mode_setf_name); + + /* TODO: loading firmware */ + fimc_is_s_int_comb_isp(core, false, INTMR2_INTMCIS22); + + // Workaround for Host to use ISP-SPI. Will be removed later. + /* set pin output for Host to use SPI*/ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_FUNC, FUNC_OUTPUT)); + + fimc_is_set_spi_config(spi_gpio, FIMC_IS_SPI_FUNC, false); + + if (fimc_is_comp_is_valid(core) == 0) { + ret = fimc_is_comp_loadfirm(core); + if (ret) { + err("fimc_is_comp_loadfirm() fail"); + goto p_err_pm; + } + ret = fimc_is_comp_loadcal(core); + if (ret) { + err("fimc_is_comp_loadcal() fail"); + } + + fimc_is_power_binning(core); + + ret = fimc_is_comp_loadsetf(core); + if (ret) { + err("fimc_is_comp_loadsetf() fail"); + goto p_err_pm; + } + } + + // Workaround for Host to use ISP-SPI. Will be removed later. + /* Set SPI pins to low before changing pin function */ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_sclk, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_miso, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_mois, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + + /* Set pin function for A5 to use SPI */ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + + set_bit(FIMC_IS_COMPANION_OPEN, &device->state); + device->companion_status = FIMC_IS_COMPANION_OPENDONE; + fimc_is_companion_wakeup(device); + +#if defined(CONFIG_OIS_USE) + if(core->use_ois) { + if (!core->use_ois_hsi2c) { + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 1)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 1)); + } + + if (!core->ois_ver_read) { + fimc_is_ois_check_fw(core); + } + + fimc_is_ois_exif_data(core); + + if (!core->use_ois_hsi2c) { + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + } + } +#endif + + info("[COMP:D] %s(%d)status(%d)\n", __func__, ret, device->companion_status); + return ret; + +p_err_pm: +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_put_sync(&device->pdev->dev); +#else + fimc_is_companion_runtime_suspend(&device->pdev->dev); +#endif + +p_err: + err("[COMP:D] open fail(%d)status(%d)", ret, device->companion_status); + return ret; +} + +int fimc_is_companion_close(struct fimc_is_device_companion *device) +{ + int ret = 0; +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + u32 timeout; +#endif + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } + + BUG_ON(!device); + + if (!test_bit(FIMC_IS_COMPANION_OPEN, &device->state)) { + err("already close"); + ret = -EMFILE; + goto p_err; + } + +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_put_sync(&device->pdev->dev); +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + if (core != NULL && !test_bit(FIMC_IS_ISCHAIN_POWER_ON, &core->state)) { + warn("only companion device closing after open.."); + timeout = 2000; + while ((readl(PMUREG_CAM1_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + if (!(timeout % 100)) + warn("wait for CAM1 power down..(%d)", timeout); + } + if (timeout == 0) + err("CAM1 power down failed(CAM1:0x%08x, A5:0x%08x)\n", + readl(PMUREG_CAM1_STATUS), readl(PMUREG_ISP_ARM_STATUS)); + } +#endif +#else + fimc_is_companion_runtime_suspend(&device->pdev->dev); +#endif /* CONFIG_PM_RUNTIME */ + + clear_bit(FIMC_IS_COMPANION_OPEN, &device->state); + +p_err: + core->running_rear_camera = false; + device->companion_status = FIMC_IS_COMPANION_IDLE; + info("[COMP:D] %s(%d)\n", __func__, ret); + return ret; +} + +static int fimc_is_companion_probe(struct platform_device *pdev) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_device_companion *device; + void *pdata; + + BUG_ON(!pdev); + + if (fimc_is_dev == NULL) { + warn("fimc_is_dev is not yet probed"); + pdev->dev.init_name = FIMC_IS_COMPANION_DEV_NAME; + return -EPROBE_DEFER; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } + + + device = kzalloc(sizeof(struct fimc_is_device_companion), GFP_KERNEL); + if (!device) { + err("fimc_is_device_companion is NULL"); + return -ENOMEM; + } + + init_waitqueue_head(&device->init_wait_queue); + + device->companion_status = FIMC_IS_COMPANION_IDLE; + +#ifdef CONFIG_OF + ret = fimc_is_companion_parse_dt(pdev); + if (ret) { + err("parsing device tree is fail(%d)", ret); + goto p_err; + } +#endif + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + err("pdata is NULL"); + ret = -EINVAL; + goto p_err; + } + + device->pdev = pdev; + device->private_data = core; + device->regs = core->regs; + device->pdata = pdata; + platform_set_drvdata(pdev, device); + device_init_wakeup(&pdev->dev, true); + core->companion = device; +#ifdef CONFIG_OIS_USE + core->pin_ois_en = device->pdata->pin_ois_en; +#endif + + /* init state */ + clear_bit(FIMC_IS_COMPANION_OPEN, &device->state); + clear_bit(FIMC_IS_COMPANION_MCLK_ON, &device->state); + clear_bit(FIMC_IS_COMPANION_ICLK_ON, &device->state); + clear_bit(FIMC_IS_COMPANION_GPIO_ON, &device->state); + + ret = v4l2_device_register(&pdev->dev, &device->v4l2_dev); + if (ret) { + err("v4l2_device_register is fail(%d)", ret); + goto p_err; + } + + ret = fimc_is_comp_video_probe(device); + if (ret) { + err("fimc_is_companion_video_probe is fail(%d)", ret); + goto p_err; + } + +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_enable(&pdev->dev); +#endif + + info("[COMP:D] %s(%d)\n", __func__, ret); + + return ret; + +p_err: + kfree(device); + return ret; +} + +static int fimc_is_companion_remove(struct platform_device *pdev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +static int fimc_is_companion_suspend(struct device *dev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +static int fimc_is_companion_resume(struct device *dev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +int fimc_is_companion_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_device_companion *device; +#ifdef CONFIG_AF_HOST_CONTROL + struct fimc_is_core *core; +#endif + + info("%s\n", __func__); + +#ifdef CONFIG_AF_HOST_CONTROL + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } +#endif + device = (struct fimc_is_device_companion *)platform_get_drvdata(pdev); + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto err_dev_null; + } + + /* gpio uninit */ + ret = fimc_is_companion_gpio_off(device); + if (ret) { + err("fimc_is_companion_gpio_off is fail(%d)", ret); + goto p_err; + } + + /* periperal internal clock off */ + ret = fimc_is_companion_iclk_off(device); + if (ret) { + err("fimc_is_companion_iclk_off is fail(%d)", ret); + goto p_err; + } + + /* companion clock off */ + ret = fimc_is_companion_mclk_off(device); + if (ret) { + err("fimc_is_companion_mclk_off is fail(%d)", ret); + goto p_err; + } + +p_err: + info("[COMP:D] %s(%d)\n", __func__, ret); +err_dev_null: + return ret; +} + +int fimc_is_companion_runtime_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_device_companion *device; +#ifdef CONFIG_AF_HOST_CONTROL + struct fimc_is_core *core; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } +#endif + device = (struct fimc_is_device_companion *)platform_get_drvdata(pdev); + if (!device) { + err("device is NULL"); + return -EINVAL; + } + + /* Sensor clock on */ + ret = fimc_is_companion_mclk_on(device); + if (ret) { + err("fimc_is_companion_mclk_on is fail(%d)", ret); + goto p_err; + } + + /* gpio init */ + ret = fimc_is_companion_gpio_on(device); + if (ret) { + err("fimc_is_companion_gpio_on is fail(%d)", ret); + goto p_err; + } + + /* periperal internal clock on */ + ret = fimc_is_companion_iclk_on(device); + if (ret) { + err("fimc_is_companion_iclk_on is fail(%d)", ret); + goto p_err; + } +p_err: + info("[COMP:D] %s(%d)\n", __func__, ret); + return ret; +} + +static const struct dev_pm_ops fimc_is_companion_pm_ops = { + .suspend = fimc_is_companion_suspend, + .resume = fimc_is_companion_resume, + .runtime_suspend = fimc_is_companion_runtime_suspend, + .runtime_resume = fimc_is_companion_runtime_resume, +}; + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_companion_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-companion", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_fimc_is_companion_match); + +static struct platform_driver fimc_is_companion_driver = { + .probe = fimc_is_companion_probe, + .remove = fimc_is_companion_remove, + .driver = { + .name = FIMC_IS_COMPANION_DEV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_companion_pm_ops, + .of_match_table = exynos_fimc_is_companion_match, + } +}; + +module_platform_driver(fimc_is_companion_driver); +#else +static struct platform_device_id fimc_is_companion_driver_ids[] = { + { + .name = FIMC_IS_COMPANION_DEV_NAME, + .driver_data = 0, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fimc_is_companion_driver_ids); + +static struct platform_driver fimc_is_companion_driver = { + .probe = fimc_is_companion_probe, + .remove = __devexit_p(fimc_is_companion_remove), + .id_table = fimc_is_companion_driver_ids, + .driver = { + .name = FIMC_IS_COMPANION_DEV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_companion_pm_ops, + } +}; + +static int __init fimc_is_companion_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&fimc_is_companion_driver); + if (ret) + err("platform_driver_register failed: %d\n", ret); + + return ret; +} + +static void __exit fimc_is_companion_exit(void) +{ + platform_driver_unregister(&fimc_is_companion_driver); +} +module_init(fimc_is_companion_init); +module_exit(fimc_is_companion_exit); +#endif + +MODULE_AUTHOR("Wooki Min"); +MODULE_DESCRIPTION("Exynos FIMC_IS_COMPANION driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.h new file mode 100644 index 000000000000..ee20321c5ee6 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-companion.h @@ -0,0 +1,59 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef fimc_is_device_companion_H +#define fimc_is_device_companion_H + +#include +#include +#include "fimc-is-video.h" + +struct fimc_is_video_ctx; + +#define FIMC_IS_COMPANION_DEV_NAME "exynos-fimc-is-companion" + +enum fimc_is_companion_state { + FIMC_IS_COMPANION_OPEN, + FIMC_IS_COMPANION_MCLK_ON, + FIMC_IS_COMPANION_ICLK_ON, + FIMC_IS_COMPANION_GPIO_ON, +}; + +enum fimc_is_companion_status { + FIMC_IS_COMPANION_IDLE, + FIMC_IS_COMPANION_OPENNING, + FIMC_IS_COMPANION_OPENDONE, +}; + +struct fimc_is_device_companion { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + void __iomem *regs; + struct fimc_is_mem mem; + + struct fimc_is_video_ctx *vctx; + struct fimc_is_video video; + + unsigned long state; + + struct exynos_platform_fimc_is_sensor *pdata; + void *private_data; + wait_queue_head_t init_wait_queue; + int companion_status; +}; + +int fimc_is_companion_open(struct fimc_is_device_companion *device); +int fimc_is_companion_close(struct fimc_is_device_companion *device); +int fimc_is_companion_wait(struct fimc_is_device_companion *device); +int fimc_is_companion_runtime_suspend(struct device *dev); +int fimc_is_companion_runtime_resume(struct device *dev); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.c new file mode 100644 index 000000000000..1bbbbfe735a1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.c @@ -0,0 +1,494 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is/mipi-csi functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-config.h" +#include "fimc-is-regs.h" +#include "fimc-is-hw.h" +#include "fimc-is-device-csi.h" +#include "fimc-is-device-sensor.h" + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0000_0000) +extern void s5pcsis_enable_interrupts(unsigned long __iomem *base_reg, struct fimc_is_image *image, bool on); +extern void s5pcsis_set_hsync_settle(unsigned long __iomem *base_reg, int settle); +extern void s5pcsis_set_params(unsigned long __iomem *base_reg, struct fimc_is_image *image, u32 lanes); +extern void s5pcsis_reset(unsigned long __iomem *base_reg); +extern void s5pcsis_system_enable(unsigned long __iomem *base_reg, int on, u32 lanes); +#endif + +static u32 get_hsync_settle(struct fimc_is_sensor_cfg *cfg, + const u32 cfgs, u32 width, u32 height, u32 framerate) +{ + u32 settle; + u32 max_settle; + u32 proximity_framerate, proximity_settle; + u32 i; + + settle = 0; + max_settle = 0; + proximity_framerate = 0; + proximity_settle = 0; + + for (i = 0; i < cfgs; i++) { + if ((cfg[i].width == width) && + (cfg[i].height == height) && + (cfg[i].framerate == framerate)) { + settle = cfg[i].settle; + break; + } + + if ((cfg[i].width == width) && + (cfg[i].height == height) && + (cfg[i].framerate > proximity_framerate)) { + proximity_settle = cfg[i].settle; + proximity_framerate = cfg[i].framerate; + } + + if (cfg[i].settle > max_settle) + max_settle = cfg[i].settle; + } + + if (!settle) { + if (proximity_settle) { + settle = proximity_settle; + } else { + /* + * return a max settle time value in above table + * as a default depending on the channel + */ + settle = max_settle; + + warn("could not find proper settle time: %dx%d@%dfps", + width, height, framerate); + } + } + + return settle; +} + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0310_0100) +static u32 get_vci_channel(struct fimc_is_vci *vci, + const u32 vcis, u32 pixelformat) +{ + u32 i; + u32 index = vcis; + + BUG_ON(!vci); + + for (i = 0; i < vcis; i++) { + if (vci[i].pixelformat == pixelformat) { + index = i; + break; + } + } + + if (index == vcis) { + err("invalid vc setting(foramt : %d)", pixelformat); + BUG(); + } + + return index; +} +#endif + +int fimc_is_csi_open(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + + BUG_ON(!subdev); + + csi = v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + ret = -EINVAL; + goto p_err; + } + + csi->sensor_cfgs = 0; + csi->sensor_cfg = NULL; + memset(&csi->image, 0, sizeof(struct fimc_is_image)); + +p_err: + return ret; +} + +int fimc_is_csi_close(struct v4l2_subdev *subdev) +{ + return 0; +} + +/* value : module enum */ +static int csi_init(struct v4l2_subdev *subdev, u32 value) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + BUG_ON(!value); + + csi = v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + ret = -EINVAL; + goto p_err; + } + + module = (struct fimc_is_module_enum *)value; + csi->sensor_cfgs = module->cfgs; + csi->sensor_cfg = module->cfg; + csi->vcis = module->vcis; + csi->vci = module->vci; + csi->image.framerate = SENSOR_DEFAULT_FRAMERATE; /* default frame rate */ + csi->mode = module->mode; + csi->lanes = module->lanes; + +p_err: + return ret; +} + +static int csi_s_power(struct v4l2_subdev *subdev, + int on) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + + BUG_ON(!subdev); + + csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + return -EINVAL; + } + + /* HACK: CSI #1 phy should be enabled when CSI #2 phy is eanbled. */ + if (csi->instance == CSI_ID_C) { + ret = exynos5_csis_phy_enable(CSI_ID_B, on); + } + + ret = exynos5_csis_phy_enable(csi->instance, on); + if (ret) { + err("fail to csi%d power on", csi->instance); + goto p_err; + } + +p_err: + mdbgd_front("%s(%d, %d)\n", csi, __func__, on, ret); + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = csi_init, + .s_power = csi_s_power +}; + +static int csi_stream_on(struct fimc_is_device_csi *csi) +{ + int ret = 0; + u32 settle; + unsigned long __iomem *base_reg; + + BUG_ON(!csi); + BUG_ON(!csi->sensor_cfg); + + base_reg = csi->base_reg; + + settle = get_hsync_settle( + csi->sensor_cfg, + csi->sensor_cfgs, + csi->image.window.width, + csi->image.window.height, + csi->image.framerate); + + minfo("[CSI:D] settle(%dx%d@%d) = %d, lane idx(%d)\n", + csi, + csi->image.window.width, + csi->image.window.height, + csi->image.framerate, + settle, + csi->lanes); + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0000_0000) + s5pcsis_reset(base_reg); + s5pcsis_set_hsync_settle(base_reg, settle); + s5pcsis_set_params(base_reg, &csi->image, csi->lanes); + /* lane total count = csi->lanes + 1 (CSI_DATA_LANES_1 is 0) */ + s5pcsis_system_enable(base_reg, true, (csi->lanes + 1)); + s5pcsis_enable_interrupts(base_reg, &csi->image, true); +#else + csi_hw_reset(base_reg); + csi_hw_s_settle(base_reg, settle); + csi_hw_s_control(base_reg, csi->image.format.pixelformat, csi->mode, csi->lanes); + if (csi->mode == CSI_MODE_CH0_ONLY) { + csi_hw_s_config(base_reg, + CSI_VIRTUAL_CH_0, + CSI_VIRTUAL_CH_0, + csi->image.format.pixelformat, + csi->image.window.width, + csi->image.window.height); + } else { + u32 index = get_vci_channel(csi->vci, csi->vcis, csi->image.format.pixelformat); + csi_hw_s_config(base_reg, + CSI_VIRTUAL_CH_0, + csi->vci[index].vc_map[CSI_VIRTUAL_CH_0], + csi->image.format.pixelformat, + csi->image.window.width, + csi->image.window.height); + csi_hw_s_config(base_reg, + CSI_VIRTUAL_CH_1, + csi->vci[index].vc_map[CSI_VIRTUAL_CH_1], + csi->image.format.pixelformat, + csi->image.window.width, + csi->image.window.height); + csi_hw_s_config(base_reg, + CSI_VIRTUAL_CH_2, + csi->vci[index].vc_map[CSI_VIRTUAL_CH_2], + csi->image.format.pixelformat, + csi->image.window.width, + csi->image.window.height); + } + + csi_hw_s_interrupt(base_reg, true); + csi_hw_enable(base_reg); +#endif + + return ret; +} + +static int csi_stream_off(struct fimc_is_device_csi *csi) +{ + int ret = 0; + unsigned long __iomem *base_reg; + + BUG_ON(!csi); + + base_reg = csi->base_reg; + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0000_0000) + s5pcsis_enable_interrupts(base_reg, &csi->image, false); + /* lane total count = csi->lanes + 1 (CSI_DATA_LANES_1 is 0) */ + s5pcsis_system_enable(base_reg, false, (csi->lanes + 1)); +#else + csi_hw_s_interrupt(base_reg, false); + csi_hw_disable(base_reg); +#endif + + return ret; +} + +static int csi_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + + BUG_ON(!subdev); + + csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + return -EINVAL; + } + + if (enable) { + ret = csi_stream_on(csi); + if (ret) { + err("csi_stream_on is fail(%d)", ret); + goto p_err; + } + } else { + ret = csi_stream_off(csi); + if (ret) { + err("csi_stream_off is fail(%d)", ret); + goto p_err; + } + } + +p_err: + return 0; +} + +static int csi_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + + BUG_ON(!subdev); + BUG_ON(!param); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + return -EINVAL; + } + + csi->image.framerate = tpf->denominator / tpf->numerator; + + mdbgd_front("%s(%d, %d)\n", csi, __func__, csi->image.framerate, ret); + return ret; +} + +static int csi_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + int ret = 0; + struct fimc_is_device_csi *csi; + + BUG_ON(!subdev); + BUG_ON(!fmt); + + csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev); + if (!csi) { + err("csi is NULL"); + return -EINVAL; + } + + csi->image.window.offs_h = 0; + csi->image.window.offs_v = 0; + csi->image.window.width = fmt->width; + csi->image.window.height = fmt->height; + csi->image.window.o_width = fmt->width; + csi->image.window.o_height = fmt->height; + csi->image.format.pixelformat = fmt->code; + csi->image.format.field = fmt->field; + + mdbgd_front("%s(%dx%d, %X)\n", csi, __func__, fmt->width, fmt->height, fmt->code); + return ret; +} + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = csi_s_stream, + .s_parm = csi_s_param, + .s_mbus_fmt = csi_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +#ifdef DBG_CSIISR +static irqreturn_t fimc_is_csi_isr(int irq, void *data) +{ + u32 status; + struct fimc_is_device_csi *csi; + + csi = data; + + status = csi_hw_g_interrupt(csi->base_reg); + info("CSI%d : irq%d(%X)\n",csi->instance, irq, status); + + return IRQ_HANDLED; +} +#endif + +int fimc_is_csi_probe(void *parent, u32 instance) +{ + int ret = 0; + struct v4l2_subdev *subdev_csi; + struct fimc_is_device_csi *csi; + struct fimc_is_device_sensor *device = parent; + + BUG_ON(!device); + + subdev_csi = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_csi) { + merr("subdev_csi is NULL", device); + ret = -ENOMEM; + goto p_err; + } + device->subdev_csi = subdev_csi; + + csi = kzalloc(sizeof(struct fimc_is_device_csi), GFP_KERNEL); + if (!csi) { + merr("csi is NULL", device); + ret = -ENOMEM; + goto p_err_free1; + } + + csi->instance = instance; + switch(instance) { + case CSI_ID_A: + csi->base_reg = (unsigned long *)MIPICSI0_REG_BASE; +#ifdef DBG_CSIISR + ret = request_irq(IRQ_MIPICSI0, + fimc_is_csi_isr, + IRQF_SHARED, + "mipi-csi0", + csi); + if (ret) { + err("request_irq(IRQ_MIPICSI0) is fail(%d)", ret); + goto p_err_free2; + } +#endif + break; + case CSI_ID_B: + csi->base_reg = (unsigned long *)MIPICSI1_REG_BASE; +#ifdef DBG_CSIISR + ret = request_irq(IRQ_MIPICSI1, + fimc_is_csi_isr, + IRQF_SHARED, + "mipi-csi1", + csi); + if (ret) { + err("request_irq(IRQ_MIPICSI1) is fail(%d)", ret); + goto p_err_free2; + } +#endif + break; + case CSI_ID_C: + csi->base_reg = (unsigned long *)MIPICSI2_REG_BASE; + break; + default: + err("instance is invalid(%d)", instance); + ret = -EINVAL; + goto p_err_free2; + } + + v4l2_subdev_init(subdev_csi, &subdev_ops); + v4l2_set_subdevdata(subdev_csi, csi); + v4l2_set_subdev_hostdata(subdev_csi, device); + snprintf(subdev_csi->name, V4L2_SUBDEV_NAME_SIZE, "csi-subdev.%d", instance); + ret = v4l2_device_register_subdev(&device->v4l2_dev, subdev_csi); + if (ret) { + merr("v4l2_device_register_subdev is fail(%d)", device, ret); + goto p_err_free2; + } + + info("[%d][FRT:D] %s(%d)\n", instance, __func__, ret); + return 0; + +p_err_free2: + kfree(csi); + +p_err_free1: + kfree(subdev_csi); + device->subdev_csi = NULL; + +p_err: + err("[%d][FRT:D] %s(%d)\n", instance, __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.h new file mode 100644 index 000000000000..dcae741348fe --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-csi.h @@ -0,0 +1,30 @@ +#ifndef FIMC_IS_DEVICE_CSI_H +#define FIMC_IS_DEVICE_CSI_H + +#include +#include "fimc-is-type.h" + +struct fimc_is_device_csi { + /* channel information */ + u32 instance; + unsigned long __iomem *base_reg; + + /* for settle time */ + u32 sensor_cfgs; + struct fimc_is_sensor_cfg *sensor_cfg; + + /* for vci setting */ + u32 vcis; + struct fimc_is_vci *vci; + + /* image configuration */ + u32 mode; + u32 lanes; + struct fimc_is_image image; +}; + +int __must_check fimc_is_csi_probe(void *parent, u32 instance); +int __must_check fimc_is_csi_open(struct v4l2_subdev *subdev); +int __must_check fimc_is_csi_close(struct v4l2_subdev *subdev); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-eeprom.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-eeprom.c new file mode 100644 index 000000000000..368a84d0560e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-eeprom.c @@ -0,0 +1,138 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fimc-is-core.h" +#include "fimc-is-device-sensor.h" +#include "fimc-is-resourcemgr.h" +#include "fimc-is-hw.h" + +#define DRIVER_NAME "fimc_is_eeprom_i2c" +#define DRIVER_NAME_REAR "rear-eeprom-i2c" +#define DRIVER_NAME_FRONT "front-eeprom-i2c" +#define REAR_DATA 0 +#define FRONT_DATA 1 + + +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +int sensor_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_core *core; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) + goto probe_defer; + + if (id->driver_data == REAR_DATA) { + core->eeprom_client0 = client; + } else if (id->driver_data == FRONT_DATA) { + core->eeprom_client1 = client; + } else { + err("rear eeprom device is failed!"); + } + + pr_info("%s %s[%ld]: fimc_is_sensor_eeprom probed!\n", + dev_driver_string(&client->dev), dev_name(&client->dev), id->driver_data); + + return 0; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; + +} + +static int sensor_eeprom_remove(struct i2c_client *client) +{ + int ret = 0; + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_eeprom_match[] = { + { + .compatible = "samsung,rear-eeprom-i2c", .data = (void *)REAR_DATA + }, + { + .compatible = "samsung,front-eeprom-i2c", .data = (void *)FRONT_DATA + }, + {}, +}; +#endif + +static const struct i2c_device_id sensor_eeprom_idt[] = { + { DRIVER_NAME_REAR, REAR_DATA }, + { DRIVER_NAME_FRONT, FRONT_DATA }, +}; + +static struct i2c_driver sensor_eeprom_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = exynos_fimc_is_sensor_eeprom_match +#endif + }, + .probe = sensor_eeprom_probe, + .remove = sensor_eeprom_remove, + .id_table = sensor_eeprom_idt +}; + +static int __init sensor_eeprom_load(void) +{ + return i2c_add_driver(&sensor_eeprom_driver); +} + +static void __exit sensor_eeprom_unload(void) +{ + i2c_del_driver(&sensor_eeprom_driver); +} + +module_init(sensor_eeprom_load); +module_exit(sensor_eeprom_unload); + +MODULE_AUTHOR("Kyoungho Yun"); +MODULE_DESCRIPTION("Camera eeprom driver"); +MODULE_LICENSE("GPL v2"); + + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.c new file mode 100644 index 000000000000..7ed2d164a811 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.c @@ -0,0 +1,2016 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-time.h" +#include "fimc-is-core.h" +#include "fimc-is-regs.h" +#include "fimc-is-hw.h" +#include "fimc-is-interface.h" +#include "fimc-is-device-flite.h" + +#define FLITE_MAX_RESET_READY_TIME (20) /* 100ms */ +#define FLITE_MAX_WIDTH_SIZE (8192) +#define FLITE_MAX_HEIGHT_SIZE (8192) +/* #define COLORBAR_MODE */ + +/*FIMCLite*/ +/* Camera Source size */ +#define FLITE_REG_CISRCSIZE (0x00) +#define FLITE_REG_CISRCSIZE_SIZE_H(x) ((x) << 16) +#define FLITE_REG_CISRCSIZE_SIZE_V(x) ((x) << 0) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR (0 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB (1 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY (2 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY (3 << 14) + +/* Global control */ +#define FLITE_REG_CIGCTRL 0x04 +#define FLITE_REG_CIGCTRL_YUV422_1P (0x1E << 24) +#define FLITE_REG_CIGCTRL_RAW8 (0x2A << 24) +#define FLITE_REG_CIGCTRL_RAW10 (0x2B << 24) +#define FLITE_REG_CIGCTRL_RAW12 (0x2C << 24) +#define FLITE_REG_CIGCTRL_RAW14 (0x2D << 24) + +/* User defined formats. x = 0...0xF. */ +#define FLITE_REG_CIGCTRL_USER(x) (0x30 + x - 1) +#define FLITE_REG_CIGCTRL_OLOCAL_DISABLE (1 << 22) +#define FLITE_REG_CIGCTRL_SHADOWMASK_DISABLE (1 << 21) +#define FLITE_REG_CIGCTRL_ODMA_DISABLE (1 << 20) +#define FLITE_REG_CIGCTRL_SWRST_REQ (1 << 19) +#define FLITE_REG_CIGCTRL_SWRST_RDY (1 << 18) +#define FLITE_REG_CIGCTRL_SWRST (1 << 17) +#define FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR (1 << 15) +#define FLITE_REG_CIGCTRL_INVPOLPCLK (1 << 14) +#define FLITE_REG_CIGCTRL_INVPOLVSYNC (1 << 13) +#define FLITE_REG_CIGCTRL_INVPOLHREF (1 << 12) +#define FLITE_REG_CIGCTRL_IRQ_LASTEN0_ENABLE (0 << 8) +#define FLITE_REG_CIGCTRL_IRQ_LASTEN0_DISABLE (1 << 8) +#define FLITE_REG_CIGCTRL_IRQ_ENDEN0_ENABLE (0 << 7) +#define FLITE_REG_CIGCTRL_IRQ_ENDEN0_DISABLE (1 << 7) +#define FLITE_REG_CIGCTRL_IRQ_STARTEN0_ENABLE (0 << 6) +#define FLITE_REG_CIGCTRL_IRQ_STARTEN0_DISABLE (1 << 6) +#define FLITE_REG_CIGCTRL_IRQ_OVFEN0_ENABLE (0 << 5) +#define FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE (1 << 5) +#define FLITE_REG_CIGCTRL_SELCAM_MIPI (1 << 3) + +/* Image Capture Enable */ +#define FLITE_REG_CIIMGCPT (0x08) +#define FLITE_REG_CIIMGCPT_IMGCPTEN (1 << 31) +#define FLITE_REG_CIIMGCPT_CPT_FREN (1 << 25) +#define FLITE_REG_CIIMGCPT_CPT_FRPTR(x) ((x) << 19) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FRCNT (1 << 18) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FREN (0 << 18) +#define FLITE_REG_CIIMGCPT_CPT_FRCNT(x) ((x) << 10) + +/* Capture Sequence */ +#define FLITE_REG_CICPTSEQ (0x0C) +#define FLITE_REG_CPT_FRSEQ(x) ((x) << 0) + +/* Camera Window Offset */ +#define FLITE_REG_CIWDOFST (0x10) +#define FLITE_REG_CIWDOFST_WINOFSEN (1 << 31) +#define FLITE_REG_CIWDOFST_CLROVIY (1 << 31) +#define FLITE_REG_CIWDOFST_WINHOROFST(x) ((x) << 16) +#define FLITE_REG_CIWDOFST_HOROFF_MASK (0x1fff << 16) +#define FLITE_REG_CIWDOFST_CLROVFICB (1 << 15) +#define FLITE_REG_CIWDOFST_CLROVFICR (1 << 14) +#define FLITE_REG_CIWDOFST_WINVEROFST(x) ((x) << 0) +#define FLITE_REG_CIWDOFST_VEROFF_MASK (0x1fff << 0) + +/* Cmaera Window Offset2 */ +#define FLITE_REG_CIWDOFST2 (0x14) +#define FLITE_REG_CIWDOFST2_WINHOROFST2(x) ((x) << 16) +#define FLITE_REG_CIWDOFST2_WINVEROFST2(x) ((x) << 0) + +/* Camera Output DMA Format */ +#define FLITE_REG_CIODMAFMT (0x18) +#define FLITE_REG_CIODMAFMT_1D_DMA (1 << 15) +#define FLITE_REG_CIODMAFMT_2D_DMA (0 << 15) +#define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) +#define FLITE_REG_CIODMAFMT_NORMAL (0 << 14) +#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4) +#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4) +#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4) +#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4) + +/* Camera Output Canvas */ +#define FLITE_REG_CIOCAN (0x20) +#define FLITE_REG_CIOCAN_OCAN_V(x) ((x) << 16) +#define FLITE_REG_CIOCAN_OCAN_H(x) ((x) << 0) + +/* Camera Output DMA Offset */ +#define FLITE_REG_CIOOFF (0x24) +#define FLITE_REG_CIOOFF_OOFF_V(x) ((x) << 16) +#define FLITE_REG_CIOOFF_OOFF_H(x) ((x) << 0) + +/* Camera Output DMA Address */ +#define FLITE_REG_CIOSA (0x30) +#define FLITE_REG_CIOSA_OSA(x) ((x) << 0) + +/* Camera Status */ +#define FLITE_REG_CISTATUS (0x40) +#define FLITE_REG_CISTATUS_MIPI_VVALID (1 << 22) +#define FLITE_REG_CISTATUS_MIPI_HVALID (1 << 21) +#define FLITE_REG_CISTATUS_MIPI_DVALID (1 << 20) +#define FLITE_REG_CISTATUS_ITU_VSYNC (1 << 14) +#define FLITE_REG_CISTATUS_ITU_HREFF (1 << 13) +#define FLITE_REG_CISTATUS_OVFIY (1 << 10) +#define FLITE_REG_CISTATUS_OVFICB (1 << 9) +#define FLITE_REG_CISTATUS_OVFICR (1 << 8) +#define FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW (1 << 7) +#define FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND (1 << 6) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART (1 << 5) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMEND (1 << 4) +#define FLITE_REG_CISTATUS_IRQ_CAM (1 << 0) +#define FLITE_REG_CISTATUS_IRQ_MASK (0xf << 4) + +/* Camera Status2 */ +#define FLITE_REG_CISTATUS2 (0x44) +#define FLITE_REG_CISTATUS2_LASTCAPEND (1 << 1) +#define FLITE_REG_CISTATUS2_FRMEND (1 << 0) + +/* Camera Status3 */ +#define FLITE_REG_CISTATUS3 0x48 +#define FLITE_REG_CISTATUS3_PRESENT_MASK (0x3F) + +/* Qos Threshold */ +#define FLITE_REG_CITHOLD (0xF0) +#define FLITE_REG_CITHOLD_W_QOS_EN (1 << 30) +#define FLITE_REG_CITHOLD_WTH_QOS(x) ((x) << 0) + +/* Camera General Purpose */ +#define FLITE_REG_CIGENERAL (0xFC) +#define FLITE_REG_CIGENERAL_CAM_A (0) +#define FLITE_REG_CIGENERAL_CAM_B (1) +#define FLITE_REG_CIGENERAL_CAM_C (2) +#define FLITE_REG_CIGENERAL_CAM_D (3) +#define FLITE_REG_CIGENERAL_3AA1_CAM_A (0 << 14) +#define FLITE_REG_CIGENERAL_3AA1_CAM_B (1 << 14) +#define FLITE_REG_CIGENERAL_3AA1_CAM_C (2 << 14) +#define FLITE_REG_CIGENERAL_3AA1_CAM_D (3 << 14) + +#define FLITE_REG_CIFCNTSEQ 0x100 + +/* BNS */ +#define FLITE_REG_BINNINGON (0x120) +#define FLITE_REG_BINNINGON_CLKGATE_ON(x) (~(x) << 1) +#define FLITE_REG_BINNINGON_EN(x) ((x) << 0) + +#define FLITE_REG_BINNINGCTRL (0x124) +#define FLITE_REG_BINNINGCTRL_FACTOR_Y(x) ((x) << 22) +#define FLITE_REG_BINNINGCTRL_FACTOR_X(x) ((x) << 17) +#define FLITE_REG_BINNINGCTRL_SHIFT_UP_Y(x) ((x) << 15) +#define FLITE_REG_BINNINGCTRL_SHIFT_UP_X(x) ((x) << 13) +#define FLITE_REG_BINNINGCTRL_PRECISION_BITS(x) ((x) << 10) +#define FLITE_REG_BINNINGCTRL_BITTAGE(x) ((x) << 5) +#define FLITE_REG_BINNINGCTRL_UNITY_SIZE(x) ((x) << 0) + +#define FLITE_REG_PEDESTAL (0x128) +#define FLITE_REG_PEDESTAL_OUT(x) ((x) << 12) +#define FLITE_REG_PEDESTAL_IN(x) ((x) << 0) + +#define FLITE_REG_BINNINGTOTAL (0x12C) +#define FLITE_REG_BINNINGTOTAL_HEIGHT(x) ((x) << 16) +#define FLITE_REG_BINNINGTOTAL_WIDTH(x) ((x) << 0) + +#define FLITE_REG_BINNINGINPUT (0x130) +#define FLITE_REG_BINNINGINPUT_HEIGHT(x) ((x) << 16) +#define FLITE_REG_BINNINGINPUT_WIDTH(x) ((x) << 0) + +#define FLITE_REG_BINNINGMARGIN (0x134) +#define FLITE_REG_BINNINGMARGIN_TOP(x) ((x) << 16) +#define FLITE_REG_BINNINGMARGIN_LEFT(x) ((x) << 0) + +#define FLITE_REG_BINNINGOUTPUT (0x138) +#define FLITE_REG_BINNINGOUTPUT_HEIGHT(x) ((x) << 16) +#define FLITE_REG_BINNINGOUTPUT_WIDTH(x) ((x) << 0) + +#define FLITE_REG_WEIGHTX01 (0x13C) +#define FLITE_REG_WEIGHTX01_1(x) ((x) << 16) +#define FLITE_REG_WEIGHTX01_0(x) ((x) << 0) + +#define FLITE_REG_WEIGHTX23 (0x140) +#define FLITE_REG_WEIGHTX23_1(x) ((x) << 16) +#define FLITE_REG_WEIGHTX23_0(x) ((x) << 0) + +#define FLITE_REG_WEIGHTY01 (0x15C) +#define FLITE_REG_WEIGHTY01_1(x) ((x) << 16) +#define FLITE_REG_WEIGHTY01_0(x) ((x) << 0) + +#define FLITE_REG_WEIGHTY23 (0x160) +#define FLITE_REG_WEIGHTY23_1(x) ((x) << 16) +#define FLITE_REG_WEIGHTY23_0(x) ((x) << 0) + +static void flite_hw_enable_bns(unsigned long __iomem *base_reg, bool enable) +{ + u32 cfg = 0; + + /* enable */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGON)); + cfg |= FLITE_REG_BINNINGON_CLKGATE_ON(enable); + cfg |= FLITE_REG_BINNINGON_EN(enable); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGON)); +} + +static void flite_hw_s_coeff_bns(unsigned long __iomem *base_reg, + u32 factor_x, u32 factor_y) +{ + u32 cfg = 0; + u32 factor = 0; + + factor = factor_x * factor_y; + if (factor_x != factor_y) + err("x y factor is not the same (%d, %d)", factor_x, factor_y); + + /* control */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGCTRL)); + cfg |= FLITE_REG_BINNINGCTRL_FACTOR_Y(factor_y); + cfg |= FLITE_REG_BINNINGCTRL_FACTOR_X(factor_x); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGCTRL)); + + switch (factor) { + case 9: + /* coefficient */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX01)); + cfg |= FLITE_REG_WEIGHTX01_1(0x20); + cfg |= FLITE_REG_WEIGHTX01_0(0xE0); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX01)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX23)); + cfg |= FLITE_REG_WEIGHTX23_1(0xA0); + cfg |= FLITE_REG_WEIGHTX23_0(0x60); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX23)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY01)); + cfg |= FLITE_REG_WEIGHTY01_1(0x20); + cfg |= FLITE_REG_WEIGHTY01_0(0xE0); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY01)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY23)); + cfg |= FLITE_REG_WEIGHTY23_1(0xA0); + cfg |= FLITE_REG_WEIGHTY23_0(0x60); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY23)); + break; + case 16: + /* coefficient */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX01)); + cfg |= FLITE_REG_WEIGHTX01_1(0x40); + cfg |= FLITE_REG_WEIGHTX01_0(0xC0); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTX01)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY01)); + cfg |= FLITE_REG_WEIGHTY01_1(0x40); + cfg |= FLITE_REG_WEIGHTY01_0(0xC0); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_WEIGHTY01)); + break; + default: + err("unknown factor!! (%d, %d)", factor_x, factor_y); + break; + } +} + +static void flite_hw_s_size_bns(unsigned long __iomem *base_reg, + u32 width, u32 height, u32 otf_width, u32 otf_height) +{ + u32 cfg = 0; + + /* size */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGTOTAL)); + cfg |= FLITE_REG_BINNINGTOTAL_HEIGHT(height); + cfg |= FLITE_REG_BINNINGTOTAL_WIDTH(width); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGTOTAL)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGINPUT)); + cfg |= FLITE_REG_BINNINGINPUT_HEIGHT(height); + cfg |= FLITE_REG_BINNINGINPUT_WIDTH(width); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGINPUT)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGMARGIN)); + cfg |= FLITE_REG_BINNINGMARGIN_TOP(0); + cfg |= FLITE_REG_BINNINGMARGIN_LEFT(0); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGMARGIN)); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGOUTPUT)); + cfg |= FLITE_REG_BINNINGOUTPUT_HEIGHT(otf_height); + cfg |= FLITE_REG_BINNINGOUTPUT_WIDTH(otf_width); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_BINNINGOUTPUT)); +} + +static int flite_hw_set_bns(unsigned long __iomem *base_reg, + struct fimc_is_image *image) +{ + int ret = 0; + u32 width, height; + u32 otf_width, otf_height; + u32 factor_x, factor_y; + + BUG_ON(!image); + + width = image->window.width; + height = image->window.height; + otf_width = image->window.otf_width; + otf_height = image->window.otf_height; + + if (otf_width == width && otf_height == height) { + info("input & output sizes are same(%d, %d)\n", otf_width, otf_height); + goto exit; + } + + if (otf_width == 0 || otf_height == 0) { + warn("bns size is zero. s_ctrl(V4L2_CID_IS_S_BNS) first\n"); + goto exit; + } + + factor_x = 2 * width / otf_width; + factor_y = 2 * height / otf_height; + + flite_hw_s_size_bns(base_reg, width, height, otf_width, otf_height); + + flite_hw_s_coeff_bns(base_reg, factor_x, factor_y); + + flite_hw_enable_bns(base_reg, true); + + info("BNS in(%d, %d), BNS out(%d, %d), ratio(%d, %d)\n", + width, height, otf_width, otf_height, factor_x, factor_y); +exit: + return ret; +} + +static void flite_hw_set_cam_source_size(unsigned long __iomem *base_reg, + struct fimc_is_image *image) +{ + u32 cfg = 0; + + BUG_ON(!image); + +#ifdef COLORBAR_MODE + cfg |= FLITE_REG_CISRCSIZE_SIZE_H(640); + cfg |= FLITE_REG_CISRCSIZE_SIZE_V(480); +#else + cfg |= FLITE_REG_CISRCSIZE_SIZE_H(image->window.o_width); + cfg |= FLITE_REG_CISRCSIZE_SIZE_V(image->window.o_height); +#endif + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CISRCSIZE)); +} + +static void flite_hw_set_dma_offset(unsigned long __iomem *base_reg, + struct fimc_is_image *image) +{ + u32 cfg = 0; + + BUG_ON(!image); + + /* HACK */ + if (image->format.pixelformat == V4L2_PIX_FMT_SBGGR10 || image->format.pixelformat == V4L2_PIX_FMT_SBGGR12) + cfg |= FLITE_REG_CIOCAN_OCAN_H(roundup(image->window.o_width, 10)); + else + cfg |= FLITE_REG_CIOCAN_OCAN_H(image->window.o_width); + + cfg |= FLITE_REG_CIOCAN_OCAN_V(image->window.o_height); + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIOCAN)); +} + +static void flite_hw_set_cam_channel(unsigned long __iomem *base_reg, + u32 otf_setting) +{ + u32 cfg = 0; + + cfg |= otf_setting; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGENERAL)); +} + +static void flite_hw_set_capture_start(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIIMGCPT)); + cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIIMGCPT)); +} + +static void flite_hw_set_capture_stop(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIIMGCPT)); + cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIIMGCPT)); +} + +static int flite_hw_set_source_format(unsigned long __iomem *base_reg, struct fimc_is_image *image) +{ + int ret = 0; + u32 pixelformat, format, cfg; + + BUG_ON(!image); + + pixelformat = image->format.pixelformat; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + switch (pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + format = HW_FORMAT_RAW8; + break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + format = HW_FORMAT_RAW10; + break; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + format = HW_FORMAT_RAW10; + /* + * HACK : hal send RAW10 for RAW12 + * formt = HW_FORMAT_RAW12 << 24; + */ + break; + case V4L2_PIX_FMT_YUYV: + format = HW_FORMAT_YUV422_8BIT; + break; + case V4L2_PIX_FMT_JPEG: + format = HW_FORMAT_USER; + break; + default: + err("unsupported format(%X)", pixelformat); + format = HW_FORMAT_RAW10; + ret = -EINVAL; + break; + } + +#ifdef COLORBAR_MODE + cfg |= (HW_FORMAT_YUV422_8BIT << 24); +#else + cfg |= (format << 24); +#endif + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + return ret; +} + +static void flite_hw_set_dma_fmt(unsigned long __iomem *base_reg, + u32 pixelformat) +{ + u32 cfg = 0; + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIODMAFMT)); + + if (pixelformat == V4L2_PIX_FMT_SBGGR10 || pixelformat == V4L2_PIX_FMT_SBGGR12) + cfg |= FLITE_REG_CIODMAFMT_PACK12; + else + cfg |= FLITE_REG_CIODMAFMT_NORMAL; + + if (pixelformat == V4L2_PIX_FMT_SGRBG8) + cfg |= FLITE_REG_CIODMAFMT_1D_DMA; + else + cfg |= FLITE_REG_CIODMAFMT_2D_DMA; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIODMAFMT)); +} + +static void flite_hw_set_output_dma(unsigned long __iomem *base_reg, bool enable, + u32 pixelformat) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + if (enable) { + cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE; + flite_hw_set_dma_fmt(base_reg, pixelformat); + } else { + cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE; + } + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_set_output_local(unsigned long __iomem *base_reg, bool enable) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + if (enable) + cfg &= ~FLITE_REG_CIGCTRL_OLOCAL_DISABLE; + else + cfg |= FLITE_REG_CIGCTRL_OLOCAL_DISABLE; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +#ifdef COLORBAR_MODE +static void flite_hw_set_test_pattern_enable(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + + /* will use for pattern generation testing */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} +#endif + +static void flite_hw_set_interrupt_source(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* for checking stop complete */ + cfg &= ~FLITE_REG_CIGCTRL_IRQ_LASTEN0_DISABLE; + + /* for checking frame start */ + cfg &= ~FLITE_REG_CIGCTRL_IRQ_STARTEN0_DISABLE; + + /* for checking frame end */ + cfg &= ~FLITE_REG_CIGCTRL_IRQ_ENDEN0_DISABLE; + + /* for checking overflow */ + cfg &= ~FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_clr_interrupt_source(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* for checking stop complete */ + cfg |= FLITE_REG_CIGCTRL_IRQ_LASTEN0_DISABLE; + + /* for checking frame start */ + cfg |= FLITE_REG_CIGCTRL_IRQ_STARTEN0_DISABLE; + + /* for checking frame end */ + cfg |= FLITE_REG_CIGCTRL_IRQ_ENDEN0_DISABLE; + + /* for checking overflow */ + cfg |= FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_set_ovf_interrupt_source(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* for checking overflow */ + cfg &= ~FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_clr_ovf_interrupt_source(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* for checking overflow */ + cfg |= FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static int flite_hw_check_ovf_interrupt_source(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* for checking overflow */ + if (cfg & FLITE_REG_CIGCTRL_IRQ_OVFEN0_DISABLE) + return true; + + return false; +} + +static void flite_hw_force_reset(unsigned long __iomem *base_reg) +{ + u32 cfg = 0, retry = 100; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* request sw reset */ + cfg |= FLITE_REG_CIGCTRL_SWRST_REQ; + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + /* checking reset ready */ + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + while (retry-- && !(cfg & FLITE_REG_CIGCTRL_SWRST_RDY)) + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + if (!(cfg & FLITE_REG_CIGCTRL_SWRST_RDY)) + warn("[CamIF] sw reset is not read but forcelly"); + + /* sw reset */ + cfg |= FLITE_REG_CIGCTRL_SWRST; + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + warn("[CamIF] sw reset"); +} + +static void flite_hw_set_inverse_polarity(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + cfg &= ~(FLITE_REG_CIGCTRL_INVPOLPCLK | FLITE_REG_CIGCTRL_INVPOLVSYNC + | FLITE_REG_CIGCTRL_INVPOLHREF); + + /* cfg |= (FLITE_REG_CIGCTRL_INVPOLPCLK | FLITE_REG_CIGCTRL_INVPOLVSYNC); */ + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_set_camera_type(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); + + cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGCTRL)); +} + +static void flite_hw_set_window_offset(unsigned long __iomem *base_reg, + struct fimc_is_image *image) +{ + u32 cfg = 0; + u32 hoff2, voff2; + + BUG_ON(!image); + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIWDOFST)); + cfg &= ~(FLITE_REG_CIWDOFST_HOROFF_MASK | + FLITE_REG_CIWDOFST_VEROFF_MASK); + cfg |= FLITE_REG_CIWDOFST_WINOFSEN | + FLITE_REG_CIWDOFST_WINHOROFST(image->window.offs_h) | + FLITE_REG_CIWDOFST_WINVEROFST(image->window.offs_v); + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIWDOFST)); + + hoff2 = image->window.o_width - image->window.width - image->window.offs_h; + voff2 = image->window.o_height - image->window.height - image->window.offs_v; + cfg = FLITE_REG_CIWDOFST2_WINHOROFST2(hoff2) | + FLITE_REG_CIWDOFST2_WINVEROFST2(voff2); + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIWDOFST2)); +} + +static void flite_hw_set_last_capture_end_clear(unsigned long __iomem *base_reg) +{ + u32 cfg = 0; + + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS2)); + cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND; + + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS2)); +} + +int flite_hw_get_present_frame_buffer(unsigned long __iomem *base_reg) +{ + u32 status = 0; + + status = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS3)); + status &= FLITE_REG_CISTATUS3_PRESENT_MASK; + + return status; +} + +int flite_hw_get_status2(unsigned long __iomem *base_reg) +{ + u32 status = 0; + + status = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS2)); + + return status; +} + +void flite_hw_set_status1(unsigned long __iomem *base_reg, u32 val) +{ + writel(val, base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS)); +} + +int flite_hw_get_status1(unsigned long __iomem *base_reg) +{ + u32 status = 0; + + status = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS)); + + return status; +} + +int flite_hw_getnclr_status1(unsigned long __iomem *base_reg) +{ + u32 status = 0; + + status = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS)); + writel(0, base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS)); + + return status; +} + +void flite_hw_set_status2(unsigned long __iomem *base_reg, u32 val) +{ + writel(val, base_reg + TO_WORD_OFFSET(FLITE_REG_CISTATUS2)); +} + +void flite_hw_set_start_addr(unsigned long __iomem *base_reg, u32 number, u32 addr) +{ + unsigned long __iomem *target_reg; + + if (number == 0) { + target_reg = base_reg + TO_WORD_OFFSET(0x30); + } else { + number--; + target_reg = base_reg + TO_WORD_OFFSET(0x200 + (0x4*number)); + } + + writel(addr, target_reg); +} + +void flite_hw_set_use_buffer(unsigned long __iomem *base_reg, u32 number) +{ + u32 buffer; + buffer = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIFCNTSEQ)); + buffer |= 1< FLITE1\n"); + cfg = readl(base_reg + TO_WORD_OFFSET(FLITE_REG_CIGENERAL)); + cfg |= (1 << 12); + writel(cfg, base_reg + TO_WORD_OFFSET(FLITE_REG_CIGENERAL)); + } +} + +int init_fimc_lite(unsigned long __iomem *base_reg) +{ + int i; + + writel(0, base_reg + TO_WORD_OFFSET(FLITE_REG_CIFCNTSEQ)); + + for (i = 0; i < 32; i++) + flite_hw_set_start_addr(base_reg , i, 0xffffffff); + + return 0; +} + +static int start_fimc_lite(unsigned long __iomem *base_reg, + struct fimc_is_image *image, + u32 otf_setting, + u32 bns, + u32 csi_ch, + u32 flite_ch) +{ + flite_hw_set_cam_channel(base_reg, otf_setting); + flite_hw_set_cam_source_size(base_reg, image); + flite_hw_set_dma_offset(base_reg, image); + flite_hw_set_camera_type(base_reg); + flite_hw_set_source_format(base_reg, image); + flite_hw_set_inverse_polarity(base_reg); + flite_hw_set_interrupt_source(base_reg); + flite_hw_set_window_offset(base_reg, image); + flite_hw_set_mux(base_reg, csi_ch, flite_ch); + +#ifdef COLORBAR_MODE + flite_hw_set_test_pattern_enable(base_reg); +#endif + + if (bns) + flite_hw_set_bns(base_reg, image); + + flite_hw_set_last_capture_end_clear(base_reg); + flite_hw_set_capture_start(base_reg); + + return 0; +} + +static inline void stop_fimc_lite(unsigned long __iomem *base_reg) +{ + flite_hw_set_capture_stop(base_reg); +} + +static inline void flite_s_buffer_addr(struct fimc_is_device_flite *device, + u32 bindex, u32 baddr) +{ + flite_hw_set_start_addr(device->base_reg, bindex, baddr); +} + +static inline int flite_s_use_buffer(struct fimc_is_device_flite *flite, + u32 bindex) +{ + int ret = 0; + unsigned long target_time; + + BUG_ON(!flite); + + if (!atomic_read(&flite->bcount)) { + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) { + target_time = jiffies + + msecs_to_jiffies(flite->buf_done_wait_time); + while ((target_time > jiffies) && + (flite_hw_get_status1(flite->base_reg) && (7 << 20))) + pr_debug("over vblank (early buffer done)"); + } + + if (flite_hw_get_status1(flite->base_reg) && (7 << 20)) { + merr("over vblank (buf-mode : %d)", flite, flite->buf_done_mode); + ret = -EINVAL; + goto p_err; + } + + flite_hw_set_use_buffer(flite->base_reg, bindex); + atomic_inc(&flite->bcount); + flite_hw_set_output_dma(flite->base_reg, true, flite->image.format.pixelformat); + } else { + flite_hw_set_use_buffer(flite->base_reg, bindex); + atomic_inc(&flite->bcount); + } + +p_err: + return ret; +} + +static inline int flite_s_unuse_buffer(struct fimc_is_device_flite *flite, + u32 bindex) +{ + int ret = 0; + unsigned long target_time; + + BUG_ON(!flite); + + if (atomic_read(&flite->bcount) == 1) { + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) { + target_time = jiffies + + msecs_to_jiffies(flite->buf_done_wait_time); + while ((target_time > jiffies) && + (flite_hw_get_status1(flite->base_reg) && (7 << 20))) + pr_debug("over vblank (early buffer done)"); + } + + if (flite_hw_get_status1(flite->base_reg) && (7 << 20)) { + merr("over vblank (buf-mode : %d)", flite, flite->buf_done_mode); + ret = -EINVAL; + goto p_err; + } + + flite_hw_set_output_dma(flite->base_reg, false, flite->image.format.pixelformat); + flite_hw_set_unuse_buffer(flite->base_reg, bindex); + atomic_dec(&flite->bcount); + } else { + flite_hw_set_unuse_buffer(flite->base_reg, bindex); + atomic_dec(&flite->bcount); + } + +p_err: + return ret; +} + +static inline void flite_set_ovf_stop(struct fimc_is_device_flite *flite) +{ + struct fimc_is_device_sensor *device; + + device = container_of(flite->subdev, struct fimc_is_device_sensor, subdev_flite); + + if (test_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state)) + return; + + /* set dtp */ + set_bit(FIMC_IS_FLITE_OVERFLOW_STOP, &device->force_stop); + + return; +} + +static u32 g_print_cnt; +#define LOG_INTERVAL_OF_DROPS 30 +static void tasklet_flite_str0(unsigned long data) +{ + struct v4l2_subdev *subdev; + struct fimc_is_device_flite *flite; + struct fimc_is_device_sensor *device; + struct fimc_is_device_ischain *ischain; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group_3aa, *group_isp; + u32 bstart, fcount, present; + + subdev = (struct v4l2_subdev *)data; + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + BUG(); + } + + device = v4l2_get_subdev_hostdata(subdev); + if (!device) { + err("device is NULL"); + BUG(); + } + + present = flite_hw_get_present_frame_buffer(flite->base_reg) - 1; + bstart = flite->tasklet_param_str; + fcount = atomic_read(&flite->fcount); + ischain = device->ischain; + +#ifdef TASKLET_MSG + info("S%d %d\n", bstart, fcount); +#endif + + /* comparing sw state and hw state */ + if (atomic_read(&flite->bcount) == 2) { + if ((bstart == FLITE_A_SLOT_VALID) && + (present != FLITE_A_SLOT_VALID)) { + mwarn("wrong SW buffer slot A(sw:%d != hw:%d)", flite, bstart, present); + flite->sw_trigger = bstart = FLITE_B_SLOT_VALID; + } + + if ((bstart == FLITE_B_SLOT_VALID) && + (present != FLITE_B_SLOT_VALID)) { + mwarn("wrong SW buffer slot B(sw:%d != hw:%d)", flite, bstart, present); + flite->sw_trigger = bstart = FLITE_A_SLOT_VALID; + } + } + + groupmgr = ischain->groupmgr; + group_3aa = &ischain->group_3aa; + group_isp = &ischain->group_isp; + if (unlikely(list_empty(&group_3aa->smp_trigger.wait_list))) { + atomic_set(&group_3aa->sensor_fcount, fcount + group_3aa->async_shots); + + /* + * pcount : program count + * current program count(location) in kthread + */ + if (((g_print_cnt % LOG_INTERVAL_OF_DROPS) == 0) || + (g_print_cnt < LOG_INTERVAL_OF_DROPS)) { + info("grp1(res %d, rcnt %d, scnt %d), " + "grp2(res %d, rcnt %d, scnt %d), " + "fcount %d(%d, %d) pcount %d\n", + groupmgr->group_smp_res[group_3aa->id].count, + atomic_read(&group_3aa->rcount), + atomic_read(&group_3aa->scount), + groupmgr->group_smp_res[group_isp->id].count, + atomic_read(&group_isp->rcount), + atomic_read(&group_isp->scount), + fcount + group_3aa->async_shots, + *last_fcount0, *last_fcount1, group_3aa->pcount); + } + g_print_cnt++; + } else { + g_print_cnt = 0; + atomic_set(&group_3aa->sensor_fcount, fcount + group_3aa->async_shots); + up(&group_3aa->smp_trigger); + } + + v4l2_subdev_notify(subdev, FLITE_NOTIFY_FSTART, &fcount); +} + +static void tasklet_flite_str1(unsigned long data) +{ + struct v4l2_subdev *subdev; + struct fimc_is_device_flite *flite; + u32 bstart, fcount, present; + + subdev = (struct v4l2_subdev *)data; + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + BUG(); + } + + present = flite_hw_get_present_frame_buffer(flite->base_reg) - 1; + bstart = flite->tasklet_param_str; + fcount = atomic_read(&flite->fcount); + +#ifdef TASKLET_MSG + info("S%d %d\n", bstart, fcount); +#endif + + /* comparing sw state and hw state */ + if (atomic_read(&flite->bcount) == 2) { + if ((bstart == FLITE_A_SLOT_VALID) && + (present != FLITE_A_SLOT_VALID)) { + mwarn("wrong SW buffer slot A(sw:%d != hw:%d)", flite, bstart, present); + flite->sw_trigger = bstart = FLITE_B_SLOT_VALID; + } + + if ((bstart == FLITE_B_SLOT_VALID) && + (present != FLITE_B_SLOT_VALID)) { + mwarn("wrong SW buffer slot B(sw:%d != hw:%d)", flite, bstart, present); + flite->sw_trigger = bstart = FLITE_A_SLOT_VALID; + } + } + + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) { + flite->early_work_called = false; + if (flite->early_work_skip == true) { + info("flite early work skip"); + flite->early_work_skip = false; + } else { + queue_delayed_work(flite->early_workqueue, &flite->early_work_wq, + msecs_to_jiffies(flite->buf_done_wait_time)); + } + } + + v4l2_subdev_notify(subdev, FLITE_NOTIFY_FSTART, &fcount); +} + +static void tasklet_flite_end(unsigned long data) +{ + struct fimc_is_device_flite *flite; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct fimc_is_frame *frame_done; + struct v4l2_subdev *subdev; + u32 bdone; + + frame_done = NULL; + subdev = (struct v4l2_subdev *)data; + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + BUG(); + } + + if ((flite->buf_done_mode == FLITE_BUF_DONE_EARLY) && + test_bit(FLITE_LAST_CAPTURE, &flite->state)) { + info("Skip due to Last Frame Capture\n"); + return; + } + +#ifdef ENABLE_FLITE_OVERFLOW_STOP + if (unlikely(flite->overflow_cnt)) { + flite_set_ovf_stop(flite); + goto ovf_exit; + } +#endif + + framemgr = flite->framemgr; + bdone = flite->tasklet_param_end; + +#ifdef TASKLET_MSG + info("E%d %d\n", bdone, atomic_read(&flite->fcount)); +#endif + + if (flite_hw_check_ovf_interrupt_source(flite->base_reg)) + flite_hw_set_ovf_interrupt_source(flite->base_reg); + + framemgr_e_barrier(framemgr, FMGR_IDX_1 + bdone); + + if (test_bit(bdone, &flite->state)) { + fimc_is_frame_process_head(framemgr, &frame); + if (frame) { +#ifdef MEASURE_TIME +#ifdef EXTERNAL_TIME + do_gettimeofday(&frame->tzone[TM_FLITE_END]); +#endif +#endif + /* 1. current frame transition to completion */ + frame_done = frame; + + /* 2. next frame ready */ + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + flite_s_buffer_addr(flite, bdone, + frame->dvaddr_buffer[0]); + set_bit(bdone, &flite->state); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + } else { + if (!flite_s_unuse_buffer(flite, bdone)) { + clear_bit(bdone, &flite->state); +#ifdef TASKLET_MSG + merr("[SEN] request is empty0(%d slot)", flite, bdone); +#endif + } else { + frame_done = NULL; + } + } + + if (frame_done) + fimc_is_frame_trans_pro_to_com(framemgr, frame_done); + + } else { +#ifdef TASKLET_MSG + merr("[SEN] process is empty(%d, %ld)", flite, bdone, flite->state); + fimc_is_frame_print_all(framemgr); +#endif + } + } else { + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + flite_s_buffer_addr(flite, bdone, frame->dvaddr_buffer[0]); + if (!flite_s_use_buffer(flite, bdone)) { + set_bit(bdone, &flite->state); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + + /* swap process queue list */ + fimc_is_frame_swap_process_head(framemgr); + } + } else { +#ifdef TASKLET_MSG + merr("request is empty1(%d slot)", flite, bdone); + fimc_is_frame_print_all(framemgr); +#endif + } + } + + framemgr_x_barrier(framemgr, FMGR_IDX_1 + bdone); + +#ifdef ENABLE_FLITE_OVERFLOW_STOP +ovf_exit: +#endif + v4l2_subdev_notify(subdev, FLITE_NOTIFY_FEND, frame_done); + flite->early_work_called = true; +} + +static void tasklet_flite_end_chk(unsigned long data) +{ + struct v4l2_subdev *subdev = NULL; + struct fimc_is_device_flite *flite = NULL; + subdev = (struct v4l2_subdev *)data; + flite = v4l2_get_subdevdata(subdev); + + if (flite->early_work_called == false) { + u32 fcount = atomic_read(&flite->fcount); + info("[CamIF%d][%08d] delayed work queue was slower than end irq", + flite->instance, fcount); + } + + return; +} + +#ifdef SUPPORTED_EARLY_BUF_DONE +static void wq_func_flite_early_work(struct work_struct *data) +{ + struct fimc_is_device_flite *flite = NULL; + struct delayed_work *early_work_wq = NULL; + + early_work_wq = container_of(data, struct delayed_work, + work); + flite = container_of(early_work_wq, struct fimc_is_device_flite, + early_work_wq); + + if (!flite) { + err("flite is NULL"); + BUG(); + } + + flite->tasklet_param_end = flite->sw_trigger; + tasklet_schedule(&flite->tasklet_flite_early_end); +} + +static void chk_early_buf_done(struct fimc_is_device_flite *flite, u32 framerate, u32 position) +{ + /* ms */ + u32 margin = 0; + u32 duration = 0; + + /* HACK: applied on 15~30fps forcely */ + if (framerate > 15 && framerate <= 30) { + + duration = 1000 / framerate; + + if (position == SENSOR_POSITION_REAR) + flite->early_buf_done_mode = FLITE_BUF_EARLY_30P; + else + flite->early_buf_done_mode = FLITE_BUF_EARLY_20P; + + margin = FLITE_VVALID_TIME_BASE * (flite->early_buf_done_mode * 0.1f); + + if (margin >= duration) { + /* normal buffer done mode */ + flite->buf_done_mode = FLITE_BUF_DONE_NORMAL; + flite->early_buf_done_mode = FLITE_BUF_EARLY_NOTHING; + flite->buf_done_wait_time = 0; + } else { + /* early buffer done mode */ + flite->buf_done_mode = FLITE_BUF_DONE_EARLY; + flite->buf_done_wait_time = duration - margin; + } + } else { + /* normal buffer done mode */ + flite->buf_done_mode = FLITE_BUF_DONE_NORMAL; + flite->early_buf_done_mode = FLITE_BUF_EARLY_NOTHING; + flite->buf_done_wait_time = 0; + } + + info("[CamIF%d] buffer done mode [m%d/em%d/du%d/mg%d/wt%d]", flite->instance, + flite->buf_done_mode, flite->early_buf_done_mode, duration, margin, + flite->buf_done_wait_time); +} +#endif + +static inline void notify_fcount(struct fimc_is_device_flite *flite) +{ + if (test_bit(FLITE_JOIN_ISCHAIN, &flite->state)) { + if (flite->instance== FLITE_ID_A) + writel(atomic_read(&flite->fcount), notify_fcount_sen0); + else if (flite->instance == FLITE_ID_B) + writel(atomic_read(&flite->fcount), notify_fcount_sen1); + else if (flite->instance == FLITE_ID_C) + writel(atomic_read(&flite->fcount), notify_fcount_sen2); + else + err("unresolved channel(%d)", flite->instance); + } +} + +static irqreturn_t fimc_is_flite_isr(int irq, void *data) +{ + u32 status, status1, status2, i; + struct fimc_is_device_flite *flite; + + flite = data; + status1 = flite_hw_getnclr_status1(flite->base_reg); + status = status1 & (3 << 4); + + if (test_bit(FLITE_LAST_CAPTURE, &flite->state)) { + if (status1) { + info("[CamIF%d] last status1 : 0x%08X\n", flite->instance, status1); + goto clear_status; + } + + err("[CamIF%d] unintended intr is occured", flite->instance); + + for (i = 0; i < 278; i += 4) + info("REG[%X] : 0x%08X\n", i, readl(flite->base_reg + i)); + + flite_hw_force_reset(flite->base_reg); + + goto clear_status; + } + + if (status) { + if (status == (3 << 4)) { +#ifdef DBG_FLITEISR + printk(KERN_CONT "*"); +#endif + /* frame both interrupt since latency */ + if (flite->sw_checker) { +#ifdef DBG_FLITEISR + printk(KERN_CONT ">"); +#endif + /* frame end interrupt */ + flite->sw_checker = EXPECT_FRAME_START; + flite->tasklet_param_end = flite->sw_trigger; + tasklet_schedule(&flite->tasklet_flite_end); +#ifdef DBG_FLITEISR + printk(KERN_CONT "<"); +#endif + /* frame start interrupt */ + flite->sw_checker = EXPECT_FRAME_END; + if (flite->sw_trigger) + flite->sw_trigger = FLITE_A_SLOT_VALID; + else + flite->sw_trigger = FLITE_B_SLOT_VALID; + flite->tasklet_param_str = flite->sw_trigger; + atomic_inc(&flite->fcount); + notify_fcount(flite); + tasklet_schedule(&flite->tasklet_flite_str); + } else { + /* W/A: Skip start tasklet at interrupt lost case */ + warn("[CamIF%d] invalid interrupt interval", + flite->instance); + goto clear_status; +/* HACK: Disable dead code because of Prevent Issue */ +#if 0 +#ifdef DBG_FLITEISR + printk(KERN_CONT "<"); +#endif + /* frame start interrupt */ + flite->sw_checker = EXPECT_FRAME_END; + if (flite->sw_trigger) + flite->sw_trigger = FLITE_A_SLOT_VALID; + else + flite->sw_trigger = FLITE_B_SLOT_VALID; + flite->tasklet_param_str = flite->sw_trigger; + atomic_inc(&flite->fcount); + notify_fcount(flite); + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) + flite->early_work_skip = true; + tasklet_schedule(&flite->tasklet_flite_str); +#ifdef DBG_FLITEISR + printk(KERN_CONT ">"); +#endif + /* frame end interrupt */ + flite->sw_checker = EXPECT_FRAME_START; + flite->tasklet_param_end = flite->sw_trigger; + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) + tasklet_schedule(&flite->tasklet_flite_early_end); + tasklet_schedule(&flite->tasklet_flite_end); +#endif + } + } else if (status == (2 << 4)) { + /* W/A: Skip start tasklet at interrupt lost case */ + if (flite->sw_checker != EXPECT_FRAME_START) + warn("[CamIF%d] Lost end interupt\n", + flite->instance); +#ifdef DBG_FLITEISR + printk(KERN_CONT "<"); +#endif + /* frame start interrupt */ + flite->sw_checker = EXPECT_FRAME_END; + if (flite->sw_trigger) + flite->sw_trigger = FLITE_A_SLOT_VALID; + else + flite->sw_trigger = FLITE_B_SLOT_VALID; + flite->tasklet_param_str = flite->sw_trigger; + atomic_inc(&flite->fcount); + notify_fcount(flite); + tasklet_schedule(&flite->tasklet_flite_str); + } else { + /* W/A: Skip end tasklet at interrupt lost case */ + if (flite->sw_checker != EXPECT_FRAME_END) { + warn("[CamIF%d] Lost start interupt\n", + flite->instance); + goto clear_status; + } +#ifdef DBG_FLITEISR + printk(KERN_CONT ">"); +#endif + /* frame end interrupt */ + flite->sw_checker = EXPECT_FRAME_START; + if (flite->buf_done_mode == FLITE_BUF_DONE_NORMAL) + flite->tasklet_param_end = flite->sw_trigger; + tasklet_schedule(&flite->tasklet_flite_end); + } + } + +clear_status: + if (status1 & (1 << 6)) { + /* Last Frame Capture Interrupt */ + info("[CamIF%d] Last Frame Capture(fcount : %d)\n", + flite->instance, atomic_read(&flite->fcount)); + + /* Clear LastCaptureEnd bit */ + status2 = flite_hw_get_status2(flite->base_reg); + status2 &= ~(0x1 << 1); + flite_hw_set_status2(flite->base_reg, status2); + + /* Notify last capture */ + set_bit(FLITE_LAST_CAPTURE, &flite->state); + wake_up(&flite->wait_queue); + } + + if (status1 & (1 << 8)) { + u32 ciwdofst; + + flite_hw_clr_ovf_interrupt_source(flite->base_reg); + flite->overflow_cnt++; + + if ((flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0) || + (flite->overflow_cnt < FLITE_OVERFLOW_COUNT)) + pr_err("[CamIF%d] OFCR(cnt:%u)\n", flite->instance, flite->overflow_cnt); + ciwdofst = readl(flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst |= (0x1 << 14); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst &= ~(0x1 << 14); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + } + + if (status1 & (1 << 9)) { + u32 ciwdofst; + + flite_hw_clr_ovf_interrupt_source(flite->base_reg); + flite->overflow_cnt++; + + if ((flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0) || + (flite->overflow_cnt < FLITE_OVERFLOW_COUNT)) + pr_err("[CamIF%d] OFCB(cnt:%u)\n", flite->instance, flite->overflow_cnt); + ciwdofst = readl(flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst |= (0x1 << 15); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst &= ~(0x1 << 15); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + } + + if (status1 & (1 << 10)) { + u32 ciwdofst; + + flite_hw_clr_ovf_interrupt_source(flite->base_reg); + flite->overflow_cnt++; + + if ((flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0) || + (flite->overflow_cnt < FLITE_OVERFLOW_COUNT)) { + pr_err("[CamIF%d] OFY(cnt:%u)\n", flite->instance, flite->overflow_cnt); +#ifdef ENABLE_FLITE_OVERFLOW_STOP + tasklet_schedule(&flite->tasklet_flite_end); +#endif + } + ciwdofst = readl(flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst |= (0x1 << 30); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + ciwdofst &= ~(0x1 << 30); + writel(ciwdofst, flite->base_reg + TO_WORD_OFFSET(0x10)); + } + + return IRQ_HANDLED; +} + +int fimc_is_flite_open(struct v4l2_subdev *subdev, + struct fimc_is_framemgr *framemgr) +{ + int ret = 0; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + BUG_ON(!framemgr); + + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + flite->group = 0; + flite->framemgr = framemgr; + atomic_set(&flite->fcount, 0); + atomic_set(&flite->bcount, 0); + + clear_bit(FLITE_JOIN_ISCHAIN, &flite->state); + clear_bit(FLITE_OTF_WITH_3AA, &flite->state); + clear_bit(FLITE_LAST_CAPTURE, &flite->state); + clear_bit(FLITE_A_SLOT_VALID, &flite->state); + clear_bit(FLITE_B_SLOT_VALID, &flite->state); + + switch(flite->instance) { + case FLITE_ID_A: + ret = request_irq(IRQ_FIMC_LITE0, + fimc_is_flite_isr, + IRQF_SHARED, + "fimc-lite0", + flite); + if (ret) + err("request_irq(L0) failed\n"); + break; + case FLITE_ID_B: + ret = request_irq(IRQ_FIMC_LITE1, + fimc_is_flite_isr, + IRQF_SHARED, + "fimc-lite1", + flite); + if (ret) + err("request_irq(L1) failed\n"); + break; +#ifdef IRQ_FIMC_LITE2 + case FLITE_ID_C: + ret = request_irq(IRQ_FIMC_LITE2, + fimc_is_flite_isr, + IRQF_SHARED, + "fimc-lite2", + flite); + if (ret) + err("request_irq(L2) failed\n"); + break; +#endif + default: + err("instance is invalid(%d)", flite->instance); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_flite_close(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + switch(flite->instance) { + case FLITE_ID_A: + free_irq(IRQ_FIMC_LITE0, flite); + break; + case FLITE_ID_B: + free_irq(IRQ_FIMC_LITE1, flite); + break; +#ifdef IRQ_FIMC_LITE2 + case FLITE_ID_C: + free_irq(IRQ_FIMC_LITE2, flite); + break; +#endif + default: + err("instance is invalid(%d)", flite->instance); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +/* value : csi ch */ +static int flite_init(struct v4l2_subdev *subdev, u32 value) +{ + int ret = 0; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + + flite = v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + flite->csi = value; + +p_err: + return ret; +} + +static int flite_stream_on(struct v4l2_subdev *subdev, + struct fimc_is_device_flite *flite) +{ + int ret = 0; + u32 otf_setting, bns; + bool buffer_ready; + unsigned long flags; + struct fimc_is_image *image; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct fimc_is_device_sensor *device = v4l2_get_subdev_hostdata(subdev); + + BUG_ON(!flite); + BUG_ON(!flite->framemgr); + BUG_ON(!device); + + otf_setting = 0; + buffer_ready = false; + framemgr = flite->framemgr; + image = &flite->image; + bns = device->pdata->is_bns; + + flite->overflow_cnt = 0; + flite->sw_trigger = FLITE_B_SLOT_VALID; + flite->sw_checker = EXPECT_FRAME_START; + flite->tasklet_param_str = 0; + flite->tasklet_param_end = 0; + atomic_set(&flite->bcount, 0); + clear_bit(FLITE_LAST_CAPTURE, &flite->state); + clear_bit(FLITE_A_SLOT_VALID, &flite->state); + clear_bit(FLITE_B_SLOT_VALID, &flite->state); + + /* 1. init */ + flite_hw_force_reset(flite->base_reg); + init_fimc_lite(flite->base_reg); + + /* 2. dma setting */ + framemgr_e_barrier_irqs(framemgr, 0, flags); + + if (framemgr->frame_req_cnt >= 1) { + fimc_is_frame_request_head(framemgr, &frame); + flite_s_use_buffer(flite, FLITE_A_SLOT_VALID); + flite_s_buffer_addr(flite, FLITE_A_SLOT_VALID, frame->dvaddr_buffer[0]); + set_bit(FLITE_A_SLOT_VALID, &flite->state); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + buffer_ready = true; + } + + if (framemgr->frame_req_cnt >= 1) { + fimc_is_frame_request_head(framemgr, &frame); + flite_s_use_buffer(flite, FLITE_B_SLOT_VALID); + flite_s_buffer_addr(flite, FLITE_B_SLOT_VALID, frame->dvaddr_buffer[0]); + set_bit(FLITE_B_SLOT_VALID, &flite->state); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + buffer_ready = true; + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); + + flite_hw_set_output_dma(flite->base_reg, buffer_ready, image->format.pixelformat); + + /* 3. otf setting */ + if (device->ischain) + set_bit(FLITE_JOIN_ISCHAIN, &flite->state); + else + clear_bit(FLITE_JOIN_ISCHAIN, &flite->state); + + if (device->ischain && IS_ISCHAIN_OTF(device->ischain)) { + tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str0, (unsigned long)subdev); + tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end, (unsigned long)subdev); + + if (device->ischain->group_3aa.id == GROUP_ID_3A0) { + flite->group = GROUP_ID_3A0; + } else if (device->ischain->group_3aa.id == GROUP_ID_3A1) { + flite->group = GROUP_ID_3A1; + } else { + merr("invalid otf path(%d)", device, device->ischain->group_3aa.id); + ret = -EINVAL; + goto p_err; + } + + mdbgd_back("Enabling OTF path. target 3aa(%d)\n", flite, flite->group); + if (flite->instance == FLITE_ID_A) { + if (flite->group == GROUP_ID_3A0) + otf_setting = FLITE_REG_CIGENERAL_CAM_A; + else + otf_setting = FLITE_REG_CIGENERAL_3AA1_CAM_A; + } else if (flite->instance == FLITE_ID_B) { + if (flite->group == GROUP_ID_3A0) + otf_setting = FLITE_REG_CIGENERAL_CAM_B; + else + otf_setting = FLITE_REG_CIGENERAL_3AA1_CAM_B; + } else { + merr("invalid FLITE channel for OTF setting", flite); + ret = -EINVAL; + goto p_err; + } + + flite_hw_set_output_local(flite->base_reg, true); + set_bit(FLITE_OTF_WITH_3AA, &flite->state); + } else { + switch (flite->buf_done_mode) { + case FLITE_BUF_DONE_NORMAL: + tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str1, (unsigned long)subdev); + tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end, (unsigned long)subdev); + break; + case FLITE_BUF_DONE_EARLY: + flite->early_work_skip = false; + flite->early_work_called = false; + tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str1, (unsigned long)subdev); + tasklet_init(&flite->tasklet_flite_early_end, tasklet_flite_end, (unsigned long)subdev); + tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end_chk, (unsigned long)subdev); + break; + default: + tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str1, (unsigned long)subdev); + tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end, (unsigned long)subdev); + break; + } + + flite_hw_set_output_local(flite->base_reg, false); + clear_bit(FLITE_OTF_WITH_3AA, &flite->state); + } + + /* 4. register setting */ + start_fimc_lite(flite->base_reg, image, otf_setting, bns, flite->csi, flite->instance); + +p_err: + return ret; +} + +static int flite_stream_off(struct v4l2_subdev *subdev, + struct fimc_is_device_flite *flite, + bool nowait) +{ + int ret = 0; + unsigned long __iomem *base_reg; + unsigned long flags; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!flite); + BUG_ON(!flite->base_reg); + BUG_ON(!flite->framemgr); + + base_reg = flite->base_reg; + framemgr = flite->framemgr; + + /* for preventing invalid memory access */ + flite_hw_set_unuse_buffer(base_reg, FLITE_A_SLOT_VALID); + flite_hw_set_unuse_buffer(base_reg, FLITE_B_SLOT_VALID); + flite_hw_set_output_dma(base_reg, false, flite->image.format.pixelformat); + flite_hw_set_output_local(base_reg, false); + + stop_fimc_lite(base_reg); + if (!nowait) { + u32 timetowait; + + timetowait = wait_event_timeout(flite->wait_queue, + test_bit(FLITE_LAST_CAPTURE, &flite->state), + FIMC_IS_FLITE_STOP_TIMEOUT); + if (!timetowait) { + /* forcely stop */ + stop_fimc_lite(base_reg); + set_bit(FLITE_LAST_CAPTURE, &flite->state); + err("last capture timeout:%s", __func__); + msleep(200); + flite_hw_force_reset(base_reg); + ret = -ETIME; + } + } else { + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) + flush_delayed_work(&flite->early_work_wq); + /* + * DTP test can make iommu fault because senosr is streaming + * therefore it need force reset + */ + flite_hw_force_reset(base_reg); + } + + if (flite->buf_done_mode == FLITE_BUF_DONE_EARLY) + cancel_delayed_work_sync(&flite->early_work_wq); + + /* clr interrupt source */ + flite_hw_clr_interrupt_source(base_reg); + + framemgr_e_barrier_irqs(framemgr, FMGR_IDX_3, flags); + + fimc_is_frame_complete_head(framemgr, &frame); + while (frame) { + fimc_is_frame_trans_com_to_fre(framemgr, frame); + fimc_is_frame_complete_head(framemgr, &frame); + } + + fimc_is_frame_process_head(framemgr, &frame); + while (frame) { + fimc_is_frame_trans_pro_to_fre(framemgr, frame); + fimc_is_frame_process_head(framemgr, &frame); + } + + fimc_is_frame_request_head(framemgr, &frame); + while (frame) { + fimc_is_frame_trans_req_to_fre(framemgr, frame); + fimc_is_frame_request_head(framemgr, &frame); + } + + /* buffer done mode init */ + flite->buf_done_mode = FLITE_BUF_DONE_NORMAL; + flite->early_buf_done_mode = FLITE_BUF_EARLY_NOTHING; + + framemgr_x_barrier_irqr(framemgr, FMGR_IDX_3, flags); + + return ret; +} + +/* + * enable + * @X0 : disable + * @X1 : enable + * @1X : no waiting flag + * @0X : waiting flag + */ +static int flite_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + bool nowait; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + + nowait = (enable & FLITE_NOWAIT_MASK) >> FLITE_NOWAIT_SHIFT; + enable = enable & FLITE_ENABLE_MASK; + + flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (enable) { + ret = flite_stream_on(subdev, flite); + if (ret) { + err("flite_stream_on is fail(%d)", ret); + goto p_err; + } + } else { + ret = flite_stream_off(subdev, flite, nowait); + if (ret) { + err("flite_stream_off is fail(%d)", ret); + goto p_err; + } + } + +p_err: + mdbgd_back("%s(%d, %d)\n", flite, __func__, enable, ret); + return 0; +} + +static int flite_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + int ret = 0; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + BUG_ON(!fmt); + + flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + flite->image.window.offs_h = 0; + flite->image.window.offs_v = 0; + flite->image.window.width = fmt->width; + flite->image.window.height = fmt->height; + flite->image.window.o_width = fmt->width; + flite->image.window.o_height = fmt->height; + flite->image.format.pixelformat = fmt->code; + +p_err: + mdbgd_back("%s(%dx%d, %X)\n", flite, __func__, fmt->width, fmt->height, fmt->code); + return ret; +} + +static int flite_s_ctrl(struct v4l2_subdev *subdev, struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_device_flite *flite; + + BUG_ON(!subdev); + BUG_ON(!ctrl); + + flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev); + if (!flite) { + err("flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + switch (ctrl->id) { + case V4L2_CID_IS_S_BNS: + { + u32 width, height, ratio; + + width = flite->image.window.width; + height = flite->image.window.height; + ratio = ctrl->value; + + flite->image.window.otf_width + = rounddown((width * 1000 / ratio), 4); + flite->image.window.otf_height + = rounddown((height * 1000 / ratio), 2); + } + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = flite_init, + .s_ctrl = flite_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = flite_s_stream, + .s_mbus_fmt = flite_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +int fimc_is_flite_probe(struct fimc_is_device_sensor *device, + u32 instance) +{ + int ret = 0; + struct v4l2_subdev *subdev_flite; + struct fimc_is_device_flite *flite; + + BUG_ON(!device); + + subdev_flite = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_flite) { + merr("subdev_flite is NULL", device); + ret = -ENOMEM; + goto err_alloc_subdev_flite; + } + device->subdev_flite = subdev_flite; + + flite = kzalloc(sizeof(struct fimc_is_device_flite), GFP_KERNEL); + if (!flite) { + merr("flite is NULL", device); + ret = -ENOMEM; + goto err_alloc_flite; + } + + /* pointer to me from device sensor */ + flite->subdev = &device->subdev_flite; + flite->instance = instance; + init_waitqueue_head(&flite->wait_queue); + switch(instance) { + case FLITE_ID_A: + flite->base_reg = (unsigned long *)FIMCLITE0_REG_BASE; + break; + case FLITE_ID_B: + flite->base_reg = (unsigned long *)FIMCLITE1_REG_BASE; + break; + case FLITE_ID_C: + flite->base_reg = (unsigned long *)FIMCLITE2_REG_BASE; + break; + default: + err("instance is invalid(%d)", instance); + ret = -EINVAL; + goto err_invalid_instance; + } + + v4l2_subdev_init(subdev_flite, &subdev_ops); + v4l2_set_subdevdata(subdev_flite, (void *)flite); + v4l2_set_subdev_hostdata(subdev_flite, device); + snprintf(subdev_flite->name, V4L2_SUBDEV_NAME_SIZE, "flite-subdev.%d", instance); + ret = v4l2_device_register_subdev(&device->v4l2_dev, subdev_flite); + if (ret) { + merr("v4l2_device_register_subdev is fail(%d)", device, ret); + goto err_reg_v4l2_subdev; + } + + /* buffer done mode is normal (default) */ + flite->buf_done_mode = FLITE_BUF_DONE_NORMAL; + flite->early_buf_done_mode = FLITE_BUF_EARLY_NOTHING; + flite->chk_early_buf_done = NULL; +#ifdef SUPPORTED_EARLY_BUF_DONE + flite->chk_early_buf_done = chk_early_buf_done; + flite->early_workqueue = alloc_workqueue("fimc-is/early_workqueue/highpri", WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!flite->early_workqueue) { + warn("failed to alloc own workqueue, will be use global one"); + goto err_reg_v4l2_subdev; + } else { + INIT_DELAYED_WORK(&flite->early_work_wq, wq_func_flite_early_work); + } +#endif + info("[BAK:D:%d] %s(%d)\n", instance, __func__, ret); + return 0; + +err_reg_v4l2_subdev: +err_invalid_instance: + kfree(flite); + +err_alloc_flite: + kfree(subdev_flite); + device->subdev_flite = NULL; + +err_alloc_subdev_flite: + err("[BAK:D:%d] %s(%d)\n", instance, __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.h new file mode 100644 index 000000000000..825900f4e2c6 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-flite.h @@ -0,0 +1,121 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_FLITE_H +#define FIMC_IS_DEVICE_FLITE_H + +#include +#include "fimc-is-type.h" + +#define EXPECT_FRAME_START 0 +#define EXPECT_FRAME_END 1 + +#define FLITE_NOTIFY_FSTART 0 +#define FLITE_NOTIFY_FEND 1 + +#define FLITE_ENABLE_FLAG 1 +#define FLITE_ENABLE_MASK 0xFFFF +#define FLITE_ENABLE_SHIFT 0 + +#define FLITE_NOWAIT_FLAG 1 +#define FLITE_NOWAIT_MASK 0xFFFF0000 +#define FLITE_NOWAIT_SHIFT 16 + +#define FLITE_OVERFLOW_COUNT 10 + +#define FLITE_VVALID_TIME_BASE 32 /* ms */ + + +struct fimc_is_device_sensor; + +enum fimc_is_flite_state { + /* buffer state*/ + FLITE_A_SLOT_VALID = 0, + FLITE_B_SLOT_VALID, + /* finish state */ + FLITE_LAST_CAPTURE, + /* flite join ischain */ + FLITE_JOIN_ISCHAIN, + /* one the fly output */ + FLITE_OTF_WITH_3AA, +}; + +enum fimc_is_flite_buf_done_mode { + FLITE_BUF_DONE_NORMAL = 0, /* when end-irq */ + FLITE_BUF_DONE_EARLY = 1, /* when delayed work queue since start-irq */ +}; + +/* + * 10p means 10% early than end irq. We supposed that VVALID time is variable + * ex. 32 * 0.1 = 3ms, early interval is (33 - 3) = 29ms + * 32 * 0.2 = 6ms, (33 - 6) = 26ms + * 32 * 0.3 = 9ms, (33 - 9) = 23ms + * 32 * 0.4 = 12ms, (33 - 12) = 20ms + */ +enum fimc_is_flite_early_buf_done_mode { + FLITE_BUF_EARLY_NOTHING = 0, + FLITE_BUF_EARLY_10P = 1, /* 10%(29ms) 3ms */ + FLITE_BUF_EARLY_20P = 2, /* 20%(26ms) 6ms */ + FLITE_BUF_EARLY_30P = 3, /* 30%(23ms) 9ms */ + FLITE_BUF_EARLY_40P = 4, /* 40%(20ms) 12ms */ +}; + +struct fimc_is_device_flite { + u32 instance; + unsigned long __iomem *base_reg; + unsigned long state; + wait_queue_head_t wait_queue; + + struct fimc_is_image image; + struct fimc_is_framemgr *framemgr; + + u32 overflow_cnt; + + u32 csi; /* which csi channel is connceted */ + u32 group; /* which 3aa gorup is connected when otf is enable */ + + u32 sw_checker; + u32 sw_trigger; + atomic_t bcount; + atomic_t fcount; + u32 tasklet_param_str; + struct tasklet_struct tasklet_flite_str; + u32 tasklet_param_end; + struct tasklet_struct tasklet_flite_end; + + /* for early buffer done */ + u32 buf_done_mode; + u32 early_buf_done_mode; + u32 buf_done_wait_time; + bool early_work_skip; + bool early_work_called; + struct tasklet_struct tasklet_flite_early_end; + struct workqueue_struct *early_workqueue; + struct delayed_work early_work_wq; + void (*chk_early_buf_done)(struct fimc_is_device_flite *flite, u32 framerate, u32 position); + + /* pointer address from device sensor */ + struct v4l2_subdev **subdev; +}; + +int fimc_is_flite_probe(struct fimc_is_device_sensor *device, + u32 instance); +int fimc_is_flite_open(struct v4l2_subdev *subdev, + struct fimc_is_framemgr *framemgr); +int fimc_is_flite_close(struct v4l2_subdev *subdev); + +extern u32 __iomem *notify_fcount_sen0; +extern u32 __iomem *notify_fcount_sen1; +extern u32 __iomem *notify_fcount_sen2; +extern u32 __iomem *last_fcount0; +extern u32 __iomem *last_fcount1; + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.c new file mode 100644 index 000000000000..2483bc671e1c --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.c @@ -0,0 +1,7657 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_SOC_EXYNOS3470) +#include +#endif + +#include "fimc-is-time.h" +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-groupmgr.h" +#include "fimc-is-device-ischain.h" +#include "fimc-is-companion.h" +#include "fimc-is-clk-gate.h" +#include "fimc-is-dvfs.h" +#include "fimc-is-device-companion.h" +#include +#include + +#ifdef CONFIG_USE_VENDER_FEATURE +#include "fimc-is-sec-define.h" +#else +#define SDCARD_FW +#define FIMC_IS_SETFILE_SDCARD_PATH "/data/" +#define FIMC_IS_FW "fimc_is_fw2.bin" +#define FIMC_IS_FW_SDCARD "/data/fimc_is_fw2.bin" + +#define FIMC_IS_FW_BASE_MASK ((1 << 26) - 1) +#define FIMC_IS_VERSION_SIZE 42 +#define FIMC_IS_SETFILE_VER_OFFSET 0x40 +#define FIMC_IS_SETFILE_VER_SIZE 52 + +#define FIMC_IS_CAL_SDCARD "/data/cal_data.bin" +#define FIMC_IS_CAL_SDCARD_FRONT "/data/cal_data_front.bin" +#define FIMC_IS_MAX_FW_SIZE (2048 * 1024) +#define FIMC_IS_CAL_START_ADDR (0x013D0000) +#define FIMC_IS_CAL_START_ADDR_FRONT (0x013E0000) +#define FIMC_IS_CAL_RETRY_CNT (2) +#define FIMC_IS_FW_RETRY_CNT (2) +#endif + +/* Default setting values */ +#define DEFAULT_PREVIEW_STILL_WIDTH (1280) /* sensor margin : 16 */ +#define DEFAULT_PREVIEW_STILL_HEIGHT (720) /* sensor margin : 12 */ +#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) + +/* sysfs variable for debug */ +extern struct fimc_is_sysfs_debug sysfs_debug; + +#ifdef FW_DEBUG +#define DEBUG_FS_ROOT_NAME "fimc-is" +#define DEBUG_FS_FILE_NAME "isfw-msg" +static struct dentry *debugfs_root; +static struct dentry *debugfs_file; + +#define SETFILE_SIZE 0x6000 +#define READ_SIZE 0x100 + +static char fw_name[100]; +//static char setf_name[100]; + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +#define FIMC_IS_MAX_CAL_SIZE (8 * 1024) +#else +#define FIMC_IS_MAX_CAL_SIZE (64 * 1024) +#endif +#define FIMC_IS_MAX_CAL_SIZE_FRONT (8 * 1024) + +#define FIMC_IS_DEFAULT_CAL_SIZE (20 * 1024) +extern bool crc32_check; +extern bool crc32_header_check; +extern bool crc32_check_front; +extern bool crc32_header_check_front; + +static int cam_id; +#ifdef CONFIG_USE_VENDER_FEATURE +extern bool is_dumped_fw_loading_needed; +extern char fw_core_version; +#else +bool is_dumped_fw_loading_needed = false; +#endif + +static int isfw_debug_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static int isfw_debug_read(struct file *file, char __user *user_buf, + size_t buf_len, loff_t *ppos) +{ + char *debug; + size_t debug_cnt, backup_cnt; + size_t count1, count2; + size_t buf_count = 0; + struct fimc_is_device_ischain *device = + (struct fimc_is_device_ischain *)file->private_data; + struct fimc_is_ishcain_mem *imemory; + struct fimc_is_core *core; + + BUG_ON(!device); + + count1 = 0; + count2 = 0; + debug_cnt = 0; + imemory = &device->imemory; + core = (struct fimc_is_core *)device->interface->core; + + if (atomic_read(&core->video_isp.refcount) <= 0) { + err("isp video node is not open"); + goto exit; + } + + vb2_ion_sync_for_device(imemory->fw_cookie, DEBUG_OFFSET, + DEBUG_CNT, DMA_FROM_DEVICE); + + debug_cnt = *((int *)(imemory->kvaddr + DEBUGCTL_OFFSET)) - DEBUG_OFFSET; + backup_cnt = core->debug_cnt; + + if (core->debug_cnt > debug_cnt) { + count1 = DEBUG_CNT - core->debug_cnt; + count2 = debug_cnt; + } else { + count1 = debug_cnt - core->debug_cnt; + count2 = 0; + } + + buf_count = buf_len; + + if (buf_count && count1) { + debug = (char *)(imemory->kvaddr + DEBUG_OFFSET + core->debug_cnt); + + if (count1 > buf_count) + count1 = buf_count; + + buf_count -= count1; + + memcpy(user_buf, debug, count1); + core->debug_cnt += count1; + } + + if (buf_count && count2) { + debug = (char *)(imemory->kvaddr + DEBUG_OFFSET); + + if (count2 > buf_count) + count2 = buf_count; + + buf_count -= count2; + + memcpy(user_buf, debug, count2); + core->debug_cnt = count2; + } + + info("FW_READ : Origin(%d), New(%d) - Length(%d)\n", + backup_cnt, + core->debug_cnt, + (buf_len - buf_count)); + +exit: + return buf_len - buf_count; +} + +static const struct file_operations debug_fops = { + .open = isfw_debug_open, + .read = isfw_debug_read, + .llseek = default_llseek +}; + +#endif + +static const struct sensor_param init_sensor_param = { + .config = { +#ifdef FIXED_FPS_DEBUG + .framerate = FIXED_FPS_VALUE, + .min_target_fps = FIXED_FPS_VALUE, + .max_target_fps = FIXED_FPS_VALUE, +#else + .framerate = 30, + .min_target_fps = 15, + .max_target_fps = 30, +#endif + }, +}; + +static const struct taa_param init_taa_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_DISABLE, + .err = CONTROL_ERROR_NO, + }, + .otf_input = { + .cmd = OTF_INPUT_COMMAND_ENABLE, + .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, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .frametime_min = 0, + .frametime_max = 33333, + .sensor_binning_ratio_x = 1000, + .sensor_binning_ratio_y = 1000, + .err = OTF_INPUT_ERROR_NO, + }, + .vdma1_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, + .width = 0, + .height = 0, + .format = 0, + .bitwidth = 0, + .plane = 0, + .order = 0, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .buffer_number = 0, + .buffer_address = 0, + .sensor_binning_ratio_x = 1000, + .sensor_binning_ratio_y = 1000, + .err = 0, + }, + .ddma_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, + }, + .otf_output = { + .cmd = OTF_OUTPUT_COMMAND_DISABLE, + }, + .vdma4_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, + .dma_out_mask = 0, + .width = DEFAULT_CAPTURE_STILL_WIDTH, + .height = DEFAULT_CAPTURE_STILL_HEIGHT, + .format = DMA_INPUT_FORMAT_YUV444, + .bitwidth = DMA_INPUT_BIT_WIDTH_8BIT, + .plane = DMA_INPUT_PLANE_1, + .order = DMA_INPUT_ORDER_YCbCr, + .buffer_number = 0, + .buffer_address = 0, + .err = DMA_OUTPUT_ERROR_NO, + }, + .vdma2_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 = DMA_OUTPUT_PLANE_1, + .order = DMA_OUTPUT_ORDER_GB_BG, + .buffer_number = 0, + .buffer_address = 0, + .dma_out_mask = 0xFFFFFFFF, + .err = DMA_OUTPUT_ERROR_NO, + }, + .ddma_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, + }, +}; + +static const struct isp_param init_isp_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_DISABLE, + .err = CONTROL_ERROR_NO, + }, + .otf_input = { + .cmd = OTF_INPUT_COMMAND_DISABLE, + }, + .vdma1_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, + .width = 0, + .height = 0, + .format = 0, + .bitwidth = 0, + .plane = 0, + .order = 0, + .buffer_number = 0, + .buffer_address = 0, + .sensor_binning_ratio_x = 1000, + .sensor_binning_ratio_y = 1000, + .err = 0, + }, + .vdma3_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, + .vdma4_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, + }, + .vdma5_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, + }, +}; + +static const struct drc_param init_drc_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .err = OTF_INPUT_ERROR_NO, + }, + .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 = DMA_INPUT_PLANE_1, + .order = DMA_INPUT_ORDER_YCbCr, + .buffer_number = 0, + .buffer_address = 0, + .err = 0, + }, + .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_12BIT, + .order = OTF_OUTPUT_ORDER_BAYER_GR_BG, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, +}; + +static const struct scalerc_param init_scalerc_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .err = OTF_INPUT_ERROR_NO, + }, + .effect = { + .cmd = 0, + .arbitrary_cb = 128, /* default value : 128 */ + .arbitrary_cr = 128, /* default value : 128 */ + .yuv_range = SCALER_OUTPUT_YUV_RANGE_FULL, + .err = 0, + }, + .input_crop = { + .cmd = OTF_INPUT_COMMAND_DISABLE, + .pos_x = 0, + .pos_y = 0, + .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, + .err = 0, + }, + .output_crop = { + .cmd = SCALER_CROP_COMMAND_DISABLE, + .pos_x = 0, + .pos_y = 0, + .crop_width = DEFAULT_CAPTURE_STILL_WIDTH, + .crop_height = DEFAULT_CAPTURE_STILL_HEIGHT, + .format = DMA_OUTPUT_FORMAT_YUV422, + .err = 0, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, + .dma_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, + .width = DEFAULT_CAPTURE_STILL_WIDTH, + .height = DEFAULT_CAPTURE_STILL_HEIGHT, + .format = DMA_OUTPUT_FORMAT_YUV422, + .bitwidth = DMA_OUTPUT_BIT_WIDTH_8BIT, + .plane = DMA_OUTPUT_PLANE_1, + .order = DMA_OUTPUT_ORDER_CrYCbY, + .buffer_number = 0, + .buffer_address = 0, + .dma_out_mask = 0xffff, + .reserved[0] = SCALER_DMA_OUT_UNSCALED, + .err = DMA_OUTPUT_ERROR_NO, + }, +}; + +static const struct odc_param init_odc_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .err = OTF_INPUT_ERROR_NO, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, +}; + +static const struct dis_param init_dis_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .err = OTF_INPUT_ERROR_NO, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, +}; +static const struct tdnr_param init_tdnr_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .err = OTF_INPUT_ERROR_NO, + }, + .frame = { + .cmd = 0, + .err = 0, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, + .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 = DMA_OUTPUT_PLANE_2, + .order = DMA_OUTPUT_ORDER_CbCr, + .buffer_number = 0, + .buffer_address = 0, + .dma_out_mask = 0xffff, + .err = DMA_OUTPUT_ERROR_NO, + }, +}; + +static const struct scalerp_param init_scalerp_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_ENABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .bayer_crop_offset_x = 0, + .bayer_crop_offset_y = 0, + .bayer_crop_width = 0, + .bayer_crop_height = 0, + .err = OTF_INPUT_ERROR_NO, + }, + .effect = { + .cmd = 0, + .arbitrary_cb = 128, /* default value : 128 */ + .arbitrary_cr = 128, /* default value : 128 */ + .yuv_range = SCALER_OUTPUT_YUV_RANGE_FULL, + .err = 0, + }, + .input_crop = { + .cmd = OTF_INPUT_COMMAND_ENABLE, + .pos_x = 0, + .pos_y = 0, + .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, + .err = 0, + }, + .output_crop = { + .cmd = SCALER_CROP_COMMAND_DISABLE, + .pos_x = 0, + .pos_y = 0, + .crop_width = DEFAULT_PREVIEW_STILL_WIDTH, + .crop_height = DEFAULT_PREVIEW_STILL_HEIGHT, + .format = OTF_OUTPUT_FORMAT_YUV420, + .err = 0, + }, + .rotation = { + .cmd = 0, + .err = 0, + }, + .flip = { + .cmd = 0, + .err = 0, + }, + .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, + .crop_offset_x = 0, + .crop_offset_y = 0, + .err = OTF_OUTPUT_ERROR_NO, + }, + .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 = DMA_OUTPUT_PLANE_3, + .order = DMA_OUTPUT_ORDER_NO, + .buffer_number = 0, + .buffer_address = 0, + .dma_out_mask = 0xffff, + .err = DMA_OUTPUT_ERROR_NO, + }, +}; + +static const struct fd_param init_fd_param = { + .control = { + .cmd = CONTROL_COMMAND_STOP, + .bypass = CONTROL_BYPASS_DISABLE, + .err = CONTROL_ERROR_NO, + }, + .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, + .err = OTF_INPUT_ERROR_NO, + }, + .dma_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, + .width = 0, .height = 0, + .format = 0, .bitwidth = 0, .plane = 0, + .order = 0, .buffer_number = 0, .buffer_address = 0, + .err = 0, + }, + .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, + .orientation_value = 0, + .err = ERROR_FD_NO, + }, +}; + +#ifndef RESERVED_MEM +static int fimc_is_ishcain_deinitmem(struct fimc_is_device_ischain *device) +{ + int ret = 0; + + vb2_ion_private_free(device->imemory.fw_cookie); + + return ret; +} +#endif + +static void fimc_is_ischain_cache_flush(struct fimc_is_device_ischain *this, + u32 offset, u32 size) +{ + vb2_ion_sync_for_device(this->imemory.fw_cookie, + offset, + size, + DMA_TO_DEVICE); +} + +static void fimc_is_ischain_region_invalid(struct fimc_is_device_ischain *device) +{ + vb2_ion_sync_for_device( + device->imemory.fw_cookie, + device->imemory.offset_region, + sizeof(struct is_region), + DMA_FROM_DEVICE); +} + +static void fimc_is_ischain_region_flush(struct fimc_is_device_ischain *device) +{ + vb2_ion_sync_for_device( + device->imemory.fw_cookie, + device->imemory.offset_region, + sizeof(struct is_region), + DMA_TO_DEVICE); +} + +void fimc_is_ischain_meta_flush(struct fimc_is_frame *frame) +{ +#ifdef ENABLE_CACHE + vb2_ion_sync_for_device( + (void *)frame->cookie_shot, + 0, + frame->shot_size, + DMA_TO_DEVICE); +#endif +} + +void fimc_is_ischain_meta_invalid(struct fimc_is_frame *frame) +{ +#ifdef ENABLE_CACHE + vb2_ion_sync_for_device( + (void *)frame->cookie_shot, + 0, + frame->shot_size, + DMA_FROM_DEVICE); +#endif +} + +static void fimc_is_ischain_version(struct fimc_is_device_ischain *this, char *name, const char *load_bin, u32 size) +{ + struct fimc_is_from_info *pinfo = NULL; + char version_str[60]; + + if (!strcmp(fw_name, name)) { + memcpy(version_str, &load_bin[size - FIMC_IS_VERSION_SIZE], + FIMC_IS_VERSION_SIZE); + version_str[FIMC_IS_VERSION_SIZE] = '\0'; + + pinfo = &this->pinfo; + memcpy(pinfo->header_ver, &version_str[32], 11); + pinfo->header_ver[11] = '\0'; + } else { + memcpy(version_str, &load_bin[size - FIMC_IS_SETFILE_VER_OFFSET], + FIMC_IS_SETFILE_VER_SIZE); + version_str[FIMC_IS_SETFILE_VER_SIZE] = '\0'; + + pinfo = &this->pinfo; + memcpy(pinfo->setfile_ver, &version_str[17], 4); + pinfo->setfile_ver[4] = '\0'; + } + + info("%s version : %s\n", name, version_str); +} + +void fimc_is_ischain_savefirm(struct fimc_is_device_ischain *this) +{ +#ifdef DEBUG_DUMP_FIRMWARE + loff_t pos; + + write_data_to_file("/data/firmware.bin", (char *)this->imemory.kvaddr, + (size_t)FIMC_IS_A5_MEM_SIZE, &pos); +#endif +} + +static int fimc_is_ischain_loadfirm(struct fimc_is_device_ischain *device) +{ + int ret = 0; + int location = 0; + const struct firmware *fw_blob = NULL; + u8 *buf = NULL; +#ifdef USE_ION_ALLOC + struct ion_handle *handle = NULL; + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(device->pdev); +#endif +#ifdef SDCARD_FW + struct file *fp = NULL; + mm_segment_t old_fs; + long fsize, nread; + int fw_requested = 1; + char fw_path[100]; + int retry_count = 0; + + mdbgd_ischain("%s\n", device, __func__); +#ifdef USE_ION_ALLOC + BUG_ON(!core); + BUG_ON(!core->fimc_ion_client); +#endif + old_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(FIMC_IS_FW_SDCARD, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { +#ifdef CONFIG_USE_VENDER_FEATURE + if (is_dumped_fw_loading_needed && + device->pdev->id == SENSOR_POSITION_REAR) { + snprintf(fw_path, sizeof(fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, FIMC_IS_FW); + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { + fp = NULL; + ret = -EIO; + set_fs(old_fs); + goto out; + } + } else +#endif + goto request_fw; + } + + location = 1; + fw_requested = 0; + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + is_dumped_fw_loading_needed ? fw_path : FIMC_IS_FW_SDCARD, fsize); +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)fsize, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("fimc_is_comp_load_binary:failed to ioc_alloc\n"); + ret = -ENOMEM; + goto out; + } + + buf = (u8 *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("fimc_is_comp_load_binary:fail to ion_map_kernle\n"); + ret = -ENOMEM; + goto out; + } +#else + buf = vmalloc(fsize); + if (!buf) { + dev_err(&device->pdev->dev, + "failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } +#endif + nread = vfs_read(fp, (char __user *)buf, fsize, &fp->f_pos); + if (nread != fsize) { + dev_err(&device->pdev->dev, + "failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto out; + } + + memcpy((void *)device->imemory.kvaddr, (void *)buf, fsize); + fimc_is_ischain_cache_flush(device, 0, fsize + 1); + fimc_is_ischain_version(device, fw_name, buf, fsize); + +request_fw: + if (fw_requested) { + set_fs(old_fs); +#endif + retry_count = 3; + ret = request_firmware(&fw_blob, fw_name, &device->pdev->dev); + while (--retry_count && ret == -EAGAIN) { + err("request_firmware retry(count:%d)", retry_count); + ret = request_firmware(&fw_blob, fw_name, &device->pdev->dev); + } + + if (ret) { + err("request_firmware is fail(%d)", ret); + ret = -EINVAL; + goto out; + } + + if (!fw_blob) { + merr("fw_blob is NULL", device); + ret = -EINVAL; + goto out; + } + + if (!fw_blob->data) { + merr("fw_blob->data is NULL", device); + ret = -EINVAL; + goto out; + } + + memcpy((void *)device->imemory.kvaddr, fw_blob->data, + fw_blob->size); + fimc_is_ischain_cache_flush(device, 0, fw_blob->size + 1); + fimc_is_ischain_version(device, fw_name, fw_blob->data, + fw_blob->size); +#ifdef SDCARD_FW + } +#endif + +out: +#ifdef SDCARD_FW +#ifdef USE_ION_ALLOC + if (!IS_ERR_OR_NULL(buf)) { + ion_unmap_kernel(core->fimc_ion_client, handle); + } + + if (!IS_ERR_OR_NULL(handle)) { + ion_free(core->fimc_ion_client, handle); + } +#endif + + if (!fw_requested) { +#ifndef USE_ION_ALLOC + if (buf) { + vfree(buf); + } +#endif + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, current->files); + } + set_fs(old_fs); + } else +#endif + { + if (!IS_ERR_OR_NULL(fw_blob)) { + release_firmware(fw_blob); + } + } + if (ret) + err("firmware loading is fail"); + else + info("Camera: the %s FW were applied successfully.\n", + ((cam_id == CAMERA_SINGLE_REAR) && + is_dumped_fw_loading_needed) ? "dumped" : "default"); + + return ret; +} + +static int fimc_is_ischain_buf_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node, + struct fimc_is_queue *queue, + struct fimc_is_framemgr *framemgr, + u32 width, u32 height, + u32 target_addr[], + enum fimc_is_frame_output frame_output) +{ + int ret = 0; + struct fimc_is_frame *frame; + u32 frame_size; + unsigned long flags; + + framemgr_e_barrier_irqs(framemgr, 0, flags); + + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + if (!frame->stream) { + framemgr_x_barrier_irqr(framemgr, 0, flags); + merr("frame->stream is NULL", device); + ret = -EINVAL; + goto p_err; + } + + frame_size = width * height; + + switch (queue->framecfg.format.pixelformat) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + target_addr[0] = frame->dvaddr_buffer[0]; + target_addr[1] = target_addr[0] + frame_size; + break; + case V4L2_PIX_FMT_YVU420M: + target_addr[0] = frame->dvaddr_buffer[0]; + target_addr[1] = frame->dvaddr_buffer[2]; + target_addr[2] = frame->dvaddr_buffer[1]; + break; + default: + target_addr[0] = frame->dvaddr_buffer[0]; + target_addr[1] = frame->dvaddr_buffer[1]; + target_addr[2] = frame->dvaddr_buffer[2]; + break; + } + + frame->stream->findex = ldr_frame->index; + set_bit(frame_output, &ldr_frame->out_flag); + set_bit(REQ_FRAME, &frame->req_flag); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + } else { + mwarn("%d frame is drop (node-id:%d)", device, ldr_frame->fcount, node->vid); + target_addr[0] = 0; + target_addr[1] = 0; + target_addr[2] = 0; + node->request = 0; + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); +p_err: + return ret; +} + +static int fimc_is_ischain_loadsetf(struct fimc_is_device_ischain *device, + u32 load_addr, char *setfile_name) +{ + int ret = 0; + int location = 0; + void *address; + const struct firmware *fw_blob = NULL; + u8 *buf = NULL; +#ifdef USE_ION_ALLOC + struct ion_handle *handle = NULL; + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(device->pdev); +#endif + +#ifdef SDCARD_FW + struct file *fp = NULL; + mm_segment_t old_fs; + long fsize, nread; + int fw_requested = 1; + char setfile_path[256]; + u32 retry; + + mdbgd_ischain("%s\n", device, __func__); + +#ifdef USE_ION_ALLOC + BUG_ON(!core); + BUG_ON(!core->fimc_ion_client); +#endif + + old_fs = get_fs(); + set_fs(KERNEL_DS); + memset(setfile_path, 0x00, sizeof(setfile_path)); + snprintf(setfile_path, sizeof(setfile_path), "%s%s", + FIMC_IS_SETFILE_SDCARD_PATH, setfile_name); + fp = filp_open(setfile_path, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { +#ifdef CONFIG_USE_VENDER_FEATURE + if (is_dumped_fw_loading_needed && + device->pdev->id == SENSOR_POSITION_REAR) { + memset(setfile_path, 0x00, sizeof(setfile_path)); + snprintf(setfile_path, sizeof(setfile_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, setfile_name); + fp = filp_open(setfile_path, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { + ret = -EIO; + fp = NULL; + set_fs(old_fs); + goto out; + } + } else +#endif + goto request_fw; + } + + location = 1; + fw_requested = 0; + fsize = fp->f_path.dentry->d_inode->i_size; + info("start, file path %s, size %ld Bytes\n", + setfile_path, fsize); +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)fsize, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("fimc_is_comp_load_binary:failed to ioc_alloc\n"); + ret = -ENOMEM; + goto out; + } + + buf = (u8 *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("fimc_is_comp_load_binary:fail to ion_map_kernle\n"); + ret = -ENOMEM; + goto out; + } +#else + buf = vmalloc(fsize); + if (!buf) { + dev_err(&device->pdev->dev, + "failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } +#endif + nread = vfs_read(fp, (char __user *)buf, fsize, &fp->f_pos); + if (nread != fsize) { + dev_err(&device->pdev->dev, + "failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto out; + } + + address = (void *)(device->imemory.kvaddr + load_addr); + memcpy((void *)address, (void *)buf, fsize); + fimc_is_ischain_cache_flush(device, load_addr, fsize + 1); + fimc_is_ischain_version(device, setfile_name, buf, fsize); + +request_fw: + if (fw_requested) { + set_fs(old_fs); +#endif + + retry = 4; + ret = request_firmware((const struct firmware **)&fw_blob, + setfile_name, &device->pdev->dev); + while (--retry && ret) { + mwarn("request_firmware is fail(%d)", device, ret); + ret = request_firmware((const struct firmware **)&fw_blob, + setfile_name, &device->pdev->dev); + } + + if (!retry) { + merr("request_firmware is fail(%d)", device, ret); + ret = -EINVAL; + goto out; + } + + if (!fw_blob) { + merr("fw_blob is NULL", device); + ret = -EINVAL; + goto out; + } + + if (!fw_blob->data) { + merr("fw_blob->data is NULL", device); + ret = -EINVAL; + goto out; + } + + address = (void *)(device->imemory.kvaddr + load_addr); + memcpy(address, fw_blob->data, fw_blob->size); + fimc_is_ischain_cache_flush(device, load_addr, fw_blob->size + 1); + fimc_is_ischain_version(device, setfile_name, fw_blob->data, + (u32)fw_blob->size); + +#ifdef SDCARD_FW + } +#endif + +out: +#ifdef SDCARD_FW +#ifdef USE_ION_ALLOC + if (!IS_ERR_OR_NULL(buf)) { + ion_unmap_kernel(core->fimc_ion_client, handle); + } + + if (!IS_ERR_OR_NULL(handle)) { + ion_free(core->fimc_ion_client, handle); + } +#endif + + if (!fw_requested) { +#ifndef USE_ION_ALLOC + if (buf) { + vfree(buf); + } +#endif + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, current->files); + } + set_fs(old_fs); + } else +#endif + { + if (!IS_ERR_OR_NULL(fw_blob)) { + release_firmware(fw_blob); + } + } + + if (ret) + err("setfile loading is fail"); + else + info("Camera: the %s Setfile were applied successfully.\n", + ((cam_id == CAMERA_SINGLE_REAR) && + is_dumped_fw_loading_needed) ? "dumped" : "default"); + + return ret; +} + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +static int fimc_is_ischain_loadcalb_eeprom(struct fimc_is_device_ischain *device, + struct fimc_is_module_enum *active_sensor, int id) + { + int ret = 0; +#ifdef CONFIG_USE_VENDER_FEATURE + char *cal_ptr; + char *cal_buf = NULL; + u32 start_addr = 0; + int cal_size = 0; + struct fimc_is_from_info *finfo; + + mdbgd_ischain("%s\n", device, __func__); + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + start_addr = FIMC_IS_CAL_START_ADDR_FRONT; + cal_size = FIMC_IS_MAX_CAL_SIZE_FRONT; + fimc_is_sec_get_sysfs_finfo_front(&finfo); + fimc_is_sec_get_front_cal_buf(&cal_buf); + } else +#endif + { + start_addr = FIMC_IS_CAL_START_ADDR; + cal_size = FIMC_IS_MAX_CAL_SIZE; + fimc_is_sec_get_sysfs_finfo(&finfo); + fimc_is_sec_get_cal_buf(&cal_buf); + } + + cal_ptr = (char *)(device->imemory.kvaddr + start_addr); + + info("CAL DATA : MAP ver : %c%c%c%c\n", cal_buf[0x30], cal_buf[0x31], + cal_buf[0x32], cal_buf[0x33]); + + /* CRC check */ + if (id == SENSOR_POSITION_FRONT) { + if (crc32_check_front == true) { + memcpy((void *)(cal_ptr) ,(void *)cal_buf, cal_size); + info("Front Camera : the dumped Cal. data was applied successfully.\n"); + } else { + if (crc32_header_check_front == true) { + pr_err("Front Camera : CRC32 error but only header section is no problem.\n"); + memset((void *)(cal_ptr + 0x1000), 0xFF, cal_size - 0x1000); + } else { + pr_err("Front Camera : CRC32 error for all section.\n"); + memset((void *)(cal_ptr), 0xFF, cal_size); + ret = -EIO; + } + } + } else { + if (crc32_check == true) { + memcpy((void *)(cal_ptr) ,(void *)cal_buf, cal_size); + info("Rear Camera : the dumped Cal. data was applied successfully.\n"); + } else { + if (crc32_header_check == true) { + pr_err("Rear Camera : CRC32 error but only header section is no problem.\n"); + memset((void *)(cal_ptr + 0x1000), 0xFF, cal_size - 0x1000); + } else { + pr_err("Rear Camera : CRC32 error for all section.\n"); + memset((void *)(cal_ptr), 0xFF, cal_size); + ret = -EIO; + } + } + } + + fimc_is_ischain_cache_flush(device, start_addr, cal_size); + if (ret) + mwarn("calibration loading is fail", device); + else + mwarn("calibration loading is success", device); + +#endif + return ret; + } +#endif + +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +static int fimc_is_ischain_loadcalb(struct fimc_is_device_ischain *device, + struct fimc_is_module_enum *active_sensor) +{ + int ret = 0; +#ifdef CONFIG_USE_VENDER_FEATURE + char *cal_ptr; + struct fimc_is_from_info *sysfs_finfo; + char *cal_buf; + +#ifdef CONFIG_COMPANION_USE + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(device->pdev); +#endif + mdbgd_ischain("%s\n", device, __func__); + + cal_ptr = (char *)(device->imemory.kvaddr + FIMC_IS_CAL_START_ADDR); + + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + fimc_is_sec_get_cal_buf(&cal_buf); + + info("CAL DATA : MAP ver : %c%c%c%c\n", cal_buf[0x60], cal_buf[0x61], + cal_buf[0x62], cal_buf[0x63]); + + /* CRC check */ + if (crc32_check == true) { +#ifdef CONFIG_COMPANION_USE + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + memcpy((void *)(cal_ptr) ,(void *)cal_buf, FIMC_IS_MAX_CAL_SIZE); + info("Camera : the dumped Cal. data was applied successfully.\n"); + } else { + info("Camera : Did not load dumped Cal. Sensor version is lower than V004.\n"); + } +#else + memcpy((void *)(cal_ptr) ,(void *)cal_buf, FIMC_IS_MAX_CAL_SIZE); + info("Camera : the dumped Cal. data was applied successfully.\n"); +#endif + } else { + if (crc32_header_check == true) { + pr_err("Camera : CRC32 error but only header section is no problem.\n"); + memset((void *)(cal_ptr + 0x1000), 0xFF, FIMC_IS_MAX_CAL_SIZE - 0x1000); + } else { + pr_err("Camera : CRC32 error for all section.\n"); + memset((void *)(cal_ptr), 0xFF, FIMC_IS_MAX_CAL_SIZE); + ret = -EIO; + } + } + + fimc_is_ischain_cache_flush(device, FIMC_IS_CAL_START_ADDR, + FIMC_IS_MAX_CAL_SIZE); + if (ret) + mwarn("calibration loading is fail", device); + else + mwarn("calibration loading is success", device); +#endif + return ret; +} +#endif +static void fimc_is_ischain_forcedown(struct fimc_is_device_ischain *this, + bool on) +{ + if (on) { + printk(KERN_INFO "Set low poweroff mode\n"); + __raw_writel(0x0, PMUREG_ISP_ARM_OPTION); + __raw_writel(0x1CF82000, PMUREG_ISP_LOW_POWER_OFF); + this->force_down = true; + } else { + printk(KERN_INFO "Clear low poweroff mode\n"); + __raw_writel(0xFFFFFFFF, PMUREG_ISP_ARM_OPTION); + __raw_writel(0x8, PMUREG_ISP_LOW_POWER_OFF); + this->force_down = false; + } +} + +#if !defined(CONFIG_SOC_EXYNOS4415) +void tdnr_s3d_pixel_async_sw_reset(struct fimc_is_device_ischain *this) +{ + u32 cfg = readl(SYSREG_GSCBLK_CFG1); + /* S3D pixel async sw reset */ + cfg &= ~(1 << 25); + writel(cfg, SYSREG_GSCBLK_CFG1); + + cfg = readl(SYSREG_ISPBLK_CFG); + /* 3DNR pixel async sw reset */ + cfg &= ~(1 << 5); + writel(cfg, SYSREG_ISPBLK_CFG); +} +#endif + +static void fimc_is_a5_power(struct device *dev, int power_flags) +{ + u32 timeout; + + /* configuration */ + __raw_writel(power_flags, PMUREG_ISP_ARM_CONFIGURATION); + + /* option */ + if (power_flags) { +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + /* A5 enable[15] */ + writel((1 << 15), PMUREG_ISP_ARM_OPTION); +#else + /* STANDBY WFI[16] & A5 enable[15] */ + writel((1 << 16 | 1 << 15), PMUREG_ISP_ARM_OPTION); +#endif + } + + /* status */ + timeout = 1000; + while ((__raw_readl(PMUREG_ISP_ARM_STATUS) & 0x1) != power_flags) { + if (timeout == 0) + err("%s can't control power(%d), timeout\n", __func__, power_flags); + timeout--; + udelay(1); + } +} + +int fimc_is_ischain_power(struct fimc_is_device_ischain *device, int on) +{ +#ifdef CONFIG_ARM_TRUSTZONE + int i; +#endif + int ret = 0; + u32 debug; +#if defined(CONFIG_PM_RUNTIME) + int rpm_ret; +#endif + u32 val; + char setf_name[100]; + + struct device *dev = &device->pdev->dev; + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(device->pdev); + struct fimc_is_from_info *sysfs_finfo; + + if (on) { + /* 1. force poweroff setting */ + if (device->force_down) + fimc_is_ischain_forcedown(device, false); + + /* 2. FIMC-IS local power enable */ +#if defined(CONFIG_PM_RUNTIME) + mdbgd_ischain("pm_runtime_suspended = %d\n", device, pm_runtime_suspended(dev)); + rpm_ret = pm_runtime_get_sync(dev); + if (rpm_ret < 0) + err("pm_runtime_get_sync() return error: %d", rpm_ret); +#else + fimc_is_runtime_resume(dev); + info("%s(%d) - fimc_is runtime resume complete\n", __func__, on); +#endif + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (core->id == SENSOR_POSITION_FRONT) { + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + if (!sysfs_finfo->is_caldata_read) { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + ret = fimc_is_sec_fw_sel_eeprom(dev, fw_name, setf_name, SENSOR_POSITION_REAR, true); +#else + ret = fimc_is_sec_fw_sel(core, dev, fw_name, setf_name, true); +#endif + } else { + snprintf(fw_name, sizeof(fw_name), "%s", sysfs_finfo->load_fw_name); + } + fimc_is_sec_get_sysfs_finfo_front(&sysfs_finfo); + if (!sysfs_finfo->is_caldata_read) { + ret = fimc_is_sec_fw_sel_eeprom(dev, fw_name, setf_name, SENSOR_POSITION_FRONT, false); + if (ret < 0) { + err("failed to select firmware (%d)", ret); + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + goto p_err_pm; + } + } + } else +#endif + { + fimc_is_sec_get_sysfs_finfo(&sysfs_finfo); + if (!sysfs_finfo->is_caldata_read) { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + ret = fimc_is_sec_fw_sel_eeprom(dev, fw_name, setf_name, SENSOR_POSITION_REAR, false); +#else + ret = fimc_is_sec_fw_sel(core, dev, fw_name, setf_name, false); +#endif + if (ret < 0) { + err("failed to select firmware (%d)", ret); + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + goto p_err_pm; + } + } else { + snprintf(fw_name, sizeof(fw_name), "%s", sysfs_finfo->load_fw_name); + } + } + +#ifdef CONFIG_COMPANION_USE +// ret = fimc_is_sec_concord_fw_sel(core, dev, device->pdata, companion_fw_name, master_setf_name, mode_setf_name); + /*if (ret < 0) { + err("failed to select companion firmware (%d)", ret); + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + goto exit; + }*/ +#endif + /* 3. Load IS firmware */ + ret = fimc_is_ischain_loadfirm(device); + if (ret) { + err("failed to fimc_is_request_firmware (%d)", ret); + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + ret = -EINVAL; + goto p_err_pm; + } + set_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + +#if defined(CONFIG_SOC_EXYNOS5422) + tdnr_s3d_pixel_async_sw_reset(device); +#endif /* defined(CONFIG_SOC_EXYNOS5422) */ + /* 4. A5 start address setting */ + mdbgd_ischain("imemory.base(dvaddr) : 0x%08x\n", device, device->imemory.dvaddr); + mdbgd_ischain("imemory.base(kvaddr) : 0x%08X\n", device, device->imemory.kvaddr); + + if (!device->imemory.dvaddr) { + merr("firmware device virtual is null", device); + ret = -ENOMEM; + goto p_err_pm; + } + + writel(device->imemory.dvaddr, device->regs + BBOAR); + val = __raw_readl(device->regs + BBOAR); + if(device->imemory.dvaddr != val) + err("dvaddr : %x , BBOAR : %x", device->imemory.dvaddr,val); + +#ifdef CONFIG_ARM_TRUSTZONE + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(PA_FIMC_IS_GIC_C + 0x4), 0x000000FF, 0); + for (i = 0; i < 3; i++) + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(PA_FIMC_IS_GIC_D + 0x80 + (i * 4)), 0xFFFFFFFF, 0); + for (i = 0; i < 18; i++) + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(PA_FIMC_IS_GIC_D + 0x400 + (i * 4)), 0x10101010, 0); + + exynos_smc_readsfr(PA_FIMC_IS_GIC_C + 0x4, &debug); + info("%s : PA_FIMC_IS_GIC_C : 0x%08x\n", __func__, debug); + if (debug == 0x00) + merr("secure configuration is fail[0x131E0004:%08X]", device, debug); +#endif + + /* To guarantee FW restart */ + if (__raw_readl(PMUREG_ISP_ARM_STATUS) & 0x1) { + fimc_is_a5_power(dev, 0); + } + + /* 5. A5 power on*/ + fimc_is_a5_power(dev, 1); + + set_bit(FIMC_IS_ISCHAIN_POWER_ON, &device->state); + + /* for mideaserver force down */ + set_bit(FIMC_IS_ISCHAIN_POWER_ON, &core->state); + } else { + /* Check FW state for WFI of A5 */ + debug = readl(device->interface->regs + ISSR6); + printk(KERN_INFO "%s: A5 state(0x%x)\n", __func__, debug); + + /* FIMC-IS local power down */ +#if defined(CONFIG_PM_RUNTIME) + rpm_ret = pm_runtime_put_sync(dev); + if (rpm_ret < 0) + err("pm_runtime_put_sync() return error: %d", rpm_ret); + mdbgd_ischain("pm_runtime_suspended = %d\n", device, pm_runtime_suspended(dev)); + + if (!pm_runtime_suspended(dev)) { + err("failed to call RPM device callback, try A5 power down manually\n"); + + fimc_is_a5_power(dev, 0); + } +#else + fimc_is_a5_power(dev, 0); + + fimc_is_runtime_suspend(dev); +#endif + clear_bit(FIMC_IS_ISCHAIN_POWER_ON, &device->state); + + /* for mideaserver force down */ + clear_bit(FIMC_IS_ISCHAIN_POWER_ON, &core->state); + } + + info("%s(%d)\n", __func__, test_bit(FIMC_IS_ISCHAIN_POWER_ON, &device->state)); + return ret; + +p_err_pm: +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_put_sync(dev); +#else + fimc_is_runtime_suspend(dev); +#endif + + return ret; +} + +static int fimc_is_itf_s_param(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 lindex, + u32 hindex, + u32 indexes) +{ + int ret = 0; + u32 flag, index; + u32 dst_base, src_base; + + BUG_ON(!device); + + if (frame) { + if (!test_bit(FIMC_IS_ISHCAIN_START, &device->state)) { + merr("s_param is fail, device already is stopped", device); + BUG(); + } + + dst_base = (u32)&device->is_region->parameter; + src_base = (u32)frame->shot->ctl.entry.parameter; + + for (index = 0; lindex && (index < 32); index++) { + flag = 1 << index; + if (lindex & flag) { + memcpy((u32 *)(dst_base + (index * PARAMETER_MAX_SIZE)), + (u32 *)(src_base + (index * PARAMETER_MAX_SIZE)), + PARAMETER_MAX_SIZE); + lindex &= ~flag; + } + } + + for (index = 0; hindex && (index < 32); index++) { + flag = 1 << index; + if (hindex & flag) { + memcpy((u32 *)(dst_base + ((32 + index) * PARAMETER_MAX_SIZE)), + (u32 *)(src_base + ((32 + index) * PARAMETER_MAX_SIZE)), + PARAMETER_MAX_SIZE); + hindex &= ~flag; + } + } + + fimc_is_ischain_region_flush(device); + } else { + /* + * this check code is commented until per-frame control is worked fully + * + * if ( test_bit(FIMC_IS_ISHCAIN_START, &device->state)) { + * merr("s_param is fail, device already is started", device); + * BUG(); + * } + */ + + fimc_is_ischain_region_flush(device); + + if (lindex || hindex) { + ret = fimc_is_hw_s_param(device->interface, + device->instance, + lindex, + hindex, + indexes); + } + } + + return ret; +} + +static void * fimc_is_itf_g_param(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 index) +{ + u32 dst_base, src_base, dst_param, src_param; + + BUG_ON(!device); + + if (frame) { + if (!test_bit(FIMC_IS_ISHCAIN_START, &device->state)) { + merr("s_param is fail, device already is stopped", device); + BUG(); + } + + dst_base = (u32)&frame->shot->ctl.entry.parameter[0]; + dst_param = (dst_base + (index * PARAMETER_MAX_SIZE)); + src_base = (u32)&device->is_region->parameter; + src_param = (src_base + (index * PARAMETER_MAX_SIZE)); + memcpy((u32 *)dst_param, (u32 *)src_param, PARAMETER_MAX_SIZE); + } else { + dst_base = (u32)&device->is_region->parameter; + dst_param = (dst_base + (index * PARAMETER_MAX_SIZE)); + } + + return (void *)dst_param; +} + +static int fimc_is_itf_a_param(struct fimc_is_device_ischain *device, + u32 group) +{ + int ret = 0; + + BUG_ON(!device); + + if (device->setfile >= ISS_SUB_END) { + mwarn("setfile id(%d) is invalid", device, device->setfile); + device->setfile = ISS_SUB_SCENARIO_STILL_PREVIEW; + } + + ret = fimc_is_hw_a_param(device->interface, + device->instance, + group, + device->setfile); + + return ret; +} + +static int fimc_is_itf_f_param(struct fimc_is_device_ischain *device) +{ + int ret = 0; + u32 group = 0; +#ifdef DEBUG + u32 navailable = 0; + struct is_region *region = device->is_region; +#endif + + mdbgd_ischain(" NAME SIZE BINNING FRAMERATE\n", device); + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state) || + !IS_ISCHAIN_OTF(device)) + mdbgd_ischain("SENSOR : %04dx%04d %1dx%1d %3d\n", + device, + region->parameter.taa.vdma1_input.width + device->margin_width, + region->parameter.taa.vdma1_input.height + device->margin_height, + (region->parameter.taa.vdma1_input.sensor_binning_ratio_x / 1000), + (region->parameter.taa.vdma1_input.sensor_binning_ratio_y / 1000), + region->parameter.sensor.config.framerate + ); + else + mdbgd_ischain("SENSOR : %04dx%04d %1dx%1d %3d\n", + device, + region->parameter.sensor.dma_output.width, + region->parameter.sensor.dma_output.height, + (region->parameter.taa.otf_input.sensor_binning_ratio_x / 1000), + (region->parameter.taa.otf_input.sensor_binning_ratio_y / 1000), + region->parameter.sensor.config.framerate + ); + mdbgd_ischain(" NAME ON BYPASS PATH SIZE FORMAT\n", device); + mdbgd_ischain("3AX OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.taa.control.cmd, + region->parameter.taa.control.bypass, + region->parameter.taa.otf_input.cmd, + region->parameter.taa.otf_input.width, + region->parameter.taa.otf_input.height, + region->parameter.taa.otf_input.format + ); + mdbgd_ischain("3AX DI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.taa.control.cmd, + region->parameter.taa.control.bypass, + region->parameter.taa.vdma1_input.cmd, + region->parameter.taa.vdma1_input.width, + region->parameter.taa.vdma1_input.height, + region->parameter.taa.vdma1_input.format + ); + mdbgd_ischain("3AX DO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.taa.control.cmd, + region->parameter.taa.control.bypass, + region->parameter.taa.vdma2_output.cmd, + region->parameter.taa.vdma2_output.width, + region->parameter.taa.vdma2_output.height, + region->parameter.taa.vdma2_output.format + ); + mdbgd_ischain("ISP OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.isp.control.cmd, + region->parameter.isp.control.bypass, + region->parameter.isp.otf_input.cmd, + region->parameter.isp.otf_input.width, + region->parameter.isp.otf_input.height, + region->parameter.isp.otf_input.format + ); + mdbgd_ischain("ISP DI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.isp.control.cmd, + region->parameter.isp.control.bypass, + region->parameter.isp.vdma1_input.cmd, + region->parameter.isp.vdma1_input.width, + region->parameter.isp.vdma1_input.height, + region->parameter.isp.vdma1_input.format + ); + mdbgd_ischain("ISP OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.isp.control.cmd, + region->parameter.isp.control.bypass, + region->parameter.isp.otf_output.cmd, + region->parameter.isp.otf_output.width, + region->parameter.isp.otf_output.height, + region->parameter.isp.otf_output.format + ); + mdbgd_ischain("DRC OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.drc.control.cmd, + region->parameter.drc.control.bypass, + region->parameter.drc.otf_input.cmd, + region->parameter.drc.otf_input.width, + region->parameter.drc.otf_input.height, + region->parameter.drc.otf_input.format + ); + mdbgd_ischain("DRC OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.drc.control.cmd, + region->parameter.drc.control.bypass, + region->parameter.drc.otf_output.cmd, + region->parameter.drc.otf_output.width, + region->parameter.drc.otf_output.height, + region->parameter.drc.otf_output.format + ); + mdbgd_ischain("SCC OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.scalerc.control.cmd, + region->parameter.scalerc.control.bypass, + region->parameter.scalerc.otf_input.cmd, + region->parameter.scalerc.otf_input.width, + region->parameter.scalerc.otf_input.height, + region->parameter.scalerc.otf_input.format + ); + mdbgd_ischain("SCC DO : %2d %4d %3d %04dx%04d %4d,%d\n", device, + region->parameter.scalerc.control.cmd, + region->parameter.scalerc.control.bypass, + region->parameter.scalerc.dma_output.cmd, + region->parameter.scalerc.dma_output.width, + region->parameter.scalerc.dma_output.height, + region->parameter.scalerc.dma_output.format, + region->parameter.scalerc.dma_output.plane + ); + mdbgd_ischain("SCC OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.scalerc.control.cmd, + region->parameter.scalerc.control.bypass, + region->parameter.scalerc.otf_output.cmd, + region->parameter.scalerc.otf_output.width, + region->parameter.scalerc.otf_output.height, + region->parameter.scalerc.otf_output.format + ); + mdbgd_ischain("ODC OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.odc.control.cmd, + region->parameter.odc.control.bypass, + region->parameter.odc.otf_input.cmd, + region->parameter.odc.otf_input.width, + region->parameter.odc.otf_input.height, + region->parameter.odc.otf_input.format + ); + mdbgd_ischain("ODC OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.odc.control.cmd, + region->parameter.odc.control.bypass, + region->parameter.odc.otf_output.cmd, + region->parameter.odc.otf_output.width, + region->parameter.odc.otf_output.height, + region->parameter.odc.otf_output.format + ); + mdbgd_ischain("DIS OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.dis.control.cmd, + region->parameter.dis.control.bypass, + region->parameter.dis.otf_input.cmd, + region->parameter.dis.otf_input.width, + region->parameter.dis.otf_input.height, + region->parameter.dis.otf_input.format + ); + mdbgd_ischain("DIS OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.dis.control.cmd, + region->parameter.dis.control.bypass, + region->parameter.dis.otf_output.cmd, + region->parameter.dis.otf_output.width, + region->parameter.dis.otf_output.height, + region->parameter.dis.otf_output.format + ); + mdbgd_ischain("DNR OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.tdnr.control.cmd, + region->parameter.tdnr.control.bypass, + region->parameter.tdnr.otf_input.cmd, + region->parameter.tdnr.otf_input.width, + region->parameter.tdnr.otf_input.height, + region->parameter.tdnr.otf_input.format + ); + mdbgd_ischain("DNR OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.tdnr.control.cmd, + region->parameter.tdnr.control.bypass, + region->parameter.tdnr.otf_output.cmd, + region->parameter.tdnr.otf_output.width, + region->parameter.tdnr.otf_output.height, + region->parameter.tdnr.otf_output.format + ); + mdbgd_ischain("SCP OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.scalerp.control.cmd, + region->parameter.scalerp.control.bypass, + region->parameter.scalerp.otf_input.cmd, + region->parameter.scalerp.otf_input.width, + region->parameter.scalerp.otf_input.height, + region->parameter.scalerp.otf_input.format + ); + mdbgd_ischain("SCP DO : %2d %4d %3d %04dx%04d %4d,%d\n", device, + region->parameter.scalerp.control.cmd, + region->parameter.scalerp.control.bypass, + region->parameter.scalerp.dma_output.cmd, + region->parameter.scalerp.dma_output.width, + region->parameter.scalerp.dma_output.height, + region->parameter.scalerp.dma_output.format, + region->parameter.scalerp.dma_output.plane + ); + mdbgd_ischain("SCP OO : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.scalerp.control.cmd, + region->parameter.scalerp.control.bypass, + region->parameter.scalerp.otf_output.cmd, + region->parameter.scalerp.otf_output.width, + region->parameter.scalerp.otf_output.height, + region->parameter.scalerp.otf_output.format + ); + mdbgd_ischain("FD OI : %2d %4d %3d %04dx%04d %3d\n", device, + region->parameter.fd.control.cmd, + region->parameter.fd.control.bypass, + region->parameter.fd.otf_input.cmd, + region->parameter.fd.otf_input.width, + region->parameter.fd.otf_input.height, + region->parameter.fd.otf_input.format + ); + mdbgd_ischain(" NAME CMD IN_SZIE OT_SIZE CROP POS\n", device); + mdbgd_ischain("SCC CI : %d %04dx%04d %04dx%04d %04dx%04d %04dx%04d\n", device, + region->parameter.scalerc.input_crop.cmd, + region->parameter.scalerc.input_crop.in_width, + region->parameter.scalerc.input_crop.in_height, + region->parameter.scalerc.input_crop.out_width, + region->parameter.scalerc.input_crop.out_height, + region->parameter.scalerc.input_crop.crop_width, + region->parameter.scalerc.input_crop.crop_height, + region->parameter.scalerc.input_crop.pos_x, + region->parameter.scalerc.input_crop.pos_y + ); + mdbgd_ischain("SCC CO : %d %04dx%04d %04dx%04d %04dx%04d %04dx%04d\n", device, + region->parameter.scalerc.output_crop.cmd, + navailable, + navailable, + navailable, + navailable, + region->parameter.scalerc.output_crop.crop_width, + region->parameter.scalerc.output_crop.crop_height, + region->parameter.scalerc.output_crop.pos_x, + region->parameter.scalerc.output_crop.pos_y + ); + mdbgd_ischain("SCP CI : %d %04dx%04d %04dx%04d %04dx%04d %04dx%04d\n", device, + region->parameter.scalerp.input_crop.cmd, + region->parameter.scalerp.input_crop.in_width, + region->parameter.scalerp.input_crop.in_height, + region->parameter.scalerp.input_crop.out_width, + region->parameter.scalerp.input_crop.out_height, + region->parameter.scalerp.input_crop.crop_width, + region->parameter.scalerp.input_crop.crop_height, + region->parameter.scalerp.input_crop.pos_x, + region->parameter.scalerp.input_crop.pos_y + ); + mdbgd_ischain("SCP CO : %d %04dx%04d %04dx%04d %04dx%04d %04dx%04d\n", device, + region->parameter.scalerp.output_crop.cmd, + navailable, + navailable, + navailable, + navailable, + region->parameter.scalerp.output_crop.crop_width, + region->parameter.scalerp.output_crop.crop_height, + region->parameter.scalerp.output_crop.pos_x, + region->parameter.scalerp.output_crop.pos_y + ); + + group |= GROUP_ID(device->group_3aa.id); + group |= GROUP_ID(device->group_isp.id); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group = GROUP_ID(GROUP_ID_3A0); + + if (device->setfile >= ISS_SUB_END) { + mwarn("setfile id(%d) is invalid", device, device->setfile); + device->setfile = ISS_SUB_SCENARIO_STILL_PREVIEW; + } + + ret = fimc_is_hw_a_param(device->interface, + device->instance, + (group & GROUP_ID_PARM_MASK), + device->setfile); + return ret; +} + +static int fimc_is_itf_enum(struct fimc_is_device_ischain *device) +{ + int ret = 0; + + mdbgd_ischain("%s()\n", device, __func__); + + ret = fimc_is_hw_enum(device->interface); + if (ret) { + merr("fimc_is_itf_enum is fail(%d)", device, ret); + CALL_POPS(device, print_pwr, device->pdev); + CALL_POPS(device, print_clk, device->pdev); + } + + return ret; +} + +static int fimc_is_itf_open(struct fimc_is_device_ischain *device, + u32 module_id, + u32 group_id, + u32 flag, + struct sensor_open_extended *ext_info) +{ + int ret = 0; + struct is_region *region; + struct fimc_is_interface *itf; + + BUG_ON(!device); + BUG_ON(!device->is_region); + BUG_ON(!device->sensor); + BUG_ON(!device->interface); + BUG_ON(!ext_info); + + region = device->is_region; + itf = device->interface; + + memcpy(®ion->shared[0], ext_info, sizeof(struct sensor_open_extended)); + + fimc_is_ischain_region_flush(device); + + ret = fimc_is_hw_open(device->interface, + device->instance, + module_id, + device->imemory.dvaddr_shared, + group_id, + flag, + &device->margin_width, + &device->margin_height); + if (ret) { + merr("fimc_is_hw_open is fail", device); + CALL_POPS(device, print_cfg, device->pdev, + fimc_is_sensor_g_instance(device->sensor)); + ret = -EINVAL; + goto p_err; + } + + /* HACK */ + device->margin_left = 8; + device->margin_right = 8; + device->margin_top = 6; + device->margin_bottom = 4; + device->margin_width = device->margin_left + device->margin_right; + device->margin_height = device->margin_top + device->margin_bottom; + mdbgd_ischain("margin %dx%d\n", device, + device->margin_width, device->margin_height); + + fimc_is_ischain_region_invalid(device); + + if (region->shared[MAX_SHARED_COUNT-1] != MAGIC_NUMBER) { + merr("MAGIC NUMBER error", device); + ret = -EINVAL; + goto p_err; + } + + memset(®ion->parameter, 0x0, sizeof(struct is_param_region)); + + memcpy(®ion->parameter.sensor, &init_sensor_param, + sizeof(struct sensor_param)); + memcpy(®ion->parameter.taa, &init_taa_param, + sizeof(struct taa_param)); + memcpy(®ion->parameter.isp, &init_isp_param, + sizeof(struct isp_param)); + memcpy(®ion->parameter.drc, &init_drc_param, + sizeof(struct drc_param)); + memcpy(®ion->parameter.scalerc, &init_scalerc_param, + sizeof(struct scalerc_param)); + memcpy(®ion->parameter.odc, &init_odc_param, + sizeof(struct odc_param)); + memcpy(®ion->parameter.dis, &init_dis_param, + sizeof(struct dis_param)); + memcpy(®ion->parameter.tdnr, &init_tdnr_param, + sizeof(struct tdnr_param)); + memcpy(®ion->parameter.scalerp, &init_scalerp_param, + sizeof(struct scalerp_param)); + memcpy(®ion->parameter.fd, &init_fd_param, + sizeof(struct fd_param)); + +p_err: + return ret; +} + +static int fimc_is_itf_close(struct fimc_is_device_ischain *device) +{ + int ret = 0; + struct fimc_is_interface *itf; + + BUG_ON(!device); + BUG_ON(!device->interface); + + itf = device->interface; + + ret = fimc_is_hw_close(itf, device->instance); + if (ret) { + merr("fimc_is_hw_close is fail", device); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_itf_setfile(struct fimc_is_device_ischain *device, + char *setfile_name) +{ + int ret = 0; + u32 setfile_addr = 0; + struct fimc_is_interface *itf; + + BUG_ON(!device); + BUG_ON(!device->interface); + BUG_ON(!setfile_name); + + itf = device->interface; + + mdbgd_ischain("%s(setfile : %s)\n", device, __func__, setfile_name); + + ret = fimc_is_hw_saddr(itf, device->instance, &setfile_addr); + if (ret) { + merr("fimc_is_hw_saddr is fail(%d)", device, ret); + goto p_err; + } + + if (!setfile_addr) { + merr("setfile address is NULL", device); + pr_err("cmd : %08X\n", readl(&itf->com_regs->ihcmd)); + pr_err("id : %08X\n", readl(&itf->com_regs->ihc_sensorid)); + pr_err("param1 : %08X\n", readl(&itf->com_regs->ihc_param1)); + pr_err("param2 : %08X\n", readl(&itf->com_regs->ihc_param2)); + pr_err("param3 : %08X\n", readl(&itf->com_regs->ihc_param3)); + pr_err("param4 : %08X\n", readl(&itf->com_regs->ihc_param4)); + goto p_err; + } + + ret = fimc_is_ischain_loadsetf(device, setfile_addr, setfile_name); + if (ret) { + merr("fimc_is_ischain_loadsetf is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_hw_setfile(itf, device->instance); + if (ret) { + merr("fimc_is_hw_setfile is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_itf_map(struct fimc_is_device_ischain *device, + u32 group, u32 shot_addr, u32 shot_size) +{ + int ret = 0; + + BUG_ON(!device); + + mdbgd_ischain("%s()\n", device, __func__); + + ret = fimc_is_hw_map(device->interface, device->instance, group, shot_addr, shot_size); + if (ret) + merr("fimc_is_hw_map is fail(%d)", device, ret); + + return ret; +} + +static int fimc_is_itf_unmap(struct fimc_is_device_ischain *device, + u32 group) +{ + int ret = 0; + + mdbgd_ischain("%s()\n", device, __func__); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group = GROUP_ID(GROUP_ID_3A0); + + ret = fimc_is_hw_unmap(device->interface, device->instance, group); + if (ret) + merr("fimc_is_hw_unmap is fail(%d)", device, ret); + + return ret; +} + +int fimc_is_itf_stream_on(struct fimc_is_device_ischain *device) +{ + int ret = 0; + u32 retry = 10000; +#ifdef ENABLE_DVFS + int scenario_id; +#endif + struct fimc_is_group *group_3aa, *group_isp; + struct fimc_is_resourcemgr *resourcemgr; + + BUG_ON(!device); + BUG_ON(!device->resourcemgr); + + resourcemgr = device->resourcemgr; + group_3aa = &device->group_3aa; + group_isp = &device->group_isp; + + /* 3ax, isp group should be started */ + if (!test_bit(FIMC_IS_GROUP_READY, &group_3aa->state)) { + merr("group isp is not start", device); + goto p_err; + } + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) || + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1)) { + if (!test_bit(FIMC_IS_GROUP_READY, &group_3aa->state)) { + merr("group 3ax is not start", device); + goto p_err; + } + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group_3aa->state)) { + while (--retry && (atomic_read(&group_3aa->scount) < + group_3aa->async_shots)) { + udelay(100); + } + } + } + + if (retry) + info("[ISC:D:%d] stream on ready\n", device->instance); + else + pr_err("[ISC:D:%d] stream on NOT ready\n", device->instance); + +#ifdef ENABLE_DVFS + mutex_lock(&resourcemgr->dvfs_ctrl.lock); + if ((!pm_qos_request_active(&device->user_qos)) && + (sysfs_debug.en_dvfs)) { + /* try to find dynamic scenario to apply */ + scenario_id = fimc_is_dvfs_sel_scenario(FIMC_IS_STATIC_SN, device, NULL); + if (scenario_id >= 0) { + struct fimc_is_dvfs_scenario_ctrl *static_ctrl = + resourcemgr->dvfs_ctrl.static_ctrl; + info("[ISC:D:%d] static scenario(%d)-[%s]\n", + device->instance, scenario_id, + static_ctrl->scenarios[static_ctrl->cur_scenario_idx].scenario_nm); + fimc_is_set_dvfs(device, scenario_id); + } + } + mutex_unlock(&resourcemgr->dvfs_ctrl.lock); +#endif + ret = fimc_is_hw_stream_on(device->interface, device->instance); + if (ret) { + merr("fimc_is_hw_stream_on is fail(%d)", device, ret); + CALL_POPS(device, print_clk, device->pdev); + } + +p_err: + return ret; +} + +int fimc_is_itf_stream_off(struct fimc_is_device_ischain *device) +{ + int ret = 0; + + info("[ISC:D:%d] stream off ready\n", device->instance); + + ret = fimc_is_hw_stream_off(device->interface, device->instance); + + return ret; +} + +int fimc_is_itf_process_start(struct fimc_is_device_ischain *device, + u32 group) +{ + int ret = 0; + + ret = fimc_is_hw_process_on(device->interface, + device->instance, group); + + return ret; +} + +int fimc_is_itf_process_stop(struct fimc_is_device_ischain *device, + u32 group) +{ + int ret = 0; + +#ifdef ENABLE_CLOCK_GATE + struct fimc_is_core *core = (struct fimc_is_core *)device->interface->core; + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + fimc_is_clk_gate_lock_set(core, device->instance, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + } +#endif + ret = fimc_is_hw_process_off(device->interface, + device->instance, group, 0); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, device->instance, false); +#endif + return ret; +} + +int fimc_is_itf_force_stop(struct fimc_is_device_ischain *device, + u32 group) +{ + int ret = 0; + +#ifdef ENABLE_CLOCK_GATE + struct fimc_is_core *core = (struct fimc_is_core *)device->interface->core; +#endif + /* if there's only one group of isp, send group id by 3a0 */ + if ((group & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group = GROUP_ID(GROUP_ID_3A0); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + fimc_is_clk_gate_lock_set(core, device->instance, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + } +#endif + ret = fimc_is_hw_process_off(device->interface, + device->instance, group, 1); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, device->instance, false); +#endif + return ret; +} + +static int fimc_is_itf_init_process_start(struct fimc_is_device_ischain *device) +{ + int ret = 0; + u32 group = 0; + + group |= GROUP_ID(device->group_3aa.id); + group |= GROUP_ID(device->group_isp.id); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group = GROUP_ID(GROUP_ID_3A0); + + ret = fimc_is_hw_process_on(device->interface, + device->instance, + (group & GROUP_ID_PARM_MASK)); + + return ret; +} + +static int fimc_is_itf_init_process_stop(struct fimc_is_device_ischain *device) +{ + int ret = 0; + u32 group = 0; + +#ifdef ENABLE_CLOCK_GATE + struct fimc_is_core *core = (struct fimc_is_core *)device->interface->core; +#endif + group |= GROUP_ID(device->group_3aa.id); + group |= GROUP_ID(device->group_isp.id); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group = GROUP_ID(GROUP_ID_3A0); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + fimc_is_clk_gate_lock_set(core, device->instance, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + + } +#endif + ret = fimc_is_hw_process_off(device->interface, + device->instance, (group & GROUP_ID_PARM_MASK), 0); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, device->instance, false); +#endif + return ret; +} + +int fimc_is_itf_i2c_lock(struct fimc_is_device_ischain *this, + int i2c_clk, bool lock) +{ + int ret = 0; + struct fimc_is_interface *itf = this->interface; + + if (lock) + fimc_is_interface_lock(itf); + + ret = fimc_is_hw_i2c_lock(itf, this->instance, + i2c_clk, lock); + + if (!lock) + fimc_is_interface_unlock(itf); + + return ret; +} + +int fimc_is_itf_g_capability(struct fimc_is_device_ischain *this) +{ + int ret = 0; +#ifdef PRINT_CAPABILITY + u32 metadata; + u32 index; + struct camera2_sm *capability; +#endif + + ret = fimc_is_hw_g_capability(this->interface, this->instance, + (this->imemory.kvaddr_shared - this->imemory.kvaddr)); + + fimc_is_ischain_region_invalid(this); + +#ifdef PRINT_CAPABILITY + memcpy(&this->capability, &this->is_region->shared, + sizeof(struct camera2_sm)); + capability = &this->capability; + + printk(KERN_INFO "===ColorC================================\n"); + printk(KERN_INFO "===ToneMapping===========================\n"); + metadata = capability->tonemap.maxCurvePoints; + printk(KERN_INFO "maxCurvePoints : %d\n", metadata); + + printk(KERN_INFO "===Scaler================================\n"); + printk(KERN_INFO "foramt : %d, %d, %d, %d\n", + capability->scaler.availableFormats[0], + capability->scaler.availableFormats[1], + capability->scaler.availableFormats[2], + capability->scaler.availableFormats[3]); + + printk(KERN_INFO "===StatisTicsG===========================\n"); + index = 0; + metadata = capability->stats.availableFaceDetectModes[index]; + while (metadata) { + printk(KERN_INFO "availableFaceDetectModes : %d\n", metadata); + index++; + metadata = capability->stats.availableFaceDetectModes[index]; + } + printk(KERN_INFO "maxFaceCount : %d\n", + capability->stats.maxFaceCount); + printk(KERN_INFO "histogrambucketCount : %d\n", + capability->stats.histogramBucketCount); + printk(KERN_INFO "maxHistogramCount : %d\n", + capability->stats.maxHistogramCount); + printk(KERN_INFO "sharpnessMapSize : %dx%d\n", + capability->stats.sharpnessMapSize[0], + capability->stats.sharpnessMapSize[1]); + printk(KERN_INFO "maxSharpnessMapValue : %d\n", + capability->stats.maxSharpnessMapValue); + + printk(KERN_INFO "===3A====================================\n"); + printk(KERN_INFO "maxRegions : %d\n", capability->aa.maxRegions); + + index = 0; + metadata = capability->aa.aeAvailableModes[index]; + while (metadata) { + printk(KERN_INFO "aeAvailableModes : %d\n", metadata); + index++; + metadata = capability->aa.aeAvailableModes[index]; + } + printk(KERN_INFO "aeCompensationStep : %d,%d\n", + capability->aa.aeCompensationStep.num, + capability->aa.aeCompensationStep.den); + printk(KERN_INFO "aeCompensationRange : %d ~ %d\n", + capability->aa.aeCompensationRange[0], + capability->aa.aeCompensationRange[1]); + index = 0; + metadata = capability->aa.aeAvailableTargetFpsRanges[index][0]; + while (metadata) { + printk(KERN_INFO "TargetFpsRanges : %d ~ %d\n", metadata, + capability->aa.aeAvailableTargetFpsRanges[index][1]); + index++; + metadata = capability->aa.aeAvailableTargetFpsRanges[index][0]; + } + index = 0; + metadata = capability->aa.aeAvailableAntibandingModes[index]; + while (metadata) { + printk(KERN_INFO "aeAvailableAntibandingModes : %d\n", + metadata); + index++; + metadata = capability->aa.aeAvailableAntibandingModes[index]; + } + index = 0; + metadata = capability->aa.awbAvailableModes[index]; + while (metadata) { + printk(KERN_INFO "awbAvailableModes : %d\n", metadata); + index++; + metadata = capability->aa.awbAvailableModes[index]; + } + index = 0; + metadata = capability->aa.afAvailableModes[index]; + while (metadata) { + printk(KERN_INFO "afAvailableModes : %d\n", metadata); + index++; + metadata = capability->aa.afAvailableModes[index]; + } +#endif + return ret; +} + +int fimc_is_itf_power_down(struct fimc_is_interface *interface) +{ + int ret = 0; +#ifdef ENABLE_CLOCK_GATE + /* HACK */ + struct fimc_is_core *core = (struct fimc_is_core *)interface->core; + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + fimc_is_clk_gate_lock_set(core, 0, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + } +#endif + ret = fimc_is_hw_power_down(interface, 0); +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, 0, false); +#endif + return ret; +} + +int fimc_is_itf_sys_ctl(struct fimc_is_device_ischain *this, + int cmd, int val) +{ + int ret = 0; + struct fimc_is_interface *itf = this->interface; + + ret = fimc_is_hw_sys_ctl(itf, this->instance, + cmd, val); + + return ret; +} + +int fimc_is_itf_sensor_mode(struct fimc_is_device_ischain *ischain) +{ + struct fimc_is_device_sensor *sensor = ischain->sensor; + + return fimc_is_hw_sensor_mode(ischain->interface, + ischain->instance, + ((sensor->mode << 16) | (ischain->module & 0xFFFF))); +} + +static int fimc_is_itf_grp_shot(struct fimc_is_device_ischain *device, + struct fimc_is_group *group, + struct fimc_is_frame *frame) +{ + int ret = 0; + u32 group_id = 0; +#ifdef ENABLE_CLOCK_GATE + struct fimc_is_core *core = (struct fimc_is_core *)device->interface->core; +#endif + BUG_ON(!device); + BUG_ON(!group); + BUG_ON(!frame); + BUG_ON(!frame->shot); + + /* Cache Flush */ + fimc_is_ischain_meta_flush(frame); + + if (frame->shot->magicNumber != SHOT_MAGIC_NUMBER) { + merr("shot magic number error(0x%08X)\n", device, frame->shot->magicNumber); + merr("shot_ext size : %d", device, sizeof(struct camera2_shot_ext)); + ret = -EINVAL; + goto p_err; + } + +#ifdef DBG_STREAMING + if (group->id == GROUP_ID_3A0) + info("[3A0:D:%d] GRP%d SHOT(%d)\n", device->instance, group->id, frame->fcount); + else if (group->id == GROUP_ID_3A1) + info("[3A1:D:%d] GRP%d SHOT(%d)\n", device->instance, group->id, frame->fcount); + else if (group->id == GROUP_ID_ISP) + info("[ISP:D:%d] GRP%d SHOT(%d)\n", device->instance, group->id, frame->fcount); + else if (group->id == GROUP_ID_DIS) + info("[DIS:D:%d] GRP%d SHOT(%d)\n", device->instance, group->id, frame->fcount); + else + info("[ERR:D:%d] GRP%d SHOT(%d)\n", device->instance, group->id, frame->fcount); +#endif + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + do_gettimeofday(&frame->time_shot); +#endif +#ifdef EXTERNAL_TIME + do_gettimeofday(&frame->tzone[TM_SHOT]); +#endif +#endif + +#ifdef ENABLE_CLOCK_GATE + /* HACK */ + /* dynamic clock on */ + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_set(core, group->id, true, false, true); +#endif + group_id = GROUP_ID(group->id); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group_id & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group_id = GROUP_ID(GROUP_ID_3A0); + + ret = fimc_is_hw_shot_nblk(device->interface, + device->instance, + group_id, + frame->dvaddr_buffer[0], + frame->dvaddr_shot, + frame->fcount, + frame->rcount); + +p_err: + return ret; +} + +int fimc_is_ischain_probe(struct fimc_is_device_ischain *device, + struct fimc_is_interface *interface, + struct fimc_is_resourcemgr *resourcemgr, + struct fimc_is_groupmgr *groupmgr, + struct fimc_is_mem *mem, + struct platform_device *pdev, + u32 instance, + u32 regs) +{ + int ret = 0; + struct fimc_is_subdev *scc, *dis, *scp; + + BUG_ON(!interface); + BUG_ON(!mem); + BUG_ON(!pdev); + BUG_ON(!device); + + /*device initialization should be just one time*/ + scc = &device->scc; + dis = &device->dis; + scp = &device->scp; + + device->interface = interface; + device->mem = mem; + device->pdev = pdev; + device->pdata = pdev->dev.platform_data; + device->regs = (void *)regs; + device->instance = instance; + device->groupmgr = groupmgr; + device->resourcemgr = resourcemgr; + device->sensor = NULL; + device->margin_left = 0; + device->margin_right = 0; + device->margin_width = 0; + device->margin_top = 0; + device->margin_bottom = 0; + device->margin_height = 0; + device->dis_width = 0; + device->dis_height = 0; + device->chain0_width = 0; + device->chain0_height = 0; + device->chain1_width = 0; + device->chain1_height = 0; + device->chain2_width = 0; + device->chain2_height = 0; + device->chain3_width = 0; + device->chain3_height = 0; + device->crop_x = 0; + device->crop_y = 0; + device->crop_width = 0; + device->crop_height = 0; + device->setfile = 0; + device->color_range = 0; + device->dzoom_width = 0; + device->force_down = false; + device->is_region = NULL; + device->taa_size_forceset = 0; + device->taa_size_changed_fcount = 0; + device->isp_size_forceset = 0; + device->isp_size_changed_fcount = 0; + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) + fimc_is_group_probe(groupmgr, &device->group_3aa, ENTRY_3AA); + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, isp)) + fimc_is_group_probe(groupmgr, &device->group_isp, ENTRY_ISP); + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, dis)) + fimc_is_group_probe(groupmgr, &device->group_dis, ENTRY_DIS); + + device->drc.entry = ENTRY_DRC; + device->scc.entry = ENTRY_SCALERC; + device->dis.entry = ENTRY_DIS; + device->dnr.entry = ENTRY_TDNR; + device->scp.entry = ENTRY_SCALERP; + device->fd.entry = ENTRY_LHFD; + device->taac.entry = ENTRY_3AAC; + device->taap.entry = ENTRY_3AAP; + + clear_bit(FIMC_IS_ISCHAIN_OPEN, &device->state); + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + clear_bit(FIMC_IS_ISCHAIN_POWER_ON, &device->state); + clear_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state); + clear_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state); + + /* clear group open state */ + clear_bit(FIMC_IS_GROUP_OPEN, &device->group_3aa.state); + clear_bit(FIMC_IS_GROUP_OPEN, &device->group_isp.state); + clear_bit(FIMC_IS_GROUP_OPEN, &device->group_dis.state); + + /* clear subdevice state */ + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->group_3aa.leader.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->group_isp.leader.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->group_dis.leader.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->drc.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->scc.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->dis.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->dnr.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->scp.state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &device->fd.state); + + clear_bit(FIMC_IS_SUBDEV_START, &device->group_3aa.leader.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->group_isp.leader.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->group_dis.leader.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->drc.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->scc.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->dis.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->dnr.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->scp.state); + clear_bit(FIMC_IS_SUBDEV_START, &device->fd.state); + + mutex_init(&device->mutex_state); + +#ifdef FW_DEBUG + debugfs_root = debugfs_create_dir(DEBUG_FS_ROOT_NAME, NULL); + if (debugfs_root) + mdbgd_ischain("debugfs %s is created\n", device, DEBUG_FS_ROOT_NAME); + + debugfs_file = debugfs_create_file(DEBUG_FS_FILE_NAME, S_IRUSR, + debugfs_root, device, &debug_fops); + if (debugfs_file) + mdbgd_ischain("debugfs %s is created\n", device, DEBUG_FS_FILE_NAME); +#endif + + return ret; +} + +int fimc_is_ischain_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx, + struct fimc_is_minfo *minfo) +{ + int ret = 0; + struct fimc_is_ishcain_mem *imemory; +#ifdef ENABLE_CLOCK_GATE + struct fimc_is_core *core; +#endif + BUG_ON(!device); + BUG_ON(!device->groupmgr); + BUG_ON(!vctx); + BUG_ON(!minfo); + + if (test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { + merr("already open", device); + ret = -EMFILE; + goto p_err; + } + +#ifndef RESERVED_MEM + if (device->instance == 0) { + /* 1. init memory */ + ret = fimc_is_ishcain_initmem(device); + if (ret) { + err("fimc_is_ishcain_initmem is fail(%d)\n", ret); + goto p_err; + } + } +#endif + + clear_bit(FIMC_IS_ISCHAIN_LOADED, &device->state); + clear_bit(FIMC_IS_ISCHAIN_POWER_ON, &device->state); + clear_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state); + clear_bit(FIMC_IS_ISHCAIN_START, &device->state); + clear_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state); + + /* 2. Init variables */ + memset(&device->cur_peri_ctl, 0, + sizeof(struct camera2_uctl)); + memset(&device->peri_ctls, 0, + sizeof(struct camera2_uctl)*SENSOR_MAX_CTL); + memset(&device->capability, 0, + sizeof(struct camera2_sm)); + + /* initial state, it's real apply to setting when opening*/ + device->margin_left = 0; + device->margin_right = 0; + device->margin_width = 0; + device->margin_top = 0; + device->margin_bottom = 0; + device->margin_height = 0; + device->dis_width = 0; + device->dis_height = 0; + device->chain0_width = 0; + device->chain0_height = 0; + device->chain1_width = 0; + device->chain1_height = 0; + device->chain2_width = 0; + device->chain2_height = 0; + device->chain3_width = 0; + device->chain3_height = 0; + device->crop_x = 0; + device->crop_y = 0; + device->crop_width = 0; + device->crop_height = 0; + device->setfile = ISS_SUB_SCENARIO_STILL_PREVIEW; + device->color_range = 0; + device->color_range |= (FIMC_IS_CRANGE_FULL << FIMC_IS_ISP_CRANGE_SHIFT); + device->color_range |= (FIMC_IS_CRANGE_FULL << FIMC_IS_SCC_CRANGE_SHIFT); + device->color_range |= (FIMC_IS_CRANGE_FULL << FIMC_IS_SCP_CRANGE_SHIFT); + device->dzoom_width = 0; + device->force_down = false; + device->sensor = NULL; + device->module = 0; + + imemory = &device->imemory; + imemory->base = minfo->base; + imemory->size = minfo->size; + imemory->vaddr_base = minfo->vaddr_base; + imemory->vaddr_curr = minfo->vaddr_curr; + imemory->fw_cookie = minfo->fw_cookie; + imemory->dvaddr = minfo->dvaddr; + imemory->kvaddr = minfo->kvaddr; + imemory->dvaddr_odc = minfo->dvaddr_odc; + imemory->kvaddr_odc = minfo->kvaddr_odc; + imemory->dvaddr_dis = minfo->dvaddr_dis; + imemory->kvaddr_dis = minfo->kvaddr_dis; + imemory->dvaddr_3dnr = minfo->dvaddr_3dnr; + imemory->kvaddr_3dnr = minfo->kvaddr_3dnr; + imemory->offset_region = (FIMC_IS_A5_MEM_SIZE - + ((device->instance + 1) * FIMC_IS_REGION_SIZE)); + imemory->dvaddr_region = imemory->dvaddr + imemory->offset_region; + imemory->kvaddr_region = imemory->kvaddr + imemory->offset_region; + imemory->is_region = (struct is_region *)imemory->kvaddr_region; + imemory->offset_shared = (u32)&imemory->is_region->shared[0] - + imemory->kvaddr; + imemory->dvaddr_shared = imemory->dvaddr + imemory->offset_shared; + imemory->kvaddr_shared = imemory->kvaddr + imemory->offset_shared; + device->is_region = imemory->is_region; + + fimc_is_group_open(device->groupmgr, &device->group_isp, GROUP_ID_ISP, + device->instance, vctx, device, fimc_is_ischain_isp_callback); + + /* subdev open */ + fimc_is_subdev_open(&device->drc, NULL, &init_drc_param.control); + fimc_is_subdev_open(&device->dis, NULL, &init_dis_param.control); + fimc_is_subdev_open(&device->dnr, NULL, &init_tdnr_param.control); + /* FD see only control.command not bypass */ + fimc_is_subdev_open(&device->fd, NULL, NULL); + + /* for mediaserver force close */ + ret = fimc_is_resource_get(device->resourcemgr, RESOURCE_TYPE_ISCHAIN); + if (ret) { + merr("fimc_is_resource_get is fail", device); + goto p_err; + } + + set_bit(FIMC_IS_ISCHAIN_OPEN, &device->state); + +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + core = (struct fimc_is_core *)device->interface->core; + fimc_is_clk_gate_lock_set(core, device->instance, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + } +#endif +p_err: + info("[ISC:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +int fimc_is_ischain_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + int refcount; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + struct fimc_is_core *core; +#ifdef CONFIG_COMPANION_USE + struct fimc_is_spi_gpio *spi_gpio; +#endif + BUG_ON(!device); + + groupmgr = device->groupmgr; + group = &device->group_isp; + leader = &group->leader; + queue = GET_SRC_QUEUE(vctx); + core = (struct fimc_is_core *)device->interface->core; + refcount = atomic_read(&vctx->video->refcount); +#ifdef CONFIG_COMPANION_USE + spi_gpio = &core->spi_gpio; +#endif + if (refcount < 0) { + merr("invalid ischain refcount", device); + ret = -ENODEV; + goto exit; + } + + if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { + merr("already close", device); + ret = -EMFILE; + goto exit; + } + +#ifdef ENABLE_CLOCK_GATE + core = (struct fimc_is_core *)device->interface->core; + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) { + fimc_is_clk_gate_lock_set(core, device->instance, true); + fimc_is_wrap_clk_gate_set(core, (1 << GROUP_ID_MAX) - 1, true); + } +#endif + /* 1. Stop all request */ + ret = fimc_is_ischain_isp_stop(device, leader, queue); + if (ret) + merr("fimc_is_ischain_isp_stop is fail", device); + + /* group close */ + ret = fimc_is_group_close(groupmgr, group); + if (ret) + merr("fimc_is_group_close is fail", device); + + /* subdev close */ + fimc_is_subdev_close(&device->drc); + fimc_is_subdev_close(&device->dis); + fimc_is_subdev_close(&device->dnr); + fimc_is_subdev_close(&device->fd); + + /* CLOSE_SENSOR */ + if (test_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state)) { + ret = fimc_is_itf_close(device); + if (ret) + merr("fimc_is_itf_close is fail", device); + + if(device->sensor->pdata->is_softlanding) + fimc_is_sensor_gpio_off_softlanding(device->sensor); + } + + /* for mediaserver force close */ + ret = fimc_is_resource_put(device->resourcemgr, RESOURCE_TYPE_ISCHAIN); + if (ret) { + merr("fimc_is_resource_put is fail", device); + goto exit; + } + + clear_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state); + clear_bit(FIMC_IS_ISCHAIN_OPEN, &device->state); +#ifdef CONFIG_COMPANION_USE + fimc_is_set_spi_config(spi_gpio, FIMC_IS_SPI_OUTPUT, true); +#endif + +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, device->instance, false); +#endif +exit: + pr_info("[ISC:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +static int fimc_is_ischain_init(struct fimc_is_device_ischain *device, + u32 module_id, + u32 group_id, + u32 video_id, + u32 flag) +{ + int ret = 0; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *sensor; +#ifdef CONFIG_COMPANION_USE + struct fimc_is_core *core + = (struct fimc_is_core *)platform_get_drvdata(device->pdev); + /* Workaround for Host to use ISP-SPI. Will be removed later.*/ +// struct fimc_is_spi_gpio *spi_gpio = &core->spi_gpio; +#endif + + BUG_ON(!device); + BUG_ON(!device->sensor); + + mdbgd_ischain("%s(module : %d)\n", device, __func__, module_id); + + sensor = device->sensor; + + if (test_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state)) { + mwarn("sensor is already open", device); + goto p_err; + } + + if (!test_bit(FIMC_IS_SENSOR_OPEN, &sensor->state)) { + merr("I2C gpio is not yet set", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_g_module(sensor, &module); + if (ret) { + merr("fimc_is_sensor_g_module is fail(%d)", device, ret); + goto p_err; + } + if (module->id != module_id) { + merr("module id is invalid(%d != %d)", device, module->id, module_id); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + /* sensor instance means flite channel */ + if(sensor->instance == 0) { + /* Load calibration data from sensor */ + module->ext.sensor_con.cal_address = FIMC_IS_CAL_START_ADDR; +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + ret = fimc_is_ischain_loadcalb_eeprom(device, NULL, SENSOR_POSITION_REAR); +#else + ret = fimc_is_ischain_loadcalb(device, NULL); +#endif + if (ret) { + err("loadcalb fail, load default caldata\n"); + } + } else { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + module->ext.sensor_con.cal_address = FIMC_IS_CAL_START_ADDR_FRONT; + ret = fimc_is_ischain_loadcalb_eeprom(device, NULL, SENSOR_POSITION_FRONT); + if (ret) { + err("loadcalb fail, load default caldata\n"); + } +#else + module->ext.sensor_con.cal_address = 0; +#endif + } + } + +#ifdef CONFIG_COMPANION_USE + if(core->companion->companion_status != FIMC_IS_COMPANION_IDLE) { + pr_info("[ISC:D:%d] fimc_is_companion_wait wait(%d)\n", device->instance,core->companion->companion_status); + fimc_is_companion_wait(core->companion); + pr_info("[ISC:D:%d] fimc_is_companion_wait wake up(%d)\n", device->instance,core->companion->companion_status); + } + + fimc_is_s_int_comb_isp(core, false, INTMR2_INTMCIS22); + +#if 0 + /* FW loading of peripheral device */ + if ((module->position == SENSOR_POSITION_REAR) + && !test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + // Workaround for Host to use ISP-SPI. Will be removed later. + /* set pin output for Host to use SPI*/ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_FUNC, FUNC_OUTPUT)); + + fimc_is_set_spi_config(spi_gpio, FIMC_IS_SPI_FUNC, false); + + if (fimc_is_comp_is_valid(core) == 0) { + fimc_is_power_binning(core); + ret = fimc_is_comp_loadfirm(core); + if (ret) { + err("fimc_is_comp_loadfirm() fail"); + goto p_err; + } + ret = fimc_is_comp_loadcal(core); + if (ret) { + err("fimc_is_comp_loadcal() fail"); + } + ret = fimc_is_comp_loadsetf(core); + if (ret) { + err("fimc_is_comp_loadsetf() fail"); + goto p_err; + } + } else { + module->ext.companion_con.product_name + = COMPANION_NAME_NOTHING; + } + // Workaround for Host to use ISP-SPI. Will be removed later. + /* Set SPI pins to low before changing pin function */ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_sclk, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_miso, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_mois, + PINCFG_PACK(PINCFG_TYPE_DAT, 0)); + + /* Set pin function for A5 to use SPI */ + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + } +#endif +#endif + + ret = fimc_is_itf_enum(device); + if (ret) { + err("enum fail"); + goto p_err; + } + +#if (FW_HAS_SENSOR_MODE_CMD) + ret = fimc_is_itf_open(device, ((sensor->mode << 16) | (module_id & 0xFFFF)), + group_id, flag, &module->ext); +#else + ret = fimc_is_itf_open(device, module_id, group_id, flag, &module->ext); +#endif + if (ret) { + merr("open fail", device); + goto p_err; + } + + ret = fimc_is_itf_setfile(device, module->setfile_name); + if (ret) { + merr("setfile fail", device); + goto p_err; + } + + if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + ret = fimc_is_itf_stream_off(device); + if (ret) { + merr("streamoff fail", device); + goto p_err; + } + } + + ret = fimc_is_itf_init_process_stop(device); + if (ret) { + merr("fimc_is_itf_init_process_stop is fail", device); + goto p_err; + } + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + measure_period(&device->group_3aa.time, 1); + measure_period(&device->group_isp.time, 1); + measure_period(&device->group_dis.time, 1); + } else { + measure_period(&device->group_3aa.time, 66); + measure_period(&device->group_isp.time, 66); + measure_period(&device->group_dis.time, 66); + } +#endif +#endif + + device->module = module_id; + set_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state); + +p_err: + return ret; +} + +int fimc_is_ischain_init_wrap(struct fimc_is_device_ischain *device, + u32 input) +{ + int ret = 0; + u32 flag = 0; + u32 group_id = 0; + u32 module, ssx_vindex, tax_vindex, rep_stream; + struct fimc_is_core *core = NULL; + struct fimc_is_device_sensor *sensor = NULL; + + BUG_ON(!device); + + mdbgd_ischain("%s(input : %08X)\n", device, __func__, input); + + core = (struct fimc_is_core *)platform_get_drvdata(device->pdev); + + module = input & MODULE_MASK; + ssx_vindex = (input & SSX_VINDEX_MASK) >> SSX_VINDEX_SHIFT; + tax_vindex = (input & TAX_VINDEX_MASK) >> TAX_VINDEX_SHIFT; + rep_stream = (input & REPROCESSING_MASK) >> REPROCESSING_SHIFT; + + /* 1. checking 3ax group to connect */ + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) + || GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1)) + group_id = device->group_3aa.id; + else + group_id = GROUP_ID_3A0; + + /* 2. checking sensor video node to connect */ + if (ssx_vindex == FIMC_IS_VIDEO_SS0_NUM) { + sensor = &core->sensor[0]; + info("[ISP:V:%d] <-> [SS0:V:0]\n", device->instance); + } else if (ssx_vindex == FIMC_IS_VIDEO_SS1_NUM) { + sensor = &core->sensor[1]; + info("[ISP:V:%d] <-> [SS1:V:0]\n", device->instance); + } else { + sensor = NULL; + merr("sensor is not matched", device); + ret = -EINVAL; + goto p_err; + } + + /* 3. checking reprocessing stream */ + if (rep_stream) { + flag = REPROCESSING_FLAG; + set_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state); + } else { + /* connect to sensor if it's not a reprocessing stream */ + sensor->ischain = device; + flag = 0; + } + + /* 4. init variable */ + device->instance_sensor = sensor->instance; + device->sensor = sensor; + + info("%s(%s) : module(%d), flag(%08X), group_id(%d)\n", __func__, + (rep_stream ? "Reprocessing" : "Preview"), module, flag, group_id); + + /* 5. init ischain */ + ret = fimc_is_ischain_init(device, module, group_id, tax_vindex, flag); + if (ret) + merr("fimc_is_device_init(%d, %d, %d) is fail", device, module, group_id, rep_stream); + +p_err: + return ret; +} + +static int fimc_is_ischain_s_color_range(struct fimc_is_device_ischain *device, + u32 color_range, u32 *lindex, u32 *hindex, u32 *indexes) +{ + int ret = 0; + u32 crange; + struct param_otf_output *otf_output; + struct param_scaler_imageeffect *scaler_effect; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + /* + * Color Range + * 0 : Wide range + * 1 : Narrow range + */ + otf_output = fimc_is_itf_g_param(device, NULL, PARAM_ISP_OTF_OUTPUT); + crange = (color_range & FIMC_IS_ISP_CRANGE_MASK) >> FIMC_IS_ISP_CRANGE_SHIFT; + if (crange) + otf_output->format = OTF_OUTPUT_FORMAT_YUV444_TRUNCATED; + else + otf_output->format = OTF_OUTPUT_FORMAT_YUV444; + *lindex |= LOWBIT_OF(PARAM_ISP_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_ISP_OTF_OUTPUT); + + info("[ISC:D:%d] ISP color range: %s\n", device->instance, + (crange ? "limited" : "full")); + + scaler_effect = fimc_is_itf_g_param(device, NULL, PARAM_SCALERC_IMAGE_EFFECT); + crange = (color_range & FIMC_IS_SCC_CRANGE_MASK) >> FIMC_IS_SCC_CRANGE_SHIFT; + if (crange) + scaler_effect->yuv_range = SCALER_OUTPUT_YUV_RANGE_NARROW; + else + scaler_effect->yuv_range = SCALER_OUTPUT_YUV_RANGE_FULL; + *lindex |= LOWBIT_OF(PARAM_SCALERC_IMAGE_EFFECT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_IMAGE_EFFECT); + (*indexes)++; + + info("[ISC:D:%d] SCC color range: %s\n", device->instance, + (crange ? "limited" : "full")); + + scaler_effect = fimc_is_itf_g_param(device, NULL, PARAM_SCALERP_IMAGE_EFFECT); + crange = (color_range & FIMC_IS_SCP_CRANGE_MASK) >> FIMC_IS_SCP_CRANGE_SHIFT; + if (crange) + scaler_effect->yuv_range = SCALER_OUTPUT_YUV_RANGE_NARROW; + else + scaler_effect->yuv_range = SCALER_OUTPUT_YUV_RANGE_FULL; + *lindex |= LOWBIT_OF(PARAM_SCALERP_IMAGE_EFFECT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_IMAGE_EFFECT); + (*indexes)++; + + info("[ISC:D:%d] SCP color range: %s\n", device->instance, + (crange ? "limited" : "full")); + + return ret; +} + +static int fimc_is_ischain_s_sensor(struct fimc_is_device_ischain *device, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct sensor_param *sensor_param; + size_t sensor_width, sensor_height; + u32 framerate; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + BUG_ON(!device->sensor); + + sensor_param = &device->is_region->parameter.sensor; + sensor_width = fimc_is_sensor_g_width(device->sensor); + sensor_height = fimc_is_sensor_g_height(device->sensor); + framerate = fimc_is_sensor_g_framerate(device->sensor); + +#ifdef FIXED_FPS_DEBUG + sensor_param->config.framerate = FIXED_FPS_VALUE; +#else + sensor_param->config.framerate = framerate; +#endif + if (device->sensor->min_target_fps > 0) + sensor_param->config.min_target_fps = device->sensor->min_target_fps; + if (device->sensor->max_target_fps > 0) + sensor_param->config.max_target_fps = device->sensor->max_target_fps; + if (device->sensor->scene_mode >= AA_SCENE_MODE_UNSUPPORTED) + sensor_param->config.scene_mode = device->sensor->scene_mode; + + *lindex |= LOWBIT_OF(PARAM_SENSOR_CONFIG); + *hindex |= HIGHBIT_OF(PARAM_SENSOR_CONFIG); + (*indexes)++; + + sensor_param->dma_output.width = sensor_width; + sensor_param->dma_output.height = sensor_height; + + *lindex |= LOWBIT_OF(PARAM_SENSOR_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SENSOR_DMA_OUTPUT); + (*indexes)++; + + return ret; +} + +static int fimc_is_ischain_s_3aa_size(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 *input_crop, + u32 *output_crop, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct fimc_is_group *group_3aa; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + struct param_control *taa_control; + struct param_otf_input *taa_otf_input; + struct param_dma_input *taa_dma_input; + size_t sensor_width, sensor_height; + size_t bns_width, bns_height; + size_t total_width, total_height; + u32 binning; + u32 bns_binning; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + BUG_ON(!device->sensor); + + sensor_width = fimc_is_sensor_g_width(device->sensor); + sensor_height = fimc_is_sensor_g_height(device->sensor); + bns_width = fimc_is_sensor_g_bns_width(device->sensor); + bns_height = fimc_is_sensor_g_bns_height(device->sensor); + binning = fimc_is_sensor_g_bratio(device->sensor); + bns_binning = fimc_is_sensor_g_bns_ratio(device->sensor); + total_width = 2 * input_crop[0] + input_crop[2]; + total_height = 2 * input_crop[1] + input_crop[3]; + + /* check crop size */ + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->group_3aa.state)) { + if (bns_width <= total_width) { + merr("crop width(%d) is bigger than input width(%d)\n", + device, total_width, bns_width); + goto p_err; + } + + if (bns_height <= total_height) { + merr("crop height(%d) is bigger than input height(%d)\n", + device, total_height, bns_height); + goto p_err; + } + + taa_otf_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_OTF_INPUT); + taa_otf_input->cmd = OTF_INPUT_COMMAND_ENABLE; + + taa_dma_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA1_INPUT); + taa_dma_input->cmd = DMA_INPUT_COMMAND_DISABLE; + } else { + if (sensor_width <= total_width) { + merr("crop width(%d) is bigger than input width(%d)\n", + device, total_width, sensor_width); + goto p_err; + } + + if (sensor_height <= total_height) { + merr("crop height(%d) is bigger than input height(%d)\n", + device, total_height, sensor_height); + goto p_err; + } + + taa_otf_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_OTF_INPUT); + taa_otf_input->cmd = OTF_INPUT_COMMAND_DISABLE; + + taa_dma_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA1_INPUT); + taa_dma_input->cmd = DMA_INPUT_COMMAND_BUF_MNGR; + } + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) + group_3aa = &device->group_3aa; + else + group_3aa = &device->group_isp; + leader = &group_3aa->leader; + queue = GET_LEADER_QUEUE(leader); + if (!queue) { + merr("get queue fail", device); + ret = -EINVAL; + goto p_err; + } + + taa_control = fimc_is_itf_g_param(device, frame, PARAM_3AA_CONTROL); + taa_control->cmd = CONTROL_COMMAND_START; + taa_control->bypass = CONTROL_BYPASS_DISABLE; + taa_control->run_mode = 1; + *lindex |= LOWBIT_OF(PARAM_3AA_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_3AA_CONTROL); + (*indexes)++; + + /* OTF */ + taa_otf_input->width = sensor_width; + taa_otf_input->height = sensor_height; + taa_otf_input->bayer_crop_enable = 1; + taa_otf_input->bayer_crop_offset_x = input_crop[0]; + taa_otf_input->bayer_crop_offset_y = input_crop[1]; + taa_otf_input->bayer_crop_width = input_crop[2]; + taa_otf_input->bayer_crop_height = input_crop[3]; + taa_otf_input->sensor_binning_ratio_x = binning; + taa_otf_input->sensor_binning_ratio_y = binning; + + taa_otf_input->bns_binning_enable = 1; + taa_otf_input->bns_binning_ratio_x = bns_binning; + taa_otf_input->bns_binning_ratio_y = bns_binning; + taa_otf_input->bns_margin_left = 0; + taa_otf_input->bns_margin_top = 0; + taa_otf_input->bns_output_width = bns_width; + taa_otf_input->bns_output_height = bns_height; + + taa_otf_input->format = OTF_INPUT_FORMAT_BAYER; + taa_otf_input->bitwidth = OTF_INPUT_BIT_WIDTH_10BIT; + taa_otf_input->order = OTF_INPUT_ORDER_BAYER_GR_BG; + taa_otf_input->frametime_min = 0; + taa_otf_input->frametime_max = 1000000; + *lindex |= LOWBIT_OF(PARAM_3AA_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_OTF_INPUT); + (*indexes)++; + + /* M2M */ + taa_dma_input->width = sensor_width; + taa_dma_input->height = sensor_height; + taa_dma_input->bayer_crop_enable = 1; + taa_dma_input->bayer_crop_offset_x = input_crop[0]; + taa_dma_input->bayer_crop_offset_y = input_crop[1]; + taa_dma_input->bayer_crop_width = input_crop[2]; + taa_dma_input->bayer_crop_height = input_crop[3]; + + if (!GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + taa_dma_input->bds_out_enable = ISP_BDS_COMMAND_ENABLE; + taa_dma_input->bds_out_width = output_crop[2]; + taa_dma_input->bds_out_height = output_crop[3]; + } + + taa_dma_input->user_min_frame_time = 0; + taa_dma_input->user_max_frame_time = 1000000; + + if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR10 || + queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR12) { + taa_dma_input->format = DMA_INPUT_FORMAT_BAYER_PACKED12; + } else if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR16) { + taa_dma_input->format = DMA_INPUT_FORMAT_BAYER; + } else { + merr("Invalid bayer format(%d)", device, queue->framecfg.format.pixelformat); + ret = -EINVAL; + goto p_err; + } + + taa_dma_input->bitwidth = DMA_INPUT_BIT_WIDTH_10BIT; + taa_dma_input->order = DMA_INPUT_ORDER_GR_BG; + taa_dma_input->plane = 1; + taa_dma_input->buffer_number = 0; + taa_dma_input->buffer_address = 0; + taa_dma_input->sensor_binning_ratio_x = binning; + taa_dma_input->sensor_binning_ratio_y = binning; + /* + * hidden spec + * [0] : sensor size is dma input size + * [X] : sensor size is reserved field + */ + taa_dma_input->reserved[1] = 0; + taa_dma_input->reserved[2] = 0; + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA1_INPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA1_INPUT); + (*indexes)++; + +p_err: + return ret; +} + +static int fimc_is_ischain_s_chain0_size(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 width, + u32 height, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct fimc_is_group *group_isp; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + struct param_otf_input *otf_input; + struct param_dma_input *dma_input; + struct param_otf_output *otf_output; + struct param_scaler_input_crop *input_crop; + u32 chain0_width, chain0_height; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + chain0_width = width; + chain0_height = height; + + group_isp = &device->group_isp; + if (!group_isp) { + merr("get gourp_isp fail", device); + ret = -EINVAL; + goto p_err; + } + + leader = &group_isp->leader; + if (!leader) { + merr("get leader fail", device); + ret = -EINVAL; + goto p_err; + } + + queue = GET_LEADER_QUEUE(leader); + if (!queue) { + merr("get queue fail", device); + ret = -EINVAL; + goto p_err; + } + + mdbgd_ischain("request chain0 size : %dx%d\n", device, chain0_width, chain0_height); + + /* ISP */ + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + dma_input = fimc_is_itf_g_param(device, frame, PARAM_ISP_VDMA1_INPUT); + dma_input->cmd = DMA_INPUT_COMMAND_BUF_MNGR; + dma_input->width = chain0_width; + dma_input->height = chain0_height; + dma_input->bitwidth = DMA_INPUT_BIT_WIDTH_10BIT; + + if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR10 || + queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR12) { + dma_input->format = DMA_INPUT_FORMAT_BAYER_PACKED12; + } else if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR16) { + dma_input->format = DMA_INPUT_FORMAT_BAYER; + } else { + merr("Invalid bayer format(%d)", device, queue->framecfg.format.pixelformat); + ret = -EINVAL; + goto p_err; + } + + *lindex |= LOWBIT_OF(PARAM_ISP_VDMA1_INPUT); + *hindex |= HIGHBIT_OF(PARAM_ISP_VDMA1_INPUT); + (*indexes)++; + } + + otf_output = fimc_is_itf_g_param(device, frame, PARAM_ISP_OTF_OUTPUT); + otf_output->cmd = OTF_OUTPUT_COMMAND_ENABLE; + otf_output->width = chain0_width; + otf_output->height = chain0_height; + otf_output->format = OTF_OUTPUT_FORMAT_YUV444; + otf_output->bitwidth = OTF_OUTPUT_BIT_WIDTH_12BIT; + otf_output->order = OTF_INPUT_ORDER_BAYER_GR_BG; + *lindex |= LOWBIT_OF(PARAM_ISP_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_ISP_OTF_OUTPUT); + (*indexes)++; + + /* DRC */ + otf_input = fimc_is_itf_g_param(device, frame, PARAM_DRC_OTF_INPUT); + otf_input->cmd = OTF_INPUT_COMMAND_ENABLE; + otf_input->width = chain0_width; + otf_input->height = chain0_height; + *lindex |= LOWBIT_OF(PARAM_DRC_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_DRC_OTF_INPUT); + (*indexes)++; + + otf_output = fimc_is_itf_g_param(device, frame, PARAM_DRC_OTF_OUTPUT); + otf_output->cmd = OTF_OUTPUT_COMMAND_ENABLE; + otf_output->width = chain0_width; + otf_output->height = chain0_height; + *lindex |= LOWBIT_OF(PARAM_DRC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_DRC_OTF_OUTPUT); + (*indexes)++; + + /* SCC */ + otf_input = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OTF_INPUT); + otf_input->cmd = OTF_INPUT_COMMAND_ENABLE; + otf_input->width = chain0_width; + otf_input->height = chain0_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_INPUT); + (*indexes)++; + + /* SCC CROP */ + input_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_INPUT_CROP); + input_crop->pos_x = 0; + input_crop->pos_y = 0; + input_crop->crop_width = chain0_width; + input_crop->crop_height = chain0_height; + input_crop->in_width = chain0_width; + input_crop->in_height = chain0_height; + + *lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + (*indexes)++; + + device->bds_width = width; + device->bds_height = height; + +p_err: + return ret; +} + +static int fimc_is_ischain_s_chain1_size(struct fimc_is_device_ischain *device, + u32 width, u32 height, u32 *lindex, u32 *hindex, u32 *indexes) +{ + int ret = 0; + struct scalerc_param *scc_param; + struct odc_param *odc_param; + struct dis_param *dis_param; + u32 chain1_width, chain1_height; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) + return 0; + + scc_param = &device->is_region->parameter.scalerc; + odc_param = &device->is_region->parameter.odc; + dis_param = &device->is_region->parameter.dis; + chain1_width = width; + chain1_height = height; + + mdbgd_ischain("current chain1 size : %dx%d\n", device, + device->chain1_width, device->chain1_height); + mdbgd_ischain("request chain1 size : %dx%d\n", device, + chain1_width, chain1_height); + + if (!chain1_width) { + err("chain1 width is zero"); + ret = -EINVAL; + goto exit; + } + + if (!chain1_height) { + err("chain1 height is zero"); + ret = -EINVAL; + goto exit; + } + + /* SCC OUTPUT */ + scc_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; + scc_param->input_crop.out_width = chain1_width; + scc_param->input_crop.out_height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + (*indexes)++; + + 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 = chain1_width; + scc_param->output_crop.crop_height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + (*indexes)++; + + scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + scc_param->otf_output.width = chain1_width; + scc_param->otf_output.height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + (*indexes)++; + + /* ODC */ + odc_param->otf_input.width = chain1_width; + odc_param->otf_input.height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_ODC_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_ODC_OTF_INPUT); + (*indexes)++; + + odc_param->otf_output.width = chain1_width; + odc_param->otf_output.height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_ODC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_ODC_OTF_OUTPUT); + (*indexes)++; + + /* DIS INPUT */ + dis_param->otf_input.width = chain1_width; + dis_param->otf_input.height = chain1_height; + *lindex |= LOWBIT_OF(PARAM_DIS_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_DIS_OTF_INPUT); + (*indexes)++; + +exit: + return ret; +} + +static int fimc_is_ischain_s_chain2_size(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 width, + u32 height, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_otf_input *otf_input; + struct param_otf_output *otf_output; + struct param_dma_output *dma_output; + u32 chain2_width, chain2_height; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) + return 0; + + mdbgd_ischain("request chain2 size : %dx%d\n", device, width, height); + mdbgd_ischain("current chain2 size : %dx%d\n", + device, device->chain2_width, device->chain2_height); + + /* CALCULATION */ + chain2_width = width; + chain2_height = height; + + /* DIS OUTPUT */ + otf_output = fimc_is_itf_g_param(device, frame, PARAM_DIS_OTF_OUTPUT); + otf_output->width = chain2_width; + otf_output->height = chain2_height; + *lindex |= LOWBIT_OF(PARAM_DIS_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_DIS_OTF_OUTPUT); + (*indexes)++; + + otf_input = fimc_is_itf_g_param(device, frame, PARAM_TDNR_OTF_INPUT); + otf_input->width = chain2_width; + otf_input->height = chain2_height; + *lindex |= LOWBIT_OF(PARAM_TDNR_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_TDNR_OTF_INPUT); + (*indexes)++; + + dma_output = fimc_is_itf_g_param(device, frame, PARAM_TDNR_DMA_OUTPUT); + dma_output->width = chain2_width; + dma_output->height = chain2_height; + *lindex |= LOWBIT_OF(PARAM_TDNR_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_TDNR_DMA_OUTPUT); + (*indexes)++; + + otf_output = fimc_is_itf_g_param(device, frame, PARAM_TDNR_OTF_OUTPUT); + otf_output->width = chain2_width; + otf_output->height = chain2_height; + *lindex |= LOWBIT_OF(PARAM_TDNR_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_TDNR_OTF_OUTPUT); + (*indexes)++; + + otf_input = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_OTF_INPUT); + otf_input->width = chain2_width; + otf_input->height = chain2_height; + *lindex |= LOWBIT_OF(PARAM_SCALERP_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_OTF_INPUT); + (*indexes)++; + + return ret; +} + +/** + Utility function to adjust output crop size based on the + H/W limitation of SCP scaling. + output_crop_w and output_crop_h are call-by reference parameter, + which contain intended cropping size. Adjusted size will be stored on + those parameters when this function returns. + */ +static int fimc_is_ischain_scp_adjust_crop(struct fimc_is_device_ischain *device, + struct scalerp_param *scp_param, + u32 *output_crop_w, u32 *output_crop_h) +{ + int changed = 0; + + if (*output_crop_w > scp_param->otf_input.width * 4) { + mwarn("Cannot be scaled up beyond 4 times(%d -> %d)", + device, scp_param->otf_input.width, *output_crop_w); + *output_crop_w = scp_param->otf_input.width * 4; + changed |= 0x01; + } + + if (*output_crop_h > scp_param->otf_input.height * 4) { + mwarn("Cannot be scaled up beyond 4 times(%d -> %d)", + device, scp_param->otf_input.height, *output_crop_h); + *output_crop_h = scp_param->otf_input.height * 4; + changed |= 0x02; + } + + if (*output_crop_w < (scp_param->otf_input.width + 15) / 16) { + mwarn("Cannot be scaled down beyond 1/16 times(%d -> %d)", + device, scp_param->otf_input.width, *output_crop_w); + *output_crop_w = (scp_param->otf_input.width + 15) / 16; + changed |= 0x10; + } + + if (*output_crop_h < (scp_param->otf_input.height + 15) / 16) { + mwarn("Cannot be scaled down beyond 1/16 times(%d -> %d)", + device, scp_param->otf_input.height, *output_crop_h); + *output_crop_h = (scp_param->otf_input.height + 15) / 16; + changed |= 0x20; + } + + return changed; +} + +static int fimc_is_ischain_s_chain3_size(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + u32 width, + u32 height, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_scaler_input_crop *input_crop; + struct param_scaler_output_crop *output_crop; + struct param_otf_input *otf_input; + struct param_otf_output *otf_output; + struct param_dma_output *dma_output; + struct fimc_is_video_ctx *vctx; + struct fimc_is_queue *queue; + struct scalerp_param *scp_param; + u32 chain2_width, chain2_height; + u32 chain3_width, chain3_height; + u32 scp_crop_width, scp_crop_height; + u32 scp_crop_x, scp_crop_y; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) + return 0; + + /* Adjust output crop to prevent exceeding SCP limitation */ + scp_param = &device->is_region->parameter.scalerp; + fimc_is_ischain_scp_adjust_crop(device, scp_param, &width, &height); + + vctx = device->scp.vctx; + queue = vctx->q_dst; + + chain2_width = device->chain2_width; + chain2_height = device->chain2_height; + chain3_width = width; + chain3_height = height; + + scp_crop_x = 0; + scp_crop_y = 0; + scp_crop_width = chain2_width; + scp_crop_height = chain2_height; + + mdbgd_ischain("request chain3 size : %dx%d\n", device, width, height); + mdbgd_ischain("current chain3 size : %dx%d\n", + device, device->chain3_width, device->chain3_height); + + /* SCP */ + input_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_INPUT_CROP); + input_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + input_crop->pos_x = scp_crop_x; + input_crop->pos_y = scp_crop_y; + input_crop->crop_width = scp_crop_width; + input_crop->crop_height = scp_crop_height; + input_crop->in_width = chain2_width; + input_crop->in_height = chain2_height; + input_crop->out_width = chain3_width; + input_crop->out_height = chain3_height; + *lindex |= LOWBIT_OF(PARAM_SCALERP_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_INPUT_CROP); + (*indexes)++; + + /* + * scaler can't apply stride to each plane, only y plane. + * basically cb, cr plane should be half of y plane, + * and it's automatically set + * + * 3 plane : all plane should be 8 or 16 stride + * 2 plane : y plane should be 32, 16 stride, others should be half stride of y + * 1 plane : all plane should be 8 stride + */ + /* + * limitation of output_crop.pos_x and pos_y + * YUV422 3P, YUV420 3P : pos_x and pos_y should be x2 + * YUV422 1P : pos_x should be x2 + */ + output_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_OUTPUT_CROP); + if (queue->framecfg.width_stride[0]) { + output_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + output_crop->pos_x = 0; + output_crop->pos_y = 0; + output_crop->crop_width = chain3_width + queue->framecfg.width_stride[0]; + output_crop->crop_height = chain3_height; + *lindex |= LOWBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + (*indexes)++; + } else { + output_crop->cmd = SCALER_CROP_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + (*indexes)++; + } + + otf_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_OTF_OUTPUT); + otf_output->width = chain3_width; + otf_output->height = chain3_height; + *lindex |= LOWBIT_OF(PARAM_SCALERP_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_OTF_OUTPUT); + (*indexes)++; + + dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_DMA_OUTPUT); + dma_output->width = chain3_width; + dma_output->height = chain3_height; + *lindex |= LOWBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + (*indexes)++; + + /* FD */ + otf_input = fimc_is_itf_g_param(device, frame, PARAM_FD_OTF_INPUT); + otf_input->width = chain3_width; + otf_input->height = chain3_height; + *lindex |= LOWBIT_OF(PARAM_FD_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_FD_OTF_INPUT); + (*indexes)++; + + return ret; +} + +static int fimc_is_ischain_s_path(struct fimc_is_device_ischain *device, + u32 *lindex, u32 *hindex, u32 *indexes) +{ + int ret = 0; + struct isp_param *isp_param; + struct drc_param *drc_param; + struct scalerc_param *scc_param; + struct odc_param *odc_param; + struct dis_param *dis_param; + struct tdnr_param *dnr_param; + struct scalerp_param *scp_param; + + BUG_ON(!device); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + isp_param = &device->is_region->parameter.isp; + drc_param = &device->is_region->parameter.drc; + scc_param = &device->is_region->parameter.scalerc; + odc_param = &device->is_region->parameter.odc; + dis_param = &device->is_region->parameter.dis; + dnr_param = &device->is_region->parameter.tdnr; + scp_param = &device->is_region->parameter.scalerp; + + isp_param->control.cmd = CONTROL_COMMAND_START; + isp_param->control.bypass = CONTROL_BYPASS_DISABLE; + isp_param->control.run_mode = 1; + *lindex |= LOWBIT_OF(PARAM_ISP_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_ISP_CONTROL); + (*indexes)++; + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + (*indexes)++; + + odc_param->control.cmd = CONTROL_COMMAND_STOP; + *lindex |= LOWBIT_OF(PARAM_ODC_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_ODC_CONTROL); + (*indexes)++; + + fimc_is_subdev_drc_bypass(device, &drc_param->control, lindex, hindex, indexes); + fimc_is_subdev_dis_stop(device, dis_param, lindex, hindex, indexes); + fimc_is_subdev_dnr_stop(device, &dnr_param->control, lindex, hindex, indexes); + + scp_param->control.cmd = CONTROL_COMMAND_STOP; + *lindex |= LOWBIT_OF(PARAM_SCALERP_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_CONTROL); + (*indexes)++; + } else { + scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + (*indexes)++; + + odc_param->control.cmd = CONTROL_COMMAND_START; + *lindex |= LOWBIT_OF(PARAM_ODC_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_ODC_CONTROL); + (*indexes)++; + + fimc_is_subdev_drc_bypass(device, &drc_param->control, lindex, hindex, indexes); + fimc_is_subdev_dis_bypass(device, dis_param, lindex, hindex, indexes); + fimc_is_subdev_dnr_bypass(device, &dnr_param->control, lindex, hindex, indexes); + + scp_param->control.cmd = CONTROL_COMMAND_START; +#ifdef SCALER_PARALLEL_MODE + scp_param->otf_input.scaler_path_sel = OTF_INPUT_PARAL_PATH; +#else + scp_param->otf_input.scaler_path_sel = OTF_INPUT_SERIAL_PATH; +#endif + *lindex |= LOWBIT_OF(PARAM_SCALERP_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_CONTROL); + (*indexes)++; + } + + return ret; +} + +#ifdef ENABLE_SETFILE +static int fimc_is_ischain_chg_setfile(struct fimc_is_device_ischain *device, + unsigned int setfile) +{ + int ret = 0; + u32 group_id = 0; + struct fimc_is_group *group_isp; + unsigned int save_setfile; + + BUG_ON(!device); + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->group_3aa.state)) { + mwarn("Changing setfile index is not supportd at OTF group\n", device); + goto p_err; + } + + group_isp = &device->group_isp; + + if (group_isp->smp_shot.count < 1) { + mwarn("group%d is working(%d), setfile change is fail", + device, group_isp->id, group_isp->smp_shot.count); + goto p_err; + } + + group_id |= GROUP_ID(device->group_3aa.id); + group_id |= GROUP_ID(device->group_isp.id); + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group_id & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group_id = GROUP_ID(GROUP_ID_3A0); + + if (test_bit(FIMC_IS_GROUP_ACTIVE, &device->group_dis.state)) + group_id |= GROUP_ID(device->group_dis.id); + + save_setfile = device->setfile; + device->setfile = setfile; + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop fail", device); + device->setfile = save_setfile; + goto p_err; + } + + ret = fimc_is_itf_a_param(device, group_id); + if (ret) { + merr("fimc_is_itf_a_param is fail", device); + device->setfile = save_setfile; + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_start(device, group_id); + if (ret) { + merr("fimc_is_itf_process_start fail", device); + device->setfile = save_setfile; + ret = -EINVAL; + goto p_err; + } + + minfo("[ISC:D] setfile: 0x%08X\n", device, device->setfile); + +p_err: + return ret; +} +#endif + +#ifdef SCALER_CROP_DZOOM +static int fimc_is_ischain_s_dzoom(struct fimc_is_device_ischain *this, + u32 crop_x, u32 crop_y, u32 crop_width) +{ + int ret = 0; + u32 indexes, lindex, hindex; + u32 chain0_width, chain0_height; + u32 temp_width, temp_height, input_width; + u32 zoom_input, zoom_target; + u32 crop_cx, crop_cy, crop_cwidth, crop_cheight; + struct scalerc_param *scc_param; + u32 chain0_ratio, preview_ratio; + u32 chain0_ratio_width, chain0_ratio_height; +#ifdef USE_ADVANCED_DZOOM + u32 zoom_pre, zoom_post, zoom_pre_max; + u32 crop_px, crop_py, crop_pwidth, crop_pheight; + u32 chain1_width, chain1_height; + u32 chain2_width, chain2_height; + u32 chain3_width, chain3_height; + u32 scp_input_width, scp_input_height; + struct scalerp_param *scp_param; + + scc_param = &this->is_region->parameter.scalerc; + scp_param = &this->is_region->parameter.scalerp; + indexes = lindex = hindex = 0; + chain0_width = this->chain0_width; + chain0_height = this->chain0_height; + chain1_width = this->chain1_width; + chain1_height = this->chain1_height; + chain2_width = this->chain2_width; + chain2_height = this->chain2_height; + chain3_width = this->chain3_width; + chain3_height = this->chain3_height; +#ifdef PRINT_DZOOM + printk(KERN_INFO "chain0(%d, %d), chain1(%d, %d), chain2(%d, %d)\n", + chain0_width, chain0_height, + chain1_width, chain1_height, + chain2_width, chain2_height); +#endif +#else + scc_param = &this->is_region->parameter.scalerc; + indexes = lindex = hindex = 0; + chain0_width = this->chain0_width; + chain0_height = this->chain0_height; +#ifdef PRINT_DZOOM + printk(KERN_INFO "chain0(%d, %d)\n", chain0_width, chain0_height); +#endif +#endif + + /* CHECK */ + input_width = crop_width; + temp_width = crop_width + (crop_x<<1); + if (temp_width != chain0_width) { + err("input width is not valid(%d != %d)", + temp_width, chain0_width); + /* if invalid input come, dzoom is not apply and + shot command is sent to firmware */ + ret = 0; + goto exit; + } + + chain0_ratio_width = chain0_width; + chain0_ratio_height = chain0_height; + +#ifdef USE_ADVANCED_DZOOM + zoom_input = (chain0_ratio_width * 1000) / crop_width; + zoom_pre_max = (chain0_ratio_width * 1000) / chain1_width; + + if (zoom_pre_max < 1000) + zoom_pre_max = 1000; + +#ifdef PRINT_DZOOM + printk(KERN_INFO "zoom input : %d, premax-zoom : %d\n", + zoom_input, zoom_pre_max); +#endif + + if (test_bit(FIMC_IS_SUBDEV_START, &this->dis.state)) + zoom_target = (zoom_input * 91 + 34000) / 125; + else + zoom_target = zoom_input; + + if (zoom_target > zoom_pre_max) { + zoom_pre = zoom_pre_max; + zoom_post = (zoom_target * 1000) / zoom_pre; + } else { + zoom_pre = zoom_target; + zoom_post = 1000; + } + + /* CALCULATION */ + temp_width = (chain0_ratio_width * 1000) / zoom_pre; + temp_height = (chain0_ratio_height * 1000) / zoom_pre; + crop_cx = (chain0_width - temp_width)>>1; + crop_cy = (chain0_height - temp_height)>>1; + crop_cwidth = chain0_width - (crop_cx<<1); + crop_cheight = chain0_height - (crop_cy<<1); + + scc_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; + scc_param->input_crop.pos_x = crop_cx; + scc_param->input_crop.pos_y = crop_cy; + scc_param->input_crop.crop_width = crop_cwidth; + scc_param->input_crop.crop_height = crop_cheight; + scc_param->input_crop.in_width = chain0_width; + scc_param->input_crop.in_height = chain0_height; + scc_param->input_crop.out_width = chain1_width; + scc_param->input_crop.out_height = chain1_height; + lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + indexes++; + +#ifdef PRINT_DZOOM + printk(KERN_INFO "pre-zoom target : %d(%d, %d, %d %d)\n", + zoom_pre, crop_cx, crop_cy, crop_cwidth, crop_cheight); +#endif + +#ifdef SCALER_PARALLEL_MODE + scp_input_width = chain0_width; + scp_input_height = chain0_height; +#else + scp_input_width = chain2_width; + scp_input_height = chain2_height; +#endif + temp_width = (scp_input_width * 1000) / zoom_post; + temp_height = (scp_input_height * 1000) / zoom_post; + crop_px = (scp_input_width - temp_width)>>1; + crop_py = (scp_input_height - temp_height)>>1; + crop_pwidth = scp_input_width - (crop_px<<1); + crop_pheight = scp_input_height - (crop_py<<1); + + scp_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; + scp_param->input_crop.pos_x = crop_px; + scp_param->input_crop.pos_y = crop_py; + scp_param->input_crop.crop_width = crop_pwidth; + scp_param->input_crop.crop_height = crop_pheight; + scp_param->input_crop.in_width = scp_input_width; + scp_param->input_crop.in_height = scp_input_height; + scp_param->input_crop.out_width = chain3_width; + scp_param->input_crop.out_height = chain3_height; + lindex |= LOWBIT_OF(PARAM_SCALERP_INPUT_CROP); + hindex |= HIGHBIT_OF(PARAM_SCALERP_INPUT_CROP); + indexes++; + +#ifdef PRINT_DZOOM + printk(KERN_INFO "post-zoom target : %d(%d, %d, %d %d)\n", + zoom_post, crop_px, crop_py, crop_pwidth, crop_pheight); +#endif +#else + zoom_input = (chain0_ratio_width * 1000) / crop_width; + + if (test_bit(FIMC_IS_SUBDEV_START, &this->dis.state)) + zoom_target = (zoom_input * 91 + 34000) / 125; + else + zoom_target = zoom_input; + + temp_width = (chain0_ratio_width * 1000) / zoom_target; + temp_height = (chain0_ratio_height * 1000) / zoom_target; + crop_cx = (chain0_width - temp_width)>>1; + crop_cy = (chain0_height - temp_height)>>1; + crop_cwidth = chain0_width - (crop_cx<<1); + crop_cheight = chain0_height - (crop_cy<<1); + + scc_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; + scc_param->input_crop.pos_x = crop_cx; + scc_param->input_crop.pos_y = crop_cy; + scc_param->input_crop.crop_width = crop_cwidth; + scc_param->input_crop.crop_height = crop_cheight; + lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + indexes++; + +#ifdef PRINT_DZOOM + printk(KERN_INFO "zoom input : %d, zoom target : %d(%d, %d, %d %d)\n", + zoom_input, zoom_target, + crop_cx, crop_cy, crop_cwidth, crop_cheight); +#endif +#endif + + ret = fimc_is_itf_s_param(this, indexes, lindex, hindex); + if (ret) { + err("fimc_is_itf_s_param is fail\n"); + ret = -EINVAL; + goto exit; + } + + this->crop_x = crop_cx; + this->crop_y = crop_cy; + this->crop_width = crop_cwidth; + this->crop_height = crop_cheight; + this->dzoom_width = input_width; + +exit: + return ret; +} +#endif + +#ifdef ENABLE_DRC +static int fimc_is_ischain_drc_bypass(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + bool bypass) +{ + int ret = 0; + u32 lindex, hindex, indexes; + struct param_control *ctl_param; + u32 group_id = 0; + struct fimc_is_group *group; + + mdbgd_ischain("%s\n", device, __func__); + + group = &device->group_isp; + if (!group) { + merr("group is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group_id = GROUP_ID(GROUP_ID_3A0); + else + group_id = GROUP_ID(group->id); + + lindex = hindex = indexes = 0; + ctl_param = fimc_is_itf_g_param(device, NULL, PARAM_DRC_CONTROL); + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + if (bypass) + fimc_is_subdev_drc_bypass(device, ctl_param, &lindex, &hindex, &indexes); + else + fimc_is_subdev_drc_start(device, ctl_param, &lindex, &hindex, &indexes); + + /* + * It deleted a per-frame control at here. Because It is impossible to use + * DRC full_bypass with per-frame. So per-frame deleted and changed to set param + * control by g_param , s_param parameters to NULL. + */ + + ret = fimc_is_itf_s_param(device, NULL, lindex, hindex, indexes); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, frame, ret); + goto p_err; + } + + ret = fimc_is_itf_a_param(device, group_id); + if (ret) { + merr("fimc_is_itf_a_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_start(device, group_id); + if (ret) { + merr("fimc_is_itf_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + if (bypass) + clear_bit(FIMC_IS_SUBDEV_START, &device->drc.state); + else + set_bit(FIMC_IS_SUBDEV_START, &device->drc.state); + +p_err: + mrinfo("[DRC] bypass : %d\n", device, frame, bypass); + return ret; +} +#endif + +#ifdef ENABLE_TDNR +static int fimc_is_ischain_dnr_bypass(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, + bool bypass) +{ + int ret = 0; + u32 lindex, hindex, indexes; + struct param_control *ctl_param; + + mdbgd_ischain("%s\n", device, __func__); + + lindex = hindex = indexes = 0; + ctl_param = fimc_is_itf_g_param(device, frame, PARAM_TDNR_CONTROL); + + if (bypass) + fimc_is_subdev_dnr_bypass(device, ctl_param, &lindex, &hindex, &indexes); + else + fimc_is_subdev_dnr_start(device, ctl_param, &lindex, &hindex, &indexes); + + frame->shot->ctl.entry.lowIndexParam |= lindex; + frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, frame, lindex, hindex, indexes); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, frame, ret); + goto p_err; + } + + if (bypass) + clear_bit(FIMC_IS_SUBDEV_START, &device->dnr.state); + else + set_bit(FIMC_IS_SUBDEV_START, &device->dnr.state); + +p_err: + mrinfo("[DNR] bypass : %d\n", device, frame, bypass); + return ret; +} +#endif + +static int fimc_is_ischain_fd_bypass(struct fimc_is_device_ischain *device, + bool bypass) +{ + int ret = 0; + struct fd_param *fd_param; + struct fimc_is_subdev *fd; + struct fimc_is_group *group; + u32 indexes, lindex, hindex; + u32 group_id = 0; + + BUG_ON(!device); + + mdbgd_ischain("%s(%d)\n", device, __func__, bypass); + + fd = &device->fd; + group = fd->group; + if (!group) { + merr("group is NULL", device); + ret = -EINVAL; + goto p_err; + } + + group_id |= GROUP_ID(group->id); + fd_param = &device->is_region->parameter.fd; + indexes = lindex = hindex = 0; + + /* if there's only one group of isp, send group id by 3a0 */ + if ((group_id & GROUP_ID(GROUP_ID_ISP)) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group_id = GROUP_ID(GROUP_ID_3A0); + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + if (bypass) { + fd_param->control.cmd = CONTROL_COMMAND_STOP; + fd_param->control.bypass = CONTROL_BYPASS_DISABLE; + } else { + fd_param->control.cmd = CONTROL_COMMAND_START; + fd_param->control.bypass = CONTROL_BYPASS_DISABLE; + } + + lindex |= LOWBIT_OF(PARAM_FD_CONTROL); + hindex |= HIGHBIT_OF(PARAM_FD_CONTROL); + indexes++; + + fd_param->otf_input.width = device->chain3_width; + fd_param->otf_input.height = device->chain3_height; + lindex |= LOWBIT_OF(PARAM_FD_OTF_INPUT); + hindex |= HIGHBIT_OF(PARAM_FD_OTF_INPUT); + indexes++; + + ret = fimc_is_itf_s_param(device, NULL, lindex, hindex, indexes); + if (ret) { + merr("fimc_is_itf_s_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_start(device, group_id); + if (ret) { + merr("fimc_is_itf_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + if (bypass) { + clear_bit(FIMC_IS_SUBDEV_START, &fd->state); + mdbgd_ischain("FD off\n", device); + } else { + set_bit(FIMC_IS_SUBDEV_START, &fd->state); + mdbgd_ischain("FD on\n", device); + } + +p_err: + return ret; +} + +int fimc_is_ischain_3aa_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + u32 group_id; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + groupmgr = device->groupmgr; + group = &device->group_3aa; + group_id = GET_3AA_ID(vctx->video); + + ret = fimc_is_group_open(groupmgr, + group, + group_id, + device->instance, + vctx, + device, + fimc_is_ischain_3aa_callback); + if (ret) + merr("fimc_is_group_open is fail", device); + + return ret; +} + +int fimc_is_ischain_3aa_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + + BUG_ON(!device); + + groupmgr = device->groupmgr; + group = &device->group_3aa; + leader = &group->leader; + queue = GET_SRC_QUEUE(vctx); + + ret = fimc_is_ischain_3aa_stop(device, leader, queue); + if (ret) + merr("fimc_is_ischain_3aa_stop is fail", device); + + ret = fimc_is_group_close(groupmgr, group); + if (ret) + merr("fimc_is_group_close is fail", device); + + return ret; +} + +int fimc_is_ischain_3aa_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!leader); + + groupmgr = device->groupmgr; + group = &device->group_3aa; + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("already start", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_group_process_start(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + set_bit(FIMC_IS_SUBDEV_START, &leader->state); + +p_err: + return ret; +} + +int fimc_is_ischain_3aa_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!leader); + + groupmgr = device->groupmgr; + group = &device->group_3aa; + + if (!test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + mwarn("already stop", device); + goto p_err; + } + + ret = fimc_is_group_process_stop(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + clear_bit(FIMC_IS_SUBDEV_START, &leader->state); + +p_err: + info("[3A%d:D:%d] %s(%d, %d)\n", group->id, device->instance, __func__, + ret, atomic_read(&group->scount)); + return ret; +} + +int fimc_is_ischain_3aa_reqbufs(struct fimc_is_device_ischain *device, + u32 count) +{ + int ret = 0; + struct fimc_is_group *group; + + BUG_ON(!device); + + group = &device->group_3aa; + + if (!count) { + ret = fimc_is_itf_unmap(device, GROUP_ID(group->id)); + if (ret) + merr("fimc_is_itf_unmap is fail(%d)", device, ret); + } + + return ret; +} + +int fimc_is_ischain_3aa_s_format(struct fimc_is_device_ischain *device, + u32 width, u32 height) +{ + int ret = 0; + struct fimc_is_group *group; + struct fimc_is_subdev *leader; + + BUG_ON(!device); + + group = &device->group_3aa; + leader = &group->leader; + + leader->input.width = width; + leader->input.height = height; + + return ret; +} + +int fimc_is_ischain_3aa_s_input(struct fimc_is_device_ischain *device, + u32 input) +{ + int ret = 0; + u32 otf_input; + struct fimc_is_group *group; + struct fimc_is_groupmgr *groupmgr; + + BUG_ON(!device); + + groupmgr = device->groupmgr; + group = &device->group_3aa; + otf_input = (input & OTF_3AA_MASK) >> OTF_3AA_SHIFT; + + mdbgd_ischain("%s() calling fimc_is_group_init\n", device, __func__); + + ret = fimc_is_group_init(groupmgr, group, otf_input, 0); + if (ret) { + merr("fimc_is_group_init is fail", device); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ischain_3aa_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_3aa; + + ret = fimc_is_group_buffer_queue(groupmgr, group, queue, index); + if (ret) + merr("fimc_is_group_buffer_queue is fail(%d)", device, ret); + + return ret; +} + +int fimc_is_ischain_3aa_buffer_finish(struct fimc_is_device_ischain *device, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_3aa; + + ret = fimc_is_group_buffer_finish(groupmgr, group, index); + if (ret) + merr("fimc_is_group_buffer_finish is fail(%d)", device, ret); + + return ret; +} + +int fimc_is_ischain_3aa_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct taa_param *taa_param; + u32 lindex, hindex, indexes; + u32 crop_x, crop_y, crop_width, crop_height; + u32 *input_crop; + u32 *output_crop; + u32 size_change_request = 0; + + BUG_ON(!device); + BUG_ON(!device->is_region); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + BUG_ON(!node); + +#ifdef DBG_STREAMING + mdbgd_ischain("3AA TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + taa_param = &device->is_region->parameter.taa; + input_crop = node->input.cropRegion; + output_crop = node->output.cropRegion; + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->group_3aa.state)) { + crop_x = taa_param->otf_input.bayer_crop_offset_x; + crop_y = taa_param->otf_input.bayer_crop_offset_y; + crop_width = taa_param->otf_input.bayer_crop_width; + crop_height = taa_param->otf_input.bayer_crop_height; + } else { + crop_x = taa_param->vdma1_input.bayer_crop_offset_x; + crop_y = taa_param->vdma1_input.bayer_crop_offset_y; + crop_width = taa_param->vdma1_input.bayer_crop_width; + crop_height = taa_param->vdma1_input.bayer_crop_height; + } + + if (IS_NULL_COORD(input_crop)) { + input_crop[0] = crop_x; + input_crop[1] = crop_y; + input_crop[2] = crop_width; + input_crop[3] = crop_height; + } + + if (!GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + + size_change_request = ((taa_param->vdma1_input.bds_out_width != output_crop[2]) || + (taa_param->vdma1_input.bds_out_height != output_crop[3]) || + (taa_param->vdma1_input.bayer_crop_width != input_crop[2]) || + (taa_param->vdma1_input.bayer_crop_height != input_crop[3])); + + if (size_change_request || device->isp_size_forceset) { + /* + * forcefully set subsequent frame 3aa sizes until firmware acknowledges the + * updation of the changed information + */ + device->isp_size_forceset = 1; + if (size_change_request) + device->taa_size_changed_fcount = ldr_frame->fcount; + + ret = fimc_is_ischain_s_3aa_size(device, + ldr_frame, + input_crop, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aa_size is fail(%d)", device, ret); + goto p_err; + } + mrdbg("[3AA] in_crop[%d, %d, %d, %d]\n", device, ldr_frame, + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + mdbg_pframe("[3AA] out_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + } else { + + size_change_request = ((input_crop[0] != crop_x) || + (input_crop[1] != crop_y) || + (input_crop[2] != crop_width) || + (input_crop[3] != crop_height)); + + if (size_change_request || device->taa_size_forceset) { + /* + * forcefully set subsequent frame 3aa sizes until firmware acknowledges the + * updation of the changed information + */ + device->taa_size_forceset = 1; + if (size_change_request) + device->taa_size_changed_fcount = ldr_frame->fcount; + + ret = fimc_is_ischain_s_3aa_size(device, + ldr_frame, + input_crop, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aa_size is fail(%d)", device, ret); + goto p_err; + } + + mrdbg("[3AA] in_crop[%d, %d, %d, %d]\n", device, ldr_frame, + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + } + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +const struct fimc_is_queue_ops fimc_is_ischain_3aa_ops = { + .start_streaming = fimc_is_ischain_3aa_start, + .stop_streaming = fimc_is_ischain_3aa_stop +}; + +static int fimc_is_ischain_3aap_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct taa_param *taa_param, + u32 *output_crop, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *taa_vdma2_output; + + if ((output_crop[2] > taa_param->otf_input.bayer_crop_width) || + (output_crop[3] > taa_param->otf_input.bayer_crop_height)) { + mrerr("bds output size is invalid((%d, %d) > (%d, %d))", device, frame, + output_crop[2], + output_crop[3], + taa_param->otf_input.bayer_crop_width, + taa_param->otf_input.bayer_crop_height); + ret = -EINVAL; + goto p_err; + } + + /* HACK */ + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->group_3aa.state)) { + struct param_otf_input *taa_otf_input; + + taa_otf_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_OTF_INPUT); + taa_otf_input->bds_out_enable = ISP_BDS_COMMAND_ENABLE; + taa_otf_input->bds_out_width = output_crop[2]; + taa_otf_input->bds_out_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_3AA_OTF_INPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_OTF_INPUT); + (*indexes)++; + } else { + struct param_dma_input *taa_dma_input; + + taa_dma_input = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA1_INPUT); + taa_dma_input->bds_out_enable = ISP_BDS_COMMAND_ENABLE; + taa_dma_input->bds_out_width = output_crop[2]; + taa_dma_input->bds_out_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA1_INPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA1_INPUT); + (*indexes)++; + } + + taa_vdma2_output = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA2_OUTPUT); + taa_vdma2_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + taa_vdma2_output->width = output_crop[2]; + taa_vdma2_output->height = output_crop[3]; + taa_vdma2_output->buffer_number = 0; + taa_vdma2_output->buffer_address = 0; + taa_vdma2_output->dma_out_mask = 0; + taa_vdma2_output->bitwidth = DMA_OUTPUT_BIT_WIDTH_12BIT; + taa_vdma2_output->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENBABLE; + + if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR10 || + queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR12) { + taa_vdma2_output->format = DMA_INPUT_FORMAT_BAYER_PACKED12; + } else if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR16) { + taa_vdma2_output->format = DMA_INPUT_FORMAT_BAYER; + } else { + mwarn("Invalid bayer format", device); + ret = -EINVAL; + goto p_err; + } + + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA2_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA2_OUTPUT); + (*indexes)++; + + set_bit(FIMC_IS_SUBDEV_START, &subdev->state); + +p_err: + return ret; +} + + +static int fimc_is_ischain_3aap_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *taa_vdma2_output; + + mdbgd_ischain("%s\n", device, __func__); + + taa_vdma2_output = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA2_OUTPUT); + taa_vdma2_output->cmd = DMA_OUTPUT_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA2_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA2_OUTPUT); + (*indexes)++; + + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +static int fimc_is_ischain_3aap_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct camera2_scaler_uctl *scalerUd; + struct taa_param *taa_param; + u32 lindex, hindex, indexes; + u32 *output_crop; + + BUG_ON(!device); + BUG_ON(!device->is_region); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + BUG_ON(!node); + +#ifdef DBG_STREAMING + mdbgd_ischain("3AAP TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + taa_param = &device->is_region->parameter.taa; + scalerUd = &ldr_frame->shot->uctl.scalerUd; + /* HACK */ + framemgr = GET_SUBDEV_FRAMEMGR(subdev->leader); + if (!framemgr) { + merr("framemgr is NULL", device); + ret = -EINVAL; + goto p_err; + } + + /* HACK */ + queue = GET_SUBDEV_QUEUE(subdev->leader); + if (!queue) { + merr("queue is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (node->request) { + output_crop = node->output.cropRegion; + + if (output_crop[0] || output_crop[1]) { + mwarn("crop pos(%d, %d) is ignored", device, + output_crop[0], + output_crop[1]); + output_crop[0] = 0; + output_crop[1] = 0; + } + + if (!output_crop[0] && !output_crop[1] && + !output_crop[2] && !output_crop[3]) { + output_crop[0] = 0; + output_crop[1] = 0; + output_crop[2] = taa_param->vdma2_output.width; + output_crop[3] = taa_param->vdma2_output.height; + } + + if ((output_crop[2] != taa_param->vdma2_output.width) || + (output_crop[3] != taa_param->vdma2_output.height) || + !test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_3aap_start(device, + subdev, + ldr_frame, + queue, + taa_param, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aap_start is fail(%d)", device, ret); + goto p_err; + } + + mdbg_pframe("[3AP] ot_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + + /* device address setting */ + if (fimc_is_ischain_buf_tag(device, + subdev, + ldr_frame, + node, + queue, + framemgr, + output_crop[2], output_crop[3], + scalerUd->taapTargetAddress, + OUT_3AAP_FRAME)) + goto p_err; + } else { + if (test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_3aap_stop(device, + subdev, + ldr_frame, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aap_stop is fail(%d)", device, ret); + goto p_err; + } + + info("[3AP:D:%d] off, %d\n", device->instance, ldr_frame->fcount); + } + + mwarn("3aap request is 0", device); + scalerUd->taapTargetAddress[0] = 0; + scalerUd->taapTargetAddress[1] = 0; + scalerUd->taapTargetAddress[2] = 0; + node->request = 0; + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_ischain_3aac_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct taa_param *taa_param, + u32 *output_crop, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *taa_vdma4_output; + + if ((output_crop[2] != taa_param->otf_input.bayer_crop_width) || + (output_crop[3] != taa_param->otf_input.bayer_crop_height)) { + merr("bds output size is invalid((%d, %d) != (%d, %d))", device, + output_crop[2], + output_crop[3], + taa_param->otf_input.bayer_crop_width, + taa_param->otf_input.bayer_crop_height); + ret = -EINVAL; + goto p_err; + } + + taa_vdma4_output = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA4_OUTPUT); + taa_vdma4_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + taa_vdma4_output->width = output_crop[2]; + taa_vdma4_output->height = output_crop[3]; + taa_vdma4_output->buffer_number = 0; + taa_vdma4_output->buffer_address = 0; + taa_vdma4_output->dma_out_mask = 0; + taa_vdma4_output->bitwidth = DMA_OUTPUT_BIT_WIDTH_12BIT; + taa_vdma4_output->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENBABLE; + + if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR10 || + queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR12) { + taa_vdma4_output->format = DMA_INPUT_FORMAT_BAYER_PACKED12; + } else if (queue->framecfg.format.pixelformat == V4L2_PIX_FMT_SBGGR16) { + taa_vdma4_output->format = DMA_INPUT_FORMAT_BAYER; + } else { + mwarn("Invalid bayer format", device); + ret = -EINVAL; + goto p_err; + } + + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA4_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA4_OUTPUT); + (*indexes)++; + + set_bit(FIMC_IS_SUBDEV_START, &subdev->state); + +p_err: + return ret; +} + +static int fimc_is_ischain_3aac_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *taa_vdma4_output; + + mdbgd_ischain("%s\n", device, __func__); + + taa_vdma4_output = fimc_is_itf_g_param(device, frame, PARAM_3AA_VDMA4_OUTPUT); + taa_vdma4_output->cmd = DMA_OUTPUT_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_3AA_VDMA4_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_3AA_VDMA4_OUTPUT); + (*indexes)++; + + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +static int fimc_is_ischain_3aac_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct camera2_scaler_uctl *scalerUd; + struct taa_param *taa_param; + u32 lindex, hindex, indexes; + u32 *output_crop; + + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + +#ifdef DBG_STREAMING + mdbgd_ischain("3AAC TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + taa_param = &device->is_region->parameter.taa; + scalerUd = &ldr_frame->shot->uctl.scalerUd; + framemgr = GET_SUBDEV_FRAMEMGR(subdev); + if (!framemgr) { + merr("framemgr is NULL", device); + ret = -EINVAL; + goto p_err; + } + + queue = GET_SUBDEV_QUEUE(subdev); + if (!queue) { + merr("queue is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (node->request) { + output_crop = node->output.cropRegion; + + output_crop[0] = 0; + output_crop[1] = 0; + output_crop[2] = taa_param->otf_input.bayer_crop_width; + output_crop[3] = taa_param->otf_input.bayer_crop_height; + + if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_3aac_start(device, + subdev, + ldr_frame, + queue, + taa_param, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aac_start is fail(%d)", device, ret); + goto p_err; + } + + mdbg_pframe("[3AC] ot_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + + /* device address setting */ + if (fimc_is_ischain_buf_tag(device, + subdev, + ldr_frame, + node, + queue, + framemgr, + output_crop[2], output_crop[3], + scalerUd->taacTargetAddress, + OUT_3AAC_FRAME)) + goto p_err; + } else { + if (test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_3aac_stop(device, + subdev, + ldr_frame, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_3aac_stop is fail(%d)", device, ret); + goto p_err; + } + + info("[3AC:D:%d] off, %d\n", device->instance, ldr_frame->fcount); + } + + scalerUd->taacTargetAddress[0] = 0; + scalerUd->taacTargetAddress[1] = 0; + scalerUd->taacTargetAddress[2] = 0; + node->request = 0; + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ischain_isp_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_subdev *leader_3aa; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; +#ifdef ENABLE_BAYER_CROP + u32 crop_x, crop_y, crop_width, crop_height; + u32 sensor_width, sensor_height, sensor_ratio; + u32 chain0_width, chain0_height, chain0_ratio; + u32 chain3_width, chain3_height, chain3_ratio; + u32 chain1_wmin, chain1_hmin; +#endif + u32 input_crop[4] = {0, }; + u32 output_crop[4] = {0, }; + u32 lindex = 0; + u32 hindex = 0; + u32 indexes = 0; + + BUG_ON(!device); + BUG_ON(!device->sensor); + BUG_ON(!queue); + + mdbgd_isp("%s()\n", device, __func__); + + groupmgr = device->groupmgr; + group = &device->group_isp; + if (device->group_3aa.id == GROUP_ID_INVALID) + leader_3aa = NULL; + else + leader_3aa = &device->group_3aa.leader; + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("already start", device); + ret = -EINVAL; + goto p_err; + } + + /* 1. check chain size */ + if (leader_3aa && (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) || + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1))) { + if (test_bit(FIMC_IS_SUBDEV_OPEN, &leader_3aa->state) && + (leader_3aa->output.width != leader->input.width)) { + merr("width size is invalid(%d != %d)", device, + leader_3aa->output.width, leader->input.width); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SUBDEV_OPEN, &leader_3aa->state) && + (leader_3aa->output.height != leader->input.height)) { + merr("height size is invalid(%d != %d)", device, + leader_3aa->output.height, leader->input.height); + ret = -EINVAL; + goto p_err; + } + + /* BDS size */ + device->chain0_width = leader->input.width; + device->chain0_height = leader->input.height; + } else { + /* BDS size */ + device->chain0_width = leader->input.width - device->margin_width; + device->chain0_height = leader->input.height - device->margin_height; + } + + device->dzoom_width = 0; + device->bds_width = 0; + device->bds_height = 0; +#ifdef ENABLE_BAYER_CROP + /* 2. crop calculation */ + sensor_width = device->sensor_width; + sensor_height = device->sensor_height; + chain3_width = device->chain3_width; + chain3_height = device->chain3_height; + crop_width = sensor_width; + crop_height = sensor_height; + crop_x = crop_y = 0; + + sensor_ratio = sensor_width * 1000 / sensor_height; + chain3_ratio = chain3_width * 1000 / chain3_height; + + if (sensor_ratio == chain3_ratio) { + crop_width = sensor_width; + crop_height = sensor_height; + } else if (sensor_ratio < chain3_ratio) { + /* + * isp dma input limitation + * height : 2 times + */ + crop_height = + (sensor_width * chain3_height) / chain3_width; + crop_height = ALIGN(crop_height, 2); + crop_y = ((sensor_height - crop_height) >> 1) & 0xFFFFFFFE; + } else { + /* + * isp dma input limitation + * width : 4 times + */ + crop_width = + (sensor_height * chain3_width) / chain3_height; + crop_width = ALIGN(crop_width, 4); + crop_x = ((sensor_width - crop_width) >> 1) & 0xFFFFFFFE; + } + device->chain0_width = crop_width; + device->chain0_height = crop_height; + + device->dzoom_width = crop_width; + device->crop_width = crop_width; + device->crop_height = crop_height; + device->crop_x = crop_x; + device->crop_y = crop_y; + + dbg_isp("crop_x : %d, crop y : %d\n", crop_x, crop_y); + dbg_isp("crop width : %d, crop height : %d\n", + crop_width, crop_height); + + /* 2. scaling calculation */ + chain1_wmin = (crop_width >> 4) & 0xFFFFFFFE; + chain1_hmin = (crop_height >> 4) & 0xFFFFFFFE; + + if (chain1_wmin > device->chain1_width) { + printk(KERN_INFO "scc down scale limited : (%d,%d)->(%d,%d)\n", + device->chain1_width, device->chain1_height, + chain1_wmin, chain1_hmin); + device->chain1_width = chain1_wmin; + device->chain1_height = chain1_hmin; + device->chain2_width = chain1_wmin; + device->chain2_height = chain1_hmin; + } +#endif + + fimc_is_ischain_s_sensor(device, &lindex, &hindex, &indexes); + + /* init value: perframe parameters are not set before stream on. */ + input_crop[0] = 0; + input_crop[1] = 0; + input_crop[2] = fimc_is_sensor_g_bns_width(device->sensor) - device->margin_width; + input_crop[3] = fimc_is_sensor_g_bns_height(device->sensor) - device->margin_height; + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + /* + * In case of dirty bayer capture, reprocessing instance does not use 3aa. + * In this case, ischain device has no 3aa group. + */ + if (leader_3aa && test_bit(FIMC_IS_SUBDEV_OPEN, &leader_3aa->state)) + fimc_is_ischain_s_3aa_size(device, NULL, input_crop, + output_crop, &lindex, &hindex, &indexes); + } else { + output_crop[0] = 0; + output_crop[1] = 0; + output_crop[2] = device->chain0_width; + output_crop[3] = device->chain0_height; + + fimc_is_ischain_s_3aa_size(device, NULL, input_crop, + output_crop, &lindex, &hindex, &indexes); + } + + fimc_is_ischain_s_chain0_size(device, + NULL, device->chain0_width, device->chain0_height, + &lindex, &hindex, &indexes); + + fimc_is_ischain_s_chain1_size(device, + device->chain1_width, device->chain1_height, + &lindex, &hindex, &indexes); + + fimc_is_ischain_s_chain2_size(device, + NULL, device->chain2_width, device->chain2_height, + &lindex, &hindex, &indexes); + + fimc_is_ischain_s_chain3_size(device, + NULL, device->chain3_width, device->chain3_height, + &lindex, &hindex, &indexes); + + info("[3AA:D:%d] 3AA in size(%d x %d)\n", device->instance, input_crop[2], input_crop[3]); + info("[ISC:D:%d] BDS out to SCC in size(%d x %d)\n", device->instance, + device->chain0_width, device->chain0_height); + info("[ISC:D:%d] SCC out to DIS in size(%d x %d)\n", device->instance, + device->chain1_width, device->chain1_height); + info("[ISC:D:%d] DIS out to SCP in size(%d x %d)\n", device->instance, + device->chain2_width, device->chain2_height); + info("[ISC:D:%d] SCP out to FD in size(%d x %d)\n", device->instance, + device->chain3_width, device->chain3_height); + + + fimc_is_ischain_s_path(device, &lindex, &hindex, &indexes); + + fimc_is_ischain_s_color_range(device, device->color_range, &lindex, &hindex, &indexes); + + if (test_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state)) + fimc_is_itf_sensor_mode(device); + + lindex = 0xFFFFFFFF; + hindex = 0xFFFFFFFF; + indexes = 64; + + ret = fimc_is_itf_s_param(device , NULL, lindex, hindex, indexes); + if (ret) { + merr("fimc_is_itf_s_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_f_param(device); + if (ret) { + merr("fimc_is_itf_f_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_sys_ctl(device, IS_SYS_CLOCK_GATE, sysfs_debug.clk_gate_mode); + if (ret) { + merr("fimc_is_itf_sys_ctl is fail", device); + ret = -EINVAL; + goto p_err; + } + + /* + * this code is enabled when camera 2.0 feature is enabled + * ret = fimc_is_itf_g_capability(device); + * if (ret) { + * err("fimc_is_itf_g_capability is fail\n"); + * ret = -EINVAL; + * goto p_err; + *} + */ + + ret = fimc_is_itf_init_process_start(device); + if (ret) { + merr("fimc_is_itf_init_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_group_process_start(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + set_bit(FIMC_IS_SUBDEV_START, &leader->state); + set_bit(FIMC_IS_ISHCAIN_START, &device->state); + +p_err: + info("[ISP:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +int fimc_is_ischain_isp_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!leader); + BUG_ON(!queue); + + mdbgd_isp("%s\n", device, __func__); + + groupmgr = device->groupmgr; + group = &device->group_isp; + + if (!test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + mwarn("already stop", device); + goto p_err; + } + + ret = fimc_is_group_process_stop(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + clear_bit(FIMC_IS_SUBDEV_START, &leader->state); + clear_bit(FIMC_IS_ISHCAIN_START, &device->state); + +p_err: + info("[ISP:D:%d] %s(%d, %d)\n", device->instance, __func__, + ret, atomic_read(&group->scount)); + return ret; +} + +int fimc_is_ischain_isp_reqbufs(struct fimc_is_device_ischain *device, + u32 count) +{ + int ret = 0; + struct fimc_is_group *group; + + BUG_ON(!device); + + group = &device->group_isp; + + if (!count) { + ret = fimc_is_itf_unmap(device, GROUP_ID(group->id)); + if (ret) + merr("fimc_is_itf_unmap is fail(%d)", device, ret); + } + + return ret; +} + +int fimc_is_ischain_isp_s_format(struct fimc_is_device_ischain *device, + u32 width, u32 height) +{ + int ret = 0; + struct fimc_is_group *group; + struct fimc_is_subdev *subdev; + + BUG_ON(!device); + + group = &device->group_isp; + subdev = &group->leader; + + subdev->input.width = width; + subdev->input.height = height; + + return ret; +} + +int fimc_is_ischain_isp_s_input(struct fimc_is_device_ischain *device, + u32 input) +{ + int ret = 0; + struct fimc_is_group *group; + struct fimc_is_groupmgr *groupmgr; + u32 tax_vindex; + + BUG_ON(!device); + + group = &device->group_isp; + groupmgr = device->groupmgr; + tax_vindex = (input & TAX_VINDEX_MASK) >> TAX_VINDEX_SHIFT; + + + /* checking 3ax group connection */ + if ((GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == true) || + (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == true)) { + if (!((tax_vindex == FIMC_IS_VIDEO_3A0C_NUM) || + (tax_vindex == FIMC_IS_VIDEO_3A0P_NUM) || + (tax_vindex == FIMC_IS_VIDEO_3A1C_NUM) || + (tax_vindex == FIMC_IS_VIDEO_3A1P_NUM))) { + merr("TAX_VINDEX(%d) is invalid", device, tax_vindex); + ret = -EINVAL; + goto p_err; + } + } + + mdbgd_ischain("%s() calling fimc_is_group_init\n", device, __func__); + + ret = fimc_is_group_init(groupmgr, group, false, tax_vindex); + if (ret) { + merr("fimc_is_group_init is fail", device); + ret = -EINVAL; + } + +p_err: + return ret; +} + +int fimc_is_ischain_isp_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_isp; + + ret = fimc_is_group_buffer_queue(groupmgr, group, queue, index); + if (ret) { + merr("fimc_is_group_buffer_queue is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ischain_isp_buffer_finish(struct fimc_is_device_ischain *device, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_isp; + + ret = fimc_is_group_buffer_finish(groupmgr, group, index); + if (ret) + merr("fimc_is_group_buffer_finish is fail(%d)", device, ret); + + return ret; +} + +int fimc_is_ischain_isp_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct isp_param *isp_param; + u32 lindex, hindex, indexes; + u32 *input_crop; + u32 *output_crop; + u32 size_change_request = 0; + + BUG_ON(!device); + BUG_ON(!device->is_region); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + BUG_ON(!node); + +#ifdef DBG_STREAMING + mdbgd_ischain("ISP TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + isp_param = &device->is_region->parameter.isp; + input_crop = node->input.cropRegion; + output_crop = node->output.cropRegion; + + if (!GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + if (IS_NULL_COORD(output_crop)) { + output_crop[0] = 0; + output_crop[1] = 0; + output_crop[2] = isp_param->otf_output.width; + output_crop[3] = isp_param->otf_output.height; + } + + size_change_request = ((output_crop[2] != isp_param->otf_output.width) || + (output_crop[3] != isp_param->otf_output.height)); + + if (size_change_request || device->isp_size_forceset) { + device->isp_size_forceset = 1; + if (size_change_request) + device->isp_size_changed_fcount = ldr_frame->fcount; + + ret = fimc_is_ischain_s_chain0_size(device, + ldr_frame, + output_crop[2], + output_crop[3], + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_s_chain0_size is fail(%d)", device, ret); + goto p_err; + } + + mrinfo("[ISP] out_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + } else { + if (IS_NULL_COORD(input_crop)) { + input_crop[0] = 0; + input_crop[1] = 0; + input_crop[2] = isp_param->vdma1_input.width; + input_crop[3] = isp_param->vdma1_input.height; + } + + size_change_request = ((input_crop[2] != isp_param->vdma1_input.width) || + (input_crop[3] != isp_param->vdma1_input.height)); + + if (size_change_request || device->isp_size_forceset) { + + device->isp_size_forceset = 1; + if (size_change_request) + device->isp_size_changed_fcount = ldr_frame->fcount; + + ret = fimc_is_ischain_s_chain0_size(device, + ldr_frame, + input_crop[2], + input_crop[3], + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_s_chain0_size is fail(%d)", device, ret); + goto p_err; + } + + mrdbg("[ISP] in_crop[%d, %d, %d, %d]\n", device, ldr_frame, + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + } + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +const struct fimc_is_queue_ops fimc_is_ischain_isp_ops = { + .start_streaming = fimc_is_ischain_isp_start, + .stop_streaming = fimc_is_ischain_isp_stop +}; + +static int fimc_is_ischain_scc_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct scalerc_param *scc_param, + u32 *input_crop, + u32 *output_crop, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + u32 planes, i, j, buf_index; + struct param_dma_output *scc_dma_output; + struct param_otf_output *scc_otf_output; + struct param_scaler_input_crop *scc_input_crop; + struct param_scaler_output_crop *scc_output_crop; +#ifndef SCALER_PARALLEL_MODE + struct param_otf_input *scp_otf_input; +#endif + + if (output_crop[2] > scc_param->otf_input.width * MAX_ZOOM_LEVEL) { + mwarn("Cannot be scaled up beyond %d times(%d -> %d)", + device, MAX_ZOOM_LEVEL, scc_param->otf_input.width, output_crop[2]); + output_crop[2] = scc_param->otf_input.width * MAX_ZOOM_LEVEL; + } + + if (output_crop[3] > scc_param->otf_input.height * MAX_ZOOM_LEVEL) { + mwarn("Cannot be scaled up beyond %d times(%d -> %d)", + device, MAX_ZOOM_LEVEL, scc_param->otf_input.height, output_crop[3]); + output_crop[3] = scc_param->otf_input.height * MAX_ZOOM_LEVEL; + } + + if (output_crop[2] < (scc_param->otf_input.width + 15) / 16) { + mwarn("Cannot be scaled down beyond 1/16 times(%d -> %d)", + device, scc_param->otf_input.width, output_crop[2]); + output_crop[2] = (scc_param->otf_input.width + 15) / 16; + } + + if (output_crop[3] < (scc_param->otf_input.height + 15) / 16) { + mwarn("Cannot be scaled down beyond 1/16 times(%d -> %d)", + device, scc_param->otf_input.height, output_crop[3]); + output_crop[3] = (scc_param->otf_input.height + 15) / 16; + } + + planes = queue->framecfg.format.num_planes; + for (i = 0; i < queue->buf_maxcount; i++) { + for (j = 0; j < planes; j++) { + buf_index = i*planes + j; + device->is_region->shared[447+buf_index] = queue->buf_dva[i][j]; + } + } + + mdbgd_ischain("buf_num:%d buf_plane:%d shared[447] : 0x%X\n", + device, + queue->buf_maxcount, + queue->framecfg.format.num_planes, + device->imemory.kvaddr_shared + 447 * sizeof(u32)); + + /* setting always although otf output is not used. */ + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + scc_otf_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OTF_OUTPUT); + scc_otf_output->width = output_crop[2]; + scc_otf_output->height = output_crop[3]; + } else { + scc_otf_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OTF_OUTPUT); +#ifdef SCALER_PARALLEL_MODE + scc_otf_output->width = output_crop[2]; + scc_otf_output->height = output_crop[3]; +#else + scp_otf_input = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_OTF_INPUT); + scc_otf_output->width = scp_otf_input->width; + scc_otf_output->height = scp_otf_input->height; +#endif + } + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + (*indexes)++; + + scc_input_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_INPUT_CROP); + scc_input_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + scc_input_crop->pos_x = input_crop[0]; + scc_input_crop->pos_y = input_crop[1]; + scc_input_crop->crop_width = input_crop[2]; + scc_input_crop->crop_height = input_crop[3]; + scc_input_crop->in_width = scc_param->otf_input.width; + scc_input_crop->in_height = scc_param->otf_input.height; + scc_input_crop->out_width = output_crop[2]; + scc_input_crop->out_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + (*indexes)++; + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + scc_output_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OUTPUT_CROP); + scc_output_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + scc_output_crop->pos_x = output_crop[0]; + scc_output_crop->pos_y = output_crop[1]; + scc_output_crop->crop_width = output_crop[2]; + scc_output_crop->crop_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + (*indexes)++; + + scc_dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_DMA_OUTPUT); + scc_dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + scc_dma_output->buffer_number = queue->buf_maxcount; + scc_dma_output->plane = queue->framecfg.format.num_planes - 1; + scc_dma_output->buffer_address = device->imemory.dvaddr_shared + 447*sizeof(u32); + scc_dma_output->width = output_crop[2]; + scc_dma_output->height = output_crop[3]; + scc_dma_output->reserved[0] = SCALER_DMA_OUT_SCALED; + } else { + scc_output_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OUTPUT_CROP); + scc_output_crop->cmd = SCALER_CROP_COMMAND_DISABLE; + scc_output_crop->pos_x = output_crop[0]; + scc_output_crop->pos_y = output_crop[1]; + scc_output_crop->crop_width = output_crop[2]; + scc_output_crop->crop_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + (*indexes)++; + + scc_dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_DMA_OUTPUT); + scc_dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + scc_dma_output->buffer_number = queue->buf_maxcount; + scc_dma_output->plane = queue->framecfg.format.num_planes - 1; + scc_dma_output->buffer_address = device->imemory.dvaddr_shared + 447*sizeof(u32); +#ifdef SCALER_PARALLEL_MODE + scc_dma_output->width = output_crop[2]; + scc_dma_output->height = output_crop[3]; + scc_dma_output->reserved[0] = SCALER_DMA_OUT_SCALED; +#else + scc_dma_output->width = input_crop[2]; + scc_dma_output->height = input_crop[3]; + scc_dma_output->reserved[0] = SCALER_DMA_OUT_UNSCALED; +#endif + } + + switch (queue->framecfg.format.pixelformat) { + case V4L2_PIX_FMT_YUYV: + scc_dma_output->format = DMA_OUTPUT_FORMAT_YUV422, + scc_dma_output->plane = DMA_OUTPUT_PLANE_1; + scc_dma_output->order = DMA_OUTPUT_ORDER_CrYCbY; + break; + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV21: + scc_dma_output->format = OTF_OUTPUT_FORMAT_YUV420, + scc_dma_output->plane = DMA_OUTPUT_PLANE_2; + scc_dma_output->order = DMA_OUTPUT_ORDER_CbCr; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV12: + scc_dma_output->format = OTF_OUTPUT_FORMAT_YUV420, + scc_dma_output->plane = DMA_OUTPUT_PLANE_2; + scc_dma_output->order = DMA_OUTPUT_ORDER_CrCb; + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + scc_dma_output->format = OTF_OUTPUT_FORMAT_YUV420, + scc_dma_output->plane = DMA_OUTPUT_PLANE_3; + scc_dma_output->order = DMA_OUTPUT_ORDER_NO; + break; + default: + mwarn("unknown preview pixelformat", device); + break; + } + + *lindex |= LOWBIT_OF(PARAM_SCALERC_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_DMA_OUTPUT); + (*indexes)++; + + set_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +static int fimc_is_ischain_scc_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct scalerc_param *scc_param, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *scc_dma_output; + struct param_scaler_input_crop *scc_input_crop; + struct param_otf_output *scc_otf_output; + struct param_scaler_output_crop *scc_output_crop; + + mdbgd_ischain("%s\n", device, __func__); + + scc_dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_DMA_OUTPUT); + scc_dma_output->cmd = DMA_OUTPUT_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERC_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_DMA_OUTPUT); + (*indexes)++; + + /* If SCC output size is bigger than input, + * it takes more time for scale up processing. + */ + scc_input_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_INPUT_CROP); + scc_input_crop->out_width = device->chain1_width; + scc_input_crop->out_height = device->chain1_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_INPUT_CROP); + (*indexes)++; + + /* Even if SCC otf output path is not used, + * otf output size should be same with input crop output size. + * Otherwise, scaler hang can be induced at digital zoom scenario. + */ + scc_otf_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OTF_OUTPUT); + scc_otf_output->width = device->chain1_width; + scc_otf_output->height = device->chain1_height; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OTF_OUTPUT); + (*indexes)++; + + scc_output_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERC_OUTPUT_CROP); + scc_output_crop->cmd = SCALER_CROP_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERC_OUTPUT_CROP); + (*indexes)++; + + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +static int fimc_is_ischain_scc_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct camera2_scaler_uctl *scalerUd; + struct scalerc_param *scc_param; + u32 lindex, hindex, indexes; + u32 *input_crop, *output_crop; + u32 dma_width, dma_height; + + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + +#ifdef DBG_STREAMING + mdbgd_ischain("SCC TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + scc_param = &device->is_region->parameter.scalerc; + scalerUd = &ldr_frame->shot->uctl.scalerUd; + framemgr = GET_SUBDEV_FRAMEMGR(subdev); + if (!framemgr) { + merr("framemgr is NULL", device); + ret = -EINVAL; + goto p_err; + } + + queue = GET_SUBDEV_QUEUE(subdev); + if (!queue) { + merr("queue is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (node->request) { + input_crop = node->input.cropRegion; + output_crop = node->output.cropRegion; + + if (!input_crop[0] && !input_crop[1] && + !input_crop[2] && !input_crop[3]) { + input_crop[0] = scc_param->input_crop.pos_x; + input_crop[1] = scc_param->input_crop.pos_y; + input_crop[2] = scc_param->input_crop.crop_width; + input_crop[3] = scc_param->input_crop.crop_height; + } + + if (!output_crop[0] && !output_crop[1] && + !output_crop[2] && !output_crop[3]) { + output_crop[0] = scc_param->output_crop.pos_x; + output_crop[1] = scc_param->output_crop.pos_y; + output_crop[2] = scc_param->output_crop.crop_width; + output_crop[3] = scc_param->output_crop.crop_height; + } + + if ((input_crop[0] != scc_param->input_crop.pos_x) || + (input_crop[1] != scc_param->input_crop.pos_y) || + (input_crop[2] != scc_param->input_crop.crop_width) || + (input_crop[3] != scc_param->input_crop.crop_height) || + (output_crop[0] != scc_param->output_crop.pos_x) || + (output_crop[1] != scc_param->output_crop.pos_y) || + (output_crop[2] != scc_param->output_crop.crop_width) || + (output_crop[3] != scc_param->output_crop.crop_height) || + (input_crop[2] != scc_param->dma_output.width) || + (input_crop[3] != scc_param->dma_output.height) || + !test_bit(FIMC_IS_SUBDEV_START, &subdev->state) || + device->isp_size_forceset) { + + ret = fimc_is_ischain_scc_start(device, + subdev, + ldr_frame, + queue, + scc_param, + input_crop, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_scc_start is fail(%d)", device, ret); + goto p_err; + } + + mdbg_pframe("[SCC] in_crop[%d, %d, %d, %d]\n", device, ldr_frame, + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + mdbg_pframe("[SCC] ot_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) { + dma_width = node->output.cropRegion[2]; + dma_height = node->output.cropRegion[3]; + } else { +#ifdef SCALER_PARALLEL_MODE + dma_width = node->output.cropRegion[2]; + dma_height = node->output.cropRegion[3]; +#else + /* in scaler serial mode, use input crop w/h for dma size */ + dma_width = node->input.cropRegion[2]; + dma_height = node->input.cropRegion[3]; +#endif + } + /* device address setting */ + if (fimc_is_ischain_buf_tag(device, + subdev, + ldr_frame, + node, + queue, + framemgr, + dma_width, dma_height, + scalerUd->sccTargetAddress, + OUT_SCC_FRAME)) + goto p_err; + + } else { + if (test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_scc_stop(device, + subdev, + ldr_frame, + scc_param, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_scc_stop is fail(%d)", device, ret); + goto p_err; + } + + info("[SCC:D:%d] off, %d\n", device->instance, ldr_frame->fcount); + } + + scalerUd->sccTargetAddress[0] = 0; + scalerUd->sccTargetAddress[1] = 0; + scalerUd->sccTargetAddress[2] = 0; + node->request = 0; + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_ischain_scp_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct scalerp_param *scp_param, + u32 *input_crop, + u32 *output_crop, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + u32 planes, i, j, buf_index; + struct param_dma_output *scp_dma_output; + struct param_scaler_input_crop *scp_input_crop; + struct param_scaler_output_crop *scp_output_crop; + + fimc_is_ischain_scp_adjust_crop(device, scp_param, &output_crop[2], &output_crop[3]); + + planes = queue->framecfg.format.num_planes; + for (i = 0; i < queue->buf_maxcount; i++) { + for (j = 0; j < planes; j++) { + buf_index = i*planes + j; + device->is_region->shared[400 + buf_index] = queue->buf_dva[i][j]; + } + } + + mdbgd_ischain("buf_num:%d buf_plane:%d shared[400] : 0x%X\n", + device, + queue->buf_maxcount, + queue->framecfg.format.num_planes, + device->imemory.kvaddr_shared + 400 * sizeof(u32)); + + scp_input_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_INPUT_CROP); + scp_input_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + scp_input_crop->pos_x = input_crop[0]; + scp_input_crop->pos_y = input_crop[1]; + scp_input_crop->crop_width = input_crop[2]; + scp_input_crop->crop_height = input_crop[3]; +#ifdef SCALER_PARALLEL_MODE + scp_input_crop->in_width = device->bds_width; + scp_input_crop->in_height = device->bds_height; + scp_input_crop->out_width = output_crop[2]; + scp_input_crop->out_height = output_crop[3]; +#else + scp_input_crop->in_width = scp_param->otf_input.width; + scp_input_crop->in_height = scp_param->otf_input.height; + scp_input_crop->out_width = scp_param->otf_output.width; + scp_input_crop->out_height = scp_param->otf_output.height; +#endif + *lindex |= LOWBIT_OF(PARAM_SCALERP_INPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_INPUT_CROP); + (*indexes)++; + + /* + * scaler can't apply stride to each plane, only y plane. + * basically cb, cr plane should be half of y plane, + * and it's automatically set + * + * 3 plane : all plane should be 8 or 16 stride + * 2 plane : y plane should be 32, 16 stride, others should be half stride of y + * 1 plane : all plane should be 8 stride + */ + /* + * limitation of output_crop.pos_x and pos_y + * YUV422 3P, YUV420 3P : pos_x and pos_y should be x2 + * YUV422 1P : pos_x should be x2 + */ + scp_output_crop = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_OUTPUT_CROP); + scp_output_crop->cmd = SCALER_CROP_COMMAND_ENABLE; + scp_output_crop->pos_x = output_crop[0]; + scp_output_crop->pos_y = output_crop[1]; + scp_output_crop->crop_width = output_crop[2]; + scp_output_crop->crop_height = output_crop[3]; + *lindex |= LOWBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_OUTPUT_CROP); + (*indexes)++; + + scp_dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_DMA_OUTPUT); + scp_dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + scp_dma_output->buffer_number = queue->buf_maxcount; + scp_dma_output->plane = queue->framecfg.format.num_planes - 1; + scp_dma_output->buffer_address = device->imemory.dvaddr_shared + 400 * sizeof(u32); + scp_dma_output->width = output_crop[2]; + scp_dma_output->height = output_crop[3]; + + switch (queue->framecfg.format.pixelformat) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + scp_dma_output->format = OTF_OUTPUT_FORMAT_YUV420, + scp_dma_output->plane = DMA_OUTPUT_PLANE_3; + scp_dma_output->order = DMA_OUTPUT_ORDER_NO; + break; + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV21: + scp_dma_output->format = OTF_OUTPUT_FORMAT_YUV420, + scp_dma_output->plane = DMA_OUTPUT_PLANE_2; + scp_dma_output->order = DMA_OUTPUT_ORDER_CbCr; + break; + default: + mwarn("unknown preview pixelformat", device); + break; + } + + *lindex |= LOWBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + (*indexes)++; + + set_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +static int fimc_is_ischain_scp_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *frame, + struct scalerp_param *scp_param, + u32 *lindex, + u32 *hindex, + u32 *indexes) +{ + int ret = 0; + struct param_dma_output *scp_dma_output; + + mdbgd_ischain("%s\n", device, __func__); + + scp_dma_output = fimc_is_itf_g_param(device, frame, PARAM_SCALERP_DMA_OUTPUT); + scp_dma_output->cmd = DMA_OUTPUT_COMMAND_DISABLE; + *lindex |= LOWBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + *hindex |= HIGHBIT_OF(PARAM_SCALERP_DMA_OUTPUT); + (*indexes)++; + + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + + return ret; +} + +int fimc_is_ischain_scp_s_format(struct fimc_is_device_ischain *device, + u32 pixelformat, u32 width, u32 height) +{ + int ret = 0; + + /* check scaler size limitation */ + switch (pixelformat) { + /* + * YUV422 1P, YUV422 2P : x8 + * YUV422 3P : x16 + */ + case V4L2_PIX_FMT_YUV422P: + if (width % 8) { + merr("width(%d) of format(%d) is not supported size", + device, width, pixelformat); + ret = -EINVAL; + goto p_err; + } + break; + /* + * YUV420 2P : x8 + * YUV420 3P : x16 + */ + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + if (width % 8) { + merr("width(%d) of format(%d) is not supported size", + device, width, pixelformat); + ret = -EINVAL; + goto p_err; + } + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + if (width % 16) { + merr("width(%d) of format(%d) is not supported size", + device, width, pixelformat); + ret = -EINVAL; + goto p_err; + } + break; + default: + merr("format(%d) is not supported", device, pixelformat); + ret = -EINVAL; + goto p_err; + break; + } + + device->chain1_width = width; + device->chain1_height = height; + device->chain2_width = width; + device->chain2_height = height; + device->chain3_width = width; + device->chain3_height = height; + +p_err: + return ret; +} + +static int fimc_is_ischain_scp_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct camera2_scaler_uctl *scalerUd; + struct scalerp_param *scp_param; + u32 lindex, hindex, indexes; + u32 *input_crop, *output_crop; + + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + +#ifdef DBG_STREAMING + mdbgd_ischain("SCP TAG(request %d)\n", device, node->request); +#endif + + lindex = hindex = indexes = 0; + scp_param = &device->is_region->parameter.scalerp; + scalerUd = &ldr_frame->shot->uctl.scalerUd; + framemgr = GET_SUBDEV_FRAMEMGR(subdev); + if (!framemgr) { + merr("framemgr is NULL", device); + ret = -EINVAL; + goto p_err; + } + + queue = GET_SUBDEV_QUEUE(subdev); + if (!queue) { + merr("queue is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (node->request) { + input_crop = node->input.cropRegion; + output_crop = node->output.cropRegion; + + if (!input_crop[0] && !input_crop[1] && + !input_crop[2] && !input_crop[3]) { + input_crop[0] = scp_param->input_crop.pos_x; + input_crop[1] = scp_param->input_crop.pos_y; + input_crop[2] = scp_param->input_crop.crop_width; + input_crop[3] = scp_param->input_crop.crop_height; + } + + if (!output_crop[0] && !output_crop[1] && + !output_crop[2] && !output_crop[3]) { + output_crop[0] = scp_param->output_crop.pos_x; + output_crop[1] = scp_param->output_crop.pos_y; + output_crop[2] = scp_param->output_crop.crop_width; + output_crop[3] = scp_param->output_crop.crop_height; + } + + if ((input_crop[0] != scp_param->input_crop.pos_x) || + (input_crop[1] != scp_param->input_crop.pos_y) || + (input_crop[2] != scp_param->input_crop.crop_width) || + (input_crop[3] != scp_param->input_crop.crop_height) || + (output_crop[0] != scp_param->output_crop.pos_x) || + (output_crop[1] != scp_param->output_crop.pos_y) || + (output_crop[2] != scp_param->output_crop.crop_width) || + (output_crop[3] != scp_param->output_crop.crop_height) || + !test_bit(FIMC_IS_SUBDEV_START, &subdev->state) || + device->isp_size_forceset) { +#ifdef SCALER_PARALLEL_MODE + ret = fimc_is_ischain_s_chain2_size(device, + ldr_frame, + input_crop[2], + input_crop[3], + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_s_chain2_size is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_ischain_s_chain3_size(device, + ldr_frame, + output_crop[2], + output_crop[3], + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_s_chain3_size is fail(%d)", device, ret); + goto p_err; + } + mrinfo("[SCPX] xx_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + +#endif + ret = fimc_is_ischain_scp_start(device, + subdev, + ldr_frame, + queue, + scp_param, + input_crop, + output_crop, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_scp_start is fail(%d)", device, ret); + goto p_err; + } + + mdbg_pframe("[SCP] in_crop[%d, %d, %d, %d]\n", device, ldr_frame, + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + mdbg_pframe("[SCP] ot_crop[%d, %d, %d, %d]\n", device, ldr_frame, + output_crop[0], output_crop[1], output_crop[2], output_crop[3]); + } + + /* device address setting */ + if (fimc_is_ischain_buf_tag(device, + subdev, + ldr_frame, + node, + queue, + framemgr, + output_crop[2], output_crop[3], + scalerUd->scpTargetAddress, + OUT_SCP_FRAME)) + goto p_err; + } else { + if (test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_scp_stop(device, + subdev, + ldr_frame, + scp_param, + &lindex, + &hindex, + &indexes); + if (ret) { + merr("fimc_is_ischain_scp_stop is fail(%d)", device, ret); + goto p_err; + } + + info("[SCP:D:%d] off, %d\n", device->instance, ldr_frame->fcount); + } + + scalerUd->scpTargetAddress[0] = 0; + scalerUd->scpTargetAddress[1] = 0; + scalerUd->scpTargetAddress[2] = 0; + node->request = 0; + } + + ldr_frame->shot->ctl.entry.lowIndexParam |= lindex; + ldr_frame->shot->ctl.entry.highIndexParam |= hindex; + ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, 0); + if (ret) { + mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ischain_dis_start(struct fimc_is_device_ischain *device, + bool bypass) +{ + int ret = 0; + u32 group_id = 0; + struct dis_param *dis_param; + u32 chain1_width, chain1_height; + u32 indexes, lindex, hindex; + + struct fimc_is_group *group; + struct fimc_is_groupmgr *groupmgr; + + mdbgd_ischain("%s()\n", device, __func__); + + BUG_ON(!device); + + chain1_width = device->dis_width; + chain1_height = device->dis_height; + indexes = lindex = hindex = 0; + dis_param = &device->is_region->parameter.dis; + group_id |= GROUP_ID(device->group_isp.id); + group_id |= GROUP_ID(device->group_dis.id); + + group = &device->group_dis; + groupmgr = device->groupmgr; + + mdbgd_ischain("%s() calling fimc_is_group_init\n", device, __func__); + + ret = fimc_is_group_init(groupmgr, group, false, 0); + if (ret) { + merr("fimc_is_group_init is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + fimc_is_ischain_s_chain1_size(device, + chain1_width, chain1_height, &lindex, &hindex, &indexes); + + if (bypass) + fimc_is_subdev_dis_bypass(device, + dis_param, &lindex, &hindex, &indexes); + else + fimc_is_subdev_dis_start(device, + dis_param, &lindex, &hindex, &indexes); + + ret = fimc_is_itf_s_param(device, NULL, lindex, hindex, indexes); + if (ret) { + merr("fimc_is_itf_s_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_a_param(device, group_id); + if (ret) { + merr("fimc_is_itf_a_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_start(device, group_id); + if (ret) { + merr("fimc_is_itf_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + set_bit(FIMC_IS_SUBDEV_START, &device->dis.state); + mdbgd_ischain("DIS on\n", device); + + device->chain1_width = chain1_width; + device->chain1_height = chain1_height; + +p_err: + return ret; +} + +int fimc_is_ischain_dis_stop(struct fimc_is_device_ischain *device) +{ + int ret = 0; + u32 group_id = 0; + struct dis_param *dis_param; + u32 chain1_width, chain1_height; + u32 indexes, lindex, hindex; + + mdbgd_ischain("%s()\n", device, __func__); + + chain1_width = device->chain2_width; + chain1_height = device->chain2_height; + indexes = lindex = hindex = 0; + dis_param = &device->is_region->parameter.dis; + group_id |= GROUP_ID(device->group_isp.id); + group_id |= GROUP_ID(device->group_dis.id); + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + fimc_is_ischain_s_chain1_size(device, + chain1_width, chain1_height, &lindex, &hindex, &indexes); + + fimc_is_subdev_dis_bypass(device, + dis_param, &lindex, &hindex, &indexes); + + ret = fimc_is_itf_s_param(device, NULL, lindex, hindex, indexes); + if (ret) { + merr("fimc_is_itf_s_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_a_param(device, group_id); + if (ret) { + merr("fimc_is_itf_a_param is fail", device); + ret = -EINVAL; + goto p_err; + } + + group_id = GROUP_ID(device->group_isp.id); + ret = fimc_is_itf_process_start(device, group_id); + if (ret) { + merr("fimc_is_itf_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + clear_bit(FIMC_IS_SUBDEV_START, &device->dis.state); + mdbgd_ischain("DIS off\n", device); + + device->chain1_width = chain1_width; + device->chain1_height = chain1_height; + +p_err: + return ret; +} + +int fimc_is_ischain_dis_tag(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_frame *ldr_frame, + struct camera2_node *node) +{ + int ret = 0; + struct fimc_is_framemgr *framemgr; + struct camera2_scaler_uctl *scalerUd; + struct fimc_is_queue *queue; + + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!ldr_frame); + BUG_ON(!ldr_frame->shot); + + scalerUd = &ldr_frame->shot->uctl.scalerUd; + + queue = GET_SUBDEV_QUEUE(subdev); + if (!queue) { + merr("queue is NULL", device); + ret = -EINVAL; + goto p_err; + } + + framemgr = GET_SUBDEV_FRAMEMGR(subdev); + if (!framemgr) { + merr("framemgr is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (node->request) { + if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_dis_start(device, + ldr_frame->shot_ext->dis_bypass); + if (ret) { + merr("vdisc_start is fail", device); + goto p_err; + } + } + + /* device address setting */ + if (fimc_is_ischain_buf_tag(device, + subdev, + ldr_frame, + node, + queue, + framemgr, + device->chain1_width, device->chain1_height, + scalerUd->disTargetAddress, + OUT_DIS_FRAME)) + goto p_err; + } else { + if (test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + ret = fimc_is_ischain_dis_stop(device); + if (ret) { + merr("vdisc_stop is fail", device); + goto p_err; + } + } + + scalerUd->disTargetAddress[0] = 0; + scalerUd->disTargetAddress[1] = 0; + scalerUd->disTargetAddress[2] = 0; + node->request = 0; + } + +p_err: + return ret; +} + +int fimc_is_ischain_vdc_s_format(struct fimc_is_device_ischain *device, + u32 width, u32 height) +{ + int ret = 0; + + device->dis_width = width; + device->dis_height = height; + + return ret; +} + +int fimc_is_ischain_vdo_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + + groupmgr = device->groupmgr; + group = &device->group_dis; + + ret = fimc_is_group_open(groupmgr, group, GROUP_ID_DIS, + device->instance, vctx, device, fimc_is_ischain_dis_callback); + if (ret) + merr("fimc_is_group_open is fail", device); + + return ret; +} + +int fimc_is_ischain_vdo_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + + BUG_ON(!device); + + groupmgr = device->groupmgr; + group = &device->group_dis; + leader = &group->leader; + queue = GET_SRC_QUEUE(vctx); + + ret = fimc_is_ischain_vdo_stop(device, leader, queue); + if (ret) + merr("fimc_is_ischain_vdo_stop is fail", device); + + ret = fimc_is_group_close(groupmgr, group); + if (ret) + merr("fimc_is_group_close is fail", device); + + return ret; +} + +int fimc_is_ischain_vdo_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!leader); + BUG_ON(!queue); + + groupmgr = device->groupmgr; + group = &device->group_dis; + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("already start", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_group_process_start(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + set_bit(FIMC_IS_SUBDEV_START, &leader->state); + +p_err: + return ret; +} + +int fimc_is_ischain_vdo_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!leader); + BUG_ON(!queue); + + groupmgr = device->groupmgr; + group = &device->group_dis; + + if (!test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + mwarn("already stop", device); + goto p_err; + } + + ret = fimc_is_group_process_stop(groupmgr, group, queue); + if (ret) { + merr("fimc_is_group_process_start is fail", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_process_stop(device, GROUP_ID_DIS); + if (ret) { + merr("fimc_is_itf_process_stop is fail", device); + ret = -EINVAL; + goto p_err; + } + + clear_bit(FIMC_IS_SUBDEV_START, &leader->state); + +p_err: + info("[DIS:D:%d] %s(%d, %d)\n", device->instance, __func__, + ret, atomic_read(&group->scount)); + return ret; +} + +int fimc_is_ischain_vdo_s_format(struct fimc_is_device_ischain *this, + u32 width, u32 height) +{ + int ret = 0; + + return ret; +} + +int fimc_is_ischain_vdo_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + BUG_ON(!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_dis; + + ret = fimc_is_group_buffer_queue(groupmgr, group, queue, index); + if (ret) { + merr("fimc_is_group_buffer_queue is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ischain_vdo_buffer_finish(struct fimc_is_device_ischain *device, + u32 index) +{ + int ret = 0; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + + BUG_ON(!device); + +#ifdef DBG_STREAMING + mdbgd_ischain("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_dis; + + ret = fimc_is_group_buffer_finish(groupmgr, group, index); + if (ret) + merr("fimc_is_group_buffer_finish is fail(%d)", device, ret); + + return ret; +} + +const struct fimc_is_queue_ops fimc_is_ischain_vdo_ops = { + .start_streaming = fimc_is_ischain_vdo_start, + .stop_streaming = fimc_is_ischain_vdo_stop +}; + +int fimc_is_ischain_g_capability(struct fimc_is_device_ischain *this, + u32 user_ptr) +{ + int ret = 0; + + ret = copy_to_user((void *)user_ptr, &this->capability, + sizeof(struct camera2_sm)); + + return ret; +} + +int fimc_is_ischain_print_status(struct fimc_is_device_ischain *device) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx; + struct fimc_is_subdev *isp; + struct fimc_is_framemgr *framemgr; + struct fimc_is_interface *itf; + + isp = &device->group_isp.leader; + vctx = isp->vctx; + framemgr = GET_SRC_FRAMEMGR(vctx); + itf = device->interface; + + fimc_is_frame_print_free_list(framemgr); + fimc_is_frame_print_request_list(framemgr); + fimc_is_frame_print_process_list(framemgr); + fimc_is_frame_print_complete_list(framemgr); + + return ret; +} + +int fimc_is_ischain_3aa_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *check_frame) +{ + int ret = 0; + u32 capture_id, group_id; + unsigned long flags; + struct fimc_is_group *group; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct fimc_is_subdev *leader, *taac, *taap, *scc, *dis, *scp; + struct camera2_node *node, *leader_node; + +#ifdef ENABLE_FAST_SHOT + uint32_t af_trigger_bk; + enum aa_capture_intent captureIntent_bk; +#endif + +#ifdef DBG_STREAMING + mdbgd_ischain("%s()\n", device, __func__); +#endif + + BUG_ON(!device); + BUG_ON(!check_frame); + + group = &device->group_3aa; + group_id = GROUP_ID(group->id); + leader = &group->leader; + framemgr = GET_LEADER_FRAMEMGR(leader); + + fimc_is_frame_request_head(framemgr, &frame); + + if (unlikely(!frame)) { + merr("frame is NULL", device); + return -EINVAL; + } + + if (unlikely(frame != check_frame)) { + merr("frame checking is fail(%X != %X)", device, + (u32)frame, (u32)check_frame); + ret = -EINVAL; + goto p_err; + } + + if (unlikely(!frame->shot)) { + merr("frame->shot is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (unlikely(!test_bit(FRAME_MAP_MEM, &frame->memory))) { + fimc_is_itf_map(device, group_id, frame->dvaddr_shot, frame->shot_size); + set_bit(FRAME_MAP_MEM, &frame->memory); + } + + frame->shot->ctl.entry.lowIndexParam = 0; + frame->shot->ctl.entry.highIndexParam = 0; + frame->shot->dm.entry.lowIndexParam = 0; + frame->shot->dm.entry.highIndexParam = 0; + leader_node = &frame->shot_ext->node_group.leader; + +#ifdef ENABLE_SETFILE + if (frame->shot_ext->setfile != device->setfile) { + unsigned int setfile = frame->shot_ext->setfile; + ret = fimc_is_ischain_chg_setfile(device, setfile); + if (ret) { + err("fimc_is_ischain_chg_setfile is fail"); + goto p_err; + } + } +#endif + +#ifdef ENABLE_FAST_SHOT + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + af_trigger_bk = frame->shot->ctl.aa.afTrigger; + captureIntent_bk = frame->shot->ctl.aa.captureIntent; + memcpy(&frame->shot->ctl.aa, &group->fast_ctl.aa, + sizeof(struct camera2_aa_ctl)); + memcpy(&frame->shot->ctl.scaler, &group->fast_ctl.scaler, + sizeof(struct camera2_scaler_ctl)); + frame->shot->ctl.aa.afTrigger = af_trigger_bk; + frame->shot->ctl.aa.captureIntent = captureIntent_bk; + } +#endif + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + enum aa_capture_intent captureIntent; + captureIntent = group->intent_ctl.aa.captureIntent; + + if (captureIntent != AA_CAPTURE_INTENT_CUSTOM) { + frame->shot->ctl.aa.captureIntent = captureIntent; + group->intent_ctl.aa.captureIntent = AA_CAPTURE_INTENT_CUSTOM; + } + } + + ret = fimc_is_ischain_3aa_tag(device, leader, frame, leader_node); + if (ret) { + merr("fimc_is_ischain_3aa_tag is fail(%d)", device, ret); + goto p_err; + } + + for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) { + node = &frame->shot_ext->node_group.capture[capture_id]; + taac = taap = scc = dis = scp = NULL; + + switch (node->vid) { + case 0: + break; + case FIMC_IS_VIDEO_3A0C_NUM: + case FIMC_IS_VIDEO_3A1C_NUM: + taac = group->subdev[ENTRY_3AAC]; + break; + case FIMC_IS_VIDEO_3A0P_NUM: + case FIMC_IS_VIDEO_3A1P_NUM: + taap = group->subdev[ENTRY_3AAP]; + break; + case FIMC_IS_VIDEO_SCC_NUM: + scc = group->subdev[ENTRY_SCALERC]; + break; + case FIMC_IS_VIDEO_VDC_NUM: + dis = group->subdev[ENTRY_DIS]; + break; + case FIMC_IS_VIDEO_SCP_NUM: + scp = group->subdev[ENTRY_SCALERP]; + break; + default: + merr("capture0 vid(%d) is invalid", device, node->vid); + ret = -EINVAL; + goto p_err; + } + + if (taac) { + ret = fimc_is_ischain_3aac_tag(device, taac, frame, node); + if (ret) { + merr("fimc_is_ischain_3aac_tag is fail(%d)", device, ret); + goto p_err; + } + } + + if (taap) { + ret = fimc_is_ischain_3aap_tag(device, taap, frame, node); + if (ret) { + merr("fimc_is_ischain_3aap_tag is fail(%d)", device, ret); + goto p_err; + } + } + + if (scc) { + ret = fimc_is_ischain_scc_tag(device, scc, frame, node); + if (ret) { + merr("fimc_is_ischain_scc_tag fail(%d)", device, ret); + goto p_err; + } + } + + if (dis) { + ret = fimc_is_ischain_dis_tag(device, dis, frame, node); + if (ret) { + merr("fimc_is_ischain_dis_tag fail(%d)", device, ret); + goto p_err; + } + } + + if (scp) { + ret = fimc_is_ischain_scp_tag(device, scp, frame, node); + if (ret) { + merr("fimc_is_ischain_scp_tag fail(%d)", device, ret); + goto p_err; + } + } + } + +p_err: + if (ret) { + merr("3aa shot(index : %d) is skipped(error : %d)", device, + frame->index, ret); + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + framemgr_x_barrier_irqr(framemgr, 0, flags); + set_bit(REQ_3AA_SHOT, &frame->req_flag); + fimc_is_itf_grp_shot(device, group, frame); + } + + return ret; +} + +int fimc_is_ischain_isp_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *check_frame) +{ + int ret = 0; + u32 capture_id, group_id; + unsigned long flags; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct fimc_is_subdev *leader, *scc, *dis, *dnr, *scp, *fd; + struct camera2_node *node, *leader_node; + + BUG_ON(!device); + BUG_ON(!check_frame); + BUG_ON(device->instance_sensor >= FIMC_IS_MAX_NODES); + +#ifdef DBG_STREAMING + mdbgd_isp("%s\n", device, __func__); +#endif + + groupmgr = device->groupmgr; + group = &device->group_isp; + group_id = GROUP_ID(group->id); + leader = &group->leader; + dnr = group->subdev[ENTRY_TDNR]; + fd = group->subdev[ENTRY_LHFD]; + framemgr = GET_LEADER_FRAMEMGR(leader); + + /* + BE CAREFUL WITH THIS + 1. buffer queue, all compoenent stop, so it's good + 2. interface callback, all component will be stop until new one is came + therefore, i expect lock object is not necessary in here + */ + + BUG_ON(!framemgr); + + fimc_is_frame_request_head(framemgr, &frame); + + if (unlikely(!frame)) { + merr("frame is NULL", device); + return -EINVAL; + } + + if (unlikely(frame != check_frame)) { + merr("frame checking is fail(%X != %X)", device, + (u32)frame, (u32)check_frame); + ret = -EINVAL; + goto p_err; + } + + if (unlikely(!frame->shot)) { + merr("frame->shot is NULL", device); + ret = -EINVAL; + goto p_err; + } + + /* HACK */ + if ((GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0) && + (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0)) + group_id = GROUP_ID(GROUP_ID_3A0); + + if (unlikely(!test_bit(FRAME_MAP_MEM, &frame->memory))) { + fimc_is_itf_map(device, group_id, frame->dvaddr_shot, frame->shot_size); + set_bit(FRAME_MAP_MEM, &frame->memory); + } + + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) { + frame->shot->uctl.scalerUd.taapTargetAddress[0] = + frame->dvaddr_buffer[0]; + frame->shot->uctl.scalerUd.taapTargetAddress[1] = 0; + frame->shot->uctl.scalerUd.taapTargetAddress[2] = 0; + } + + frame->shot->ctl.entry.lowIndexParam = 0; + frame->shot->ctl.entry.highIndexParam = 0; + frame->shot->dm.entry.lowIndexParam = 0; + frame->shot->dm.entry.highIndexParam = 0; + leader_node = &frame->shot_ext->node_group.leader; + +#ifdef ENABLE_SETFILE + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) { + if (frame->shot_ext->setfile != device->setfile) { + unsigned int setfile = frame->shot_ext->setfile; + ret = fimc_is_ischain_chg_setfile(device, setfile); + if (ret) { + err("fimc_is_ischain_chg_setfile is fail"); + goto p_err; + } + } + } +#endif + +#ifdef ENABLE_DRC + if (frame->shot_ext->drc_bypass) { + if (test_bit(FIMC_IS_SUBDEV_START, &device->drc.state)) { + ret = fimc_is_ischain_drc_bypass(device, frame, true); + if (ret) { + err("fimc_is_ischain_drc_bypass(1) is fail"); + goto p_err; + } + } + } else { + if (!test_bit(FIMC_IS_SUBDEV_START, &device->drc.state)) { + ret = fimc_is_ischain_drc_bypass(device, frame, false); + if (ret) { + err("fimc_is_ischain_drc_bypass(0) is fail"); + goto p_err; + } + } + } +#endif + +#ifdef ENABLE_TDNR + if (dnr) { + if (frame->shot_ext->dnr_bypass) { + if (test_bit(FIMC_IS_SUBDEV_START, &dnr->state)) { + ret = fimc_is_ischain_dnr_bypass(device, frame, true); + if (ret) { + merr("dnr_bypass(1) is fail", device); + goto p_err; + } + } + } else { + if (!test_bit(FIMC_IS_SUBDEV_START, &dnr->state)) { + ret = fimc_is_ischain_dnr_bypass(device, frame, false); + if (ret) { + merr("dnr_bypass(0) is fail", device); + goto p_err; + } + } + } + } +#endif + +#ifdef ENABLE_FD + if (fd) { + if (frame->shot_ext->fd_bypass) { + if (test_bit(FIMC_IS_SUBDEV_START, &fd->state)) { + ret = fimc_is_ischain_fd_bypass(device, true); + if (ret) { + merr("fd_bypass(1) is fail", device); + goto p_err; + } + } + } else { + if (!test_bit(FIMC_IS_SUBDEV_START, &fd->state)) { + ret = fimc_is_ischain_fd_bypass(device, false); + if (ret) { + merr("fd_bypass(0) is fail", device); + goto p_err; + } + } + } + } +#endif + +#ifdef SCALER_CROP_DZOOM + crop_width = frame->shot->ctl.scaler.cropRegion[2]; + /* Digital zoom is not supported in multiple sensor mode */ + if (crop_width && (crop_width != device->dzoom_width)) { + ret = fimc_is_ischain_s_dzoom(device, + frame->shot->ctl.scaler.cropRegion[0], + frame->shot->ctl.scaler.cropRegion[1], + frame->shot->ctl.scaler.cropRegion[2]); + if (ret) { + err("fimc_is_ischain_s_dzoom(%d, %d, %d) is fail", + frame->shot->ctl.scaler.cropRegion[0], + frame->shot->ctl.scaler.cropRegion[1], + frame->shot->ctl.scaler.cropRegion[2]); + goto exit; + } + } +#endif + + if (!GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0)) { + ret = fimc_is_ischain_3aa_tag(device, leader, frame, leader_node); + if (ret) { + merr("fimc_is_ischain_3aa_tag is fail(%d)", device, ret); + goto p_err; + } + } + + ret = fimc_is_ischain_isp_tag(device, leader, frame, leader_node); + if (ret) { + merr("fimc_is_ischain_isp_tag is fail(%d)", device, ret); + goto p_err; + } + + for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) { + node = &frame->shot_ext->node_group.capture[capture_id]; + scc = dis = scp = NULL; + + switch (node->vid) { + case 0: + break; + case FIMC_IS_VIDEO_SCC_NUM: + scc = group->subdev[ENTRY_SCALERC]; + break; + case FIMC_IS_VIDEO_VDC_NUM: + dis = group->subdev[ENTRY_DIS]; + break; + case FIMC_IS_VIDEO_SCP_NUM: + scp = group->subdev[ENTRY_SCALERP]; + break; + case FIMC_IS_VIDEO_3A0C_NUM: + case FIMC_IS_VIDEO_3A1C_NUM: + case FIMC_IS_VIDEO_3A0P_NUM: + case FIMC_IS_VIDEO_3A1P_NUM: + default: + merr("capture0 vid(%d) is invalid", device, node->vid); + ret = -EINVAL; + goto p_err; + } + + if (scc) { + ret = fimc_is_ischain_scc_tag(device, scc, frame, node); + if (ret) { + merr("fimc_is_ischain_scc_tag fail(%d)", device, ret); + goto p_err; + } + } + + if (dis) { + ret = fimc_is_ischain_dis_tag(device, dis, frame, node); + if (ret) { + merr("fimc_is_ischain_dis_tag fail(%d)", device, ret); + goto p_err; + } + } + + if (scp) { + ret = fimc_is_ischain_scp_tag(device, scp, frame, node); + if (ret) { + merr("fimc_is_ischain_scp_tag fail(%d)", device, ret); + goto p_err; + } + } + } + +#ifdef PRINT_PARAM + if (frame->fcount == 1) { + fimc_is_hw_memdump(device->interface, + (u32) &device->is_region->parameter, + (u32) &device->is_region->parameter + sizeof(device->is_region->parameter)); + } +#endif + +p_err: + if (ret) { + merr("isp shot(index : %d) is skipped(error : %d)", + device, frame->index, ret); + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + framemgr_x_barrier_irqr(framemgr, 0, flags); + set_bit(REQ_ISP_SHOT, &frame->req_flag); + fimc_is_itf_grp_shot(device, group, frame); + } + + return ret; +} + +int fimc_is_ischain_dis_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *check_frame) +{ + int ret = 0; + unsigned long flags; + u32 capture_id; + bool dis_req, scp_req; + struct fimc_is_video_ctx *vctx; + struct fimc_is_group *group; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct fimc_is_subdev *leader, *dnr, *scp, *fd; + struct camera2_node *node; + +#ifdef DBG_STREAMING + mdbgd_ischain("%s()\n", device, __func__); +#endif + + BUG_ON(!device); + BUG_ON(!check_frame); + + group = &device->group_dis; + vctx = group->leader.vctx; + framemgr = GET_SRC_FRAMEMGR(vctx); + dis_req = scp_req = false; + leader = &group->leader; + dnr = group->subdev[ENTRY_TDNR]; + fd = group->subdev[ENTRY_LHFD]; + + fimc_is_frame_request_head(framemgr, &frame); + + if (frame != check_frame) { + merr("grp_frame is invalid(%X != %X)", device, + (u32)frame, (u32)check_frame); + return -EINVAL; + } + + frame->shot->ctl.entry.lowIndexParam = 0; + frame->shot->ctl.entry.highIndexParam = 0; + frame->shot->dm.entry.lowIndexParam = 0; + frame->shot->dm.entry.highIndexParam = 0; + +#ifdef ENABLE_TDNR + if (dnr) { + if (frame->shot_ext->dnr_bypass) { + if (test_bit(FIMC_IS_SUBDEV_START, &dnr->state)) { + ret = fimc_is_ischain_dnr_bypass(device, frame, true); + if (ret) { + merr("dnr_bypass(1) is fail", device); + goto p_err; + } + } + } else { + if (!test_bit(FIMC_IS_SUBDEV_START, &dnr->state)) { + ret = fimc_is_ischain_dnr_bypass(device, frame, false); + if (ret) { + merr("dnr_bypass(0) is fail", device); + goto p_err; + } + } + } + } +#endif + +#ifdef ENABLE_FD + if (fd) { + if (frame->shot_ext->fd_bypass) { + if (test_bit(FIMC_IS_SUBDEV_START, &fd->state)) { + ret = fimc_is_ischain_fd_bypass(device, true); + if (ret) { + merr("fd_bypass(1) is fail", device); + goto p_err; + } + } + } else { + if (!test_bit(FIMC_IS_SUBDEV_START, &fd->state)) { + ret = fimc_is_ischain_fd_bypass(device, false); + if (ret) { + merr("fd_bypass(0) is fail", device); + goto p_err; + } + } + } + } +#endif + + for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) { + node = &frame->shot_ext->node_group.capture[capture_id]; + scp = NULL; + + switch (node->vid) { + case 0: + break; + case FIMC_IS_VIDEO_SCP_NUM: + scp = group->subdev[ENTRY_SCALERP]; + break; + case FIMC_IS_VIDEO_SCC_NUM: + case FIMC_IS_VIDEO_VDC_NUM: + case FIMC_IS_VIDEO_3A0C_NUM: + case FIMC_IS_VIDEO_3A1C_NUM: + case FIMC_IS_VIDEO_3A0P_NUM: + case FIMC_IS_VIDEO_3A1P_NUM: + default: + merr("capture0 vid(%d) is invalid", device, node->vid); + ret = -EINVAL; + goto p_err; + } + + if (scp) { + ret = fimc_is_ischain_scp_tag(device, scp, frame, node); + if (ret) { + merr("fimc_is_ischain_scp_tag fail(%d)", device, ret); + goto p_err; + } + } + } + +p_err: + if (ret) { + err("dis shot(index : %d) is skipped(error : %d)", + frame->index, ret); + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + fimc_is_frame_trans_req_to_pro(framemgr, frame); + framemgr_x_barrier_irqr(framemgr, 0, flags); + set_bit(REQ_DIS_SHOT, &frame->req_flag); + fimc_is_itf_grp_shot(device, group, frame); + } + + return ret; +} + +int fimc_is_ischain_camctl(struct fimc_is_device_ischain *this, + struct fimc_is_frame *frame, + u32 fcount) +{ + int ret = 0; +#ifdef ENABLE_SENSOR_DRIVER + struct fimc_is_interface *itf; + struct camera2_uctl *applied_ctl; + + struct camera2_sensor_ctl *isp_sensor_ctl; + struct camera2_lens_ctl *isp_lens_ctl; + struct camera2_flash_ctl *isp_flash_ctl; + + u32 index; + +#ifdef DBG_STREAMING + mdbgd_ischain("%s()\n", device, __func__); +#endif + + itf = this->interface; + isp_sensor_ctl = &itf->isp_peri_ctl.sensorUd.ctl; + isp_lens_ctl = &itf->isp_peri_ctl.lensUd.ctl; + isp_flash_ctl = &itf->isp_peri_ctl.flashUd.ctl; + + /*lens*/ + index = (fcount + 0) & SENSOR_MAX_CTL_MASK; + applied_ctl = &this->peri_ctls[index]; + applied_ctl->lensUd.ctl.focusDistance = isp_lens_ctl->focusDistance; + + /*sensor*/ + index = (fcount + 1) & SENSOR_MAX_CTL_MASK; + applied_ctl = &this->peri_ctls[index]; + applied_ctl->sensorUd.ctl.exposureTime = isp_sensor_ctl->exposureTime; + applied_ctl->sensorUd.ctl.frameDuration = isp_sensor_ctl->frameDuration; + applied_ctl->sensorUd.ctl.sensitivity = isp_sensor_ctl->sensitivity; + + /*flash*/ + index = (fcount + 0) & SENSOR_MAX_CTL_MASK; + applied_ctl = &this->peri_ctls[index]; + applied_ctl->flashUd.ctl.flashMode = isp_flash_ctl->flashMode; + applied_ctl->flashUd.ctl.firingPower = isp_flash_ctl->firingPower; + applied_ctl->flashUd.ctl.firingTime = isp_flash_ctl->firingTime; +#endif + return ret; +} + +int fimc_is_ischain_tag(struct fimc_is_device_ischain *ischain, + struct fimc_is_frame *frame) +{ + int ret = 0; +#ifdef ENABLE_SENSOR_DRIVER + struct camera2_uctl *applied_ctl; + struct timeval curtime; + u32 fcount; + + fcount = frame->fcount; + applied_ctl = &ischain->peri_ctls[fcount & SENSOR_MAX_CTL_MASK]; + + do_gettimeofday(&curtime); + + /* Request */ + frame->shot->dm.request.frameCount = fcount; + + /* Lens */ + frame->shot->dm.lens.focusDistance = + applied_ctl->lensUd.ctl.focusDistance; + + /* Sensor */ + frame->shot->dm.sensor.exposureTime = + applied_ctl->sensorUd.ctl.exposureTime; + frame->shot->dm.sensor.sensitivity = + applied_ctl->sensorUd.ctl.sensitivity; + frame->shot->dm.sensor.frameDuration = + applied_ctl->sensorUd.ctl.frameDuration; + frame->shot->dm.sensor.timeStamp = + (uint64_t)curtime.tv_sec*1000000 + curtime.tv_usec; + + /* Flash */ + frame->shot->dm.flash.flashMode = + applied_ctl->flashUd.ctl.flashMode; + frame->shot->dm.flash.firingPower = + applied_ctl->flashUd.ctl.firingPower; + frame->shot->dm.flash.firingTime = + applied_ctl->flashUd.ctl.firingTime; +#else + struct timespec curtime; + + do_posix_clock_monotonic_gettime(&curtime); + + frame->shot->dm.request.frameCount = frame->fcount; + frame->shot->dm.sensor.timeStamp = fimc_is_get_timestamp(); +#endif + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.h new file mode 100644 index 000000000000..9c0f28150def --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ischain.h @@ -0,0 +1,419 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_ISCHAIN_H +#define FIMC_IS_DEVICE_ISCHAIN_H + +#include + +#include "fimc-is-mem.h" +#include "fimc-is-subdev-ctrl.h" +#include "fimc-is-groupmgr.h" +#include "fimc-is-resourcemgr.h" + +#define SENSOR_MAX_CTL 0x10 +#define SENSOR_MAX_CTL_MASK (SENSOR_MAX_CTL-1) + +#define REPROCESSING_FLAG 0x80000000 +#define REPROCESSING_MASK 0xF0000000 +#define REPROCESSING_SHIFT 28 +#define OTF_3AA_MASK 0x0F000000 +#define OTF_3AA_SHIFT 24 +#define SSX_VINDEX_MASK 0x00FF0000 +#define SSX_VINDEX_SHIFT 16 +#define TAX_VINDEX_MASK 0x0000FF00 +#define TAX_VINDEX_SHIFT 8 +#define MODULE_MASK 0x000000FF + +#define FIMC_IS_SETFILE_MASK 0x0000FFFF +#define FIMC_IS_ISP_CRANGE_MASK 0x0F000000 +#define FIMC_IS_ISP_CRANGE_SHIFT 24 +#define FIMC_IS_SCC_CRANGE_MASK 0x00F00000 +#define FIMC_IS_SCC_CRANGE_SHIFT 20 +#define FIMC_IS_SCP_CRANGE_MASK 0x000F0000 +#define FIMC_IS_SCP_CRANGE_SHIFT 16 +#define FIMC_IS_CRANGE_FULL 0 +#define FIMC_IS_CRANGE_LIMITED 1 + +#if defined(CONFIG_SOC_EXYNOS5422) +#define FIMC_IS_SPI_PINNAME "14000000.pinctrl" +#endif + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#define FIMC_IS_SPI_PINNAME "14cc0000.pinctrl" +#endif +#define FIMC_IS_SPI_OUTPUT 1 +#define FIMC_IS_SPI_FUNC 2 + +/*global state*/ +enum fimc_is_ischain_state { + FIMC_IS_ISCHAIN_OPEN, + FIMC_IS_ISCHAIN_LOADED, + FIMC_IS_ISCHAIN_POWER_ON, + FIMC_IS_ISCHAIN_OPEN_SENSOR, + FIMC_IS_ISHCAIN_START, + FIMC_IS_ISCHAIN_REPROCESSING, +}; + +enum fimc_is_camera_device { + CAMERA_SINGLE_REAR, + CAMERA_SINGLE_FRONT, +}; + +#ifdef CONFIG_COMPANION_USE +enum fimc_is_companion_sensor { + COMPANION_SENSOR_2P2 = 1, + COMPANION_SENSOR_IMX240 = 2, +}; +#endif + +struct fimc_is_from_info { + u32 bin_start_addr; + u32 bin_end_addr; + u32 oem_start_addr; + u32 oem_end_addr; + u32 awb_start_addr; + u32 awb_end_addr; + u32 shading_start_addr; + u32 shading_end_addr; + u32 setfile_start_addr; + u32 setfile_end_addr; +#ifdef CONFIG_COMPANION_USE + u32 concord_master_setfile_start_addr; + u32 concord_master_setfile_end_addr; + u32 concord_mode_setfile_start_addr; + u32 concord_mode_setfile_end_addr; + u32 lsc_gain_start_addr; + u32 lsc_gain_end_addr; + u32 pdaf_start_addr; + u32 pdaf_end_addr; + u32 coefficient_cal_start_addr; + u32 coefficient_cal_end_addr; + u32 pdaf_cal_start_addr; + u32 pdaf_cal_end_addr; + u32 concord_cal_start_addr; + u32 concord_cal_end_addr; + u32 concord_bin_start_addr; + u32 concord_bin_end_addr; + u32 lsc_i0_gain_addr; + u32 lsc_j0_gain_addr; + u32 lsc_a_gain_addr; + u32 lsc_k4_gain_addr; + u32 lsc_scale_gain_addr; + u32 wcoefficient1_addr; + u32 coef1_start; + u32 coef1_end; + u32 coef2_start; + u32 coef2_end; + u32 coef3_start; + u32 coef3_end; + u32 coef4_start; + u32 coef4_end; + u32 coef5_start; + u32 coef5_end; + u32 coef6_start; + u32 coef6_end; + u32 af_inf_addr; + u32 af_macro_addr; + u32 lsc_gain_crc_addr; + u32 pdaf_crc_addr; + u32 coef1_crc_addr; + u32 coef2_crc_addr; + u32 coef3_crc_addr; + u32 coef4_crc_addr; + u32 coef5_crc_addr; + u32 coef6_crc_addr; + char concord_header_ver[12]; + bool is_c1_caldata_read; + char load_c1_fw_name[50]; + char load_c1_mastersetf_name[50]; + char load_c1_modesetf_name[50]; + int sensor_id; +#endif + char header_ver[12]; + char cal_map_ver[4]; + char setfile_ver[7]; + char oem_ver[12]; + char awb_ver[12]; + char shading_ver[12]; + char load_fw_name[50]; + char load_setfile_name[50]; + char project_name[9]; + bool is_caldata_read; +}; + +#ifdef CONFIG_OIS_USE +struct fimc_is_ois_info { + char header_ver[7]; + char load_fw_name[50]; +}; +#endif + +struct fimc_is_ishcain_mem { + /* buffer base */ + dma_addr_t base; + /* total length */ + size_t size; + /* buffer base */ + dma_addr_t vaddr_base; + /* current addr */ + dma_addr_t vaddr_curr; + void *fw_cookie; + + /* fw memory base */ + u32 dvaddr; + u32 kvaddr; + /* debug part of fw memory */ + u32 dvaddr_debug; + u32 kvaddr_debug; + /* is region part of fw memory */ + u32 offset_region; + u32 dvaddr_region; + u32 kvaddr_region; + /* shared part of is region */ + u32 offset_shared; + u32 dvaddr_shared; + u32 kvaddr_shared; + /* internal memory for ODC */ + u32 dvaddr_odc; + u32 kvaddr_odc; + /* internal memory for DIS */ + u32 dvaddr_dis; + u32 kvaddr_dis; + /* internal memory for 3DNR */ + u32 dvaddr_3dnr; + u32 kvaddr_3dnr; + + struct is_region *is_region; +}; + +struct fimc_is_device_ischain { + struct platform_device *pdev; + struct exynos_platform_fimc_is *pdata; + void __iomem *regs; + + struct fimc_is_resourcemgr *resourcemgr; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_interface *interface; + struct fimc_is_mem *mem; + + u32 instance; + u32 instance_sensor; + u32 module; + struct fimc_is_ishcain_mem imemory; + struct fimc_is_from_info finfo; + struct fimc_is_from_info pinfo; + struct is_region *is_region; + + bool force_down; + unsigned long state; + struct mutex mutex_state; + + u32 dzoom_width; + u32 bds_width; + u32 bds_height; + u32 setfile; + u32 color_range; + + struct camera2_sm capability; + struct camera2_uctl cur_peri_ctl; + struct camera2_uctl peri_ctls[SENSOR_MAX_CTL]; + + /*isp margin*/ + u32 margin_left; + u32 margin_right; + u32 margin_width; + u32 margin_top; + u32 margin_bottom; + u32 margin_height; + + /* chain0 : isp ~ scc */ + struct fimc_is_group group_3aa; + struct fimc_is_subdev taac; + struct fimc_is_subdev taap; + + u32 taa_size_forceset; + u32 taa_size_changed_fcount; + + struct fimc_is_group group_isp; + u32 chain0_width; + u32 chain0_height; + struct fimc_is_subdev drc; + + u32 isp_size_forceset; + u32 isp_size_changed_fcount; + + /* chain1 : scc ~ dis */ + struct fimc_is_subdev scc; + u32 chain1_width; + u32 chain1_height; + u32 crop_x; + u32 crop_y; + u32 crop_width; + u32 crop_height; + struct fimc_is_subdev dis; + u32 dis_width; + u32 dis_height; + + /* chain2 : dis ~ scp */ + struct fimc_is_group group_dis; + u32 chain2_width; + u32 chain2_height; + struct fimc_is_subdev dnr; + + /* chain3 : scp ~ fd */ + struct fimc_is_subdev scp; + u32 chain3_width; + u32 chain3_height; + struct fimc_is_subdev fd; + + u32 private_data; + struct fimc_is_device_sensor *sensor; + struct pm_qos_request user_qos; +}; + +/*global function*/ +int fimc_is_ischain_probe(struct fimc_is_device_ischain *device, + struct fimc_is_interface *interface, + struct fimc_is_resourcemgr *resourcemgr, + struct fimc_is_groupmgr *groupmgr, + struct fimc_is_mem *mem, + struct platform_device *pdev, + u32 instance, + u32 regs); +int fimc_is_ischain_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx, + struct fimc_is_minfo *minfo); +int fimc_is_ischain_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_ischain_init_wrap(struct fimc_is_device_ischain *device, + u32 input); +int fimc_is_ischain_g_capability(struct fimc_is_device_ischain *this, + u32 user_ptr); +int fimc_is_ischain_print_status(struct fimc_is_device_ischain *this); +void fimc_is_ischain_meta_invalid(struct fimc_is_frame *frame); + +/* 3AA subdev */ +int fimc_is_ischain_3aa_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_ischain_3aa_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_ischain_3aa_s_input(struct fimc_is_device_ischain *device, + u32 input); +int fimc_is_ischain_3aa_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_ischain_3aa_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_ischain_3aa_reqbufs(struct fimc_is_device_ischain *device, + u32 count); +int fimc_is_ischain_3aa_s_format(struct fimc_is_device_ischain *device, + u32 width, u32 height); +int fimc_is_ischain_3aa_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index); +int fimc_is_ischain_3aa_buffer_finish(struct fimc_is_device_ischain *device, + u32 index); + +/* isp subdev */ +int fimc_is_ischain_isp_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_ischain_isp_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_ischain_isp_reqbufs(struct fimc_is_device_ischain *device, + u32 count); +int fimc_is_ischain_isp_s_format(struct fimc_is_device_ischain *this, + u32 width, u32 height); +int fimc_is_ischain_isp_s_input(struct fimc_is_device_ischain *this, + u32 input); +int fimc_is_ischain_isp_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index); +int fimc_is_ischain_isp_buffer_finish(struct fimc_is_device_ischain *this, + u32 index); + +/*scc subdev*/ +/*scp subdev*/ +int fimc_is_ischain_scp_s_format(struct fimc_is_device_ischain *device, + u32 pixelformat, u32 width, u32 height); + +/* vdisc subdev */ +int fimc_is_ischain_dis_start(struct fimc_is_device_ischain *this, + bool bypass); +int fimc_is_ischain_dis_stop(struct fimc_is_device_ischain *this); +int fimc_is_ischain_vdc_s_format(struct fimc_is_device_ischain *this, + u32 width, u32 height); + +/* vdiso subdev */ +int fimc_is_ischain_vdo_open(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_ischain_vdo_close(struct fimc_is_device_ischain *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_ischain_vdo_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue); +int fimc_is_ischain_vdo_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *leader, + struct fimc_is_queue *queue); +int fimc_is_ischain_vdo_s_format(struct fimc_is_device_ischain *this, + u32 width, u32 height); +int fimc_is_ischain_vdo_buffer_queue(struct fimc_is_device_ischain *device, + struct fimc_is_queue *queue, + u32 index); +int fimc_is_ischain_vdo_buffer_finish(struct fimc_is_device_ischain *this, + u32 index); + +/*special api for sensor*/ +int fimc_is_ischain_3aa_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame); +int fimc_is_ischain_isp_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame); +int fimc_is_ischain_dis_callback(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame); +int fimc_is_ischain_camctl(struct fimc_is_device_ischain *this, + struct fimc_is_frame *frame, + u32 fcount); +int fimc_is_ischain_tag(struct fimc_is_device_ischain *ischain, + struct fimc_is_frame *frame); + +int fimc_is_itf_stream_on(struct fimc_is_device_ischain *this); +int fimc_is_itf_stream_off(struct fimc_is_device_ischain *this); +int fimc_is_itf_process_start(struct fimc_is_device_ischain *device, + u32 group); +int fimc_is_itf_process_stop(struct fimc_is_device_ischain *device, + u32 group); +int fimc_is_itf_force_stop(struct fimc_is_device_ischain *device, + u32 group); +int fimc_is_itf_map(struct fimc_is_device_ischain *device, + u32 group, u32 shot_addr, u32 shot_size); +int fimc_is_itf_i2c_lock(struct fimc_is_device_ischain *this, + int i2c_clk, bool lock); + +extern const struct fimc_is_queue_ops fimc_is_ischain_3aa_ops; +extern const struct fimc_is_queue_ops fimc_is_ischain_isp_ops; +extern const struct fimc_is_queue_ops fimc_is_ischain_vdo_ops; +extern const struct fimc_is_queue_ops fimc_is_ischain_sub_ops; + +int fimc_is_itf_power_down(struct fimc_is_interface *interface); +int fimc_is_ischain_power(struct fimc_is_device_ischain *this, int on); +void fimc_is_ischain_savefirm(struct fimc_is_device_ischain *this); + +#define IS_ISCHAIN_OTF(device) \ + (test_bit(FIMC_IS_GROUP_OTF_INPUT, &(device)->group_3aa.state)) +#define IS_EQUAL_COORD(i, o) \ + (((i)[0] != (o)[0]) || ((i)[1] != (o)[1]) || \ + ((i)[2] != (o)[2]) || ((i)[3] != (o)[3])) +#define IS_NULL_COORD(c) \ + (!(c)[0] && !(c)[1] && !(c)[2] && !(c)[3]) +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.c new file mode 100644 index 000000000000..f1bbe3fa9141 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.c @@ -0,0 +1,1535 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE +#include +#endif + +#include "fimc-is-core.h" +#include "fimc-is-interface.h" +#include "fimc-is-sec-define.h" +#include "fimc-is-device-ischain.h" +#include "fimc-is-dt.h" +#include "fimc-is-device-ois.h" +#ifdef CONFIG_AF_HOST_CONTROL +#include "fimc-is-device-af.h" +#endif + +#define FIMC_IS_OIS_SDCARD_PATH "/data/media/0/" +#define FIMC_IS_OIS_DEV_NAME "exynos-fimc-is-ois" +#define FIMC_OIS_FW_NAME_SEC "fimc_is_ois_63B.bin" +#define FIMC_OIS_FW_NAME_DOM "fimc_is_ois_63A.bin" +#define OIS_BOOT_FW_SIZE (1024 * 4) +#define OIS_PROG_FW_SIZE (1024 * 24) +#define FW_GYRO_SENSOR 0 +#define FW_DRIVER_IC 1 +#define FW_CORE_VERSION 2 +#define FW_RELEASE_MONTH 3 +#define FW_RELEASE_COUNT 4 +#define FW_TRANS_SIZE 256 +#define OIS_VER_OFFSET 14 +#define OIS_BIN_LEN 28672 +#define OIS_BIN_HEADER 28658 +#define OIS_FW_HEADER_SIZE 6 +static u8 bootCode[OIS_BOOT_FW_SIZE] = {0,}; +static u8 progCode[OIS_PROG_FW_SIZE] = {0,}; + +static struct fimc_is_ois_info ois_minfo; +static struct fimc_is_ois_info ois_pinfo; +static struct fimc_is_ois_info ois_uinfo; +static struct fimc_is_ois_exif ois_exif_data; +static bool fw_sdcard; +static bool not_crc_bin; +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE +static struct task_struct *ois_ts; +#endif + +static void fimc_is_ois_i2c_config(struct i2c_client *client, bool onoff) +{ + struct pinctrl *pinctrl_i2c = NULL; + struct device *i2c_dev = client->dev.parent->parent; + struct fimc_is_device_ois *ois_device = i2c_get_clientdata(client); + + if (ois_device->ois_hsi2c_status != onoff) { + info("%s : ois_hsi2c_stauts(%d),onoff(%d)\n",__func__, + ois_device->ois_hsi2c_status, onoff); + + if (onoff) { + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + + pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "on_i2c"); + if (IS_ERR_OR_NULL(pinctrl_i2c)) { + printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__); + } else { + devm_pinctrl_put(pinctrl_i2c); + } + } else { + pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "off_i2c"); + if (IS_ERR_OR_NULL(pinctrl_i2c)) { + printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__); + } else { + devm_pinctrl_put(pinctrl_i2c); + } + + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 2)); + } + ois_device->ois_hsi2c_status = onoff; + } +} + +int fimc_is_ois_i2c_read(struct i2c_client *client, u16 addr, u8 *data) +{ + int err; + u8 txbuf[2], rxbuf[1]; + struct i2c_msg msg[2]; + + *data = 0; + txbuf[0] = (addr & 0xff00) >> 8; + txbuf[1] = (addr & 0xff); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = txbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = rxbuf; + + err = i2c_transfer(client->adapter, msg, 2); + if (unlikely(err != 2)) { + err("%s: register read fail err = %d\n", __func__, err); + return -EIO; + } + + *data = rxbuf[0]; + return 0; +} + +int fimc_is_ois_i2c_write(struct i2c_client *client ,u16 addr, u8 data) +{ + int retries = I2C_RETRY_COUNT; + int ret = 0, err = 0; + u8 buf[3] = {0,}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 3, + .buf = buf, + }; + + buf[0] = (addr & 0xff00) >> 8; + buf[1] = addr & 0xff; + buf[2] = data; + +#if 0 + pr_info("%s : W(0x%02X%02X %02X)\n",__func__, buf[0], buf[1], buf[2]); +#endif + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + + usleep_range(10000,11000); + err = ret; + } while (--retries > 0); + + /* Retry occured */ + if (unlikely(retries < I2C_RETRY_COUNT)) { + err("i2c_write: error %d, write (%04X, %04X), retry %d\n", + err, addr, data, I2C_RETRY_COUNT - retries); + } + + if (unlikely(ret != 1)) { + err("I2C does not work\n\n"); + return -EIO; + } + + return 0; +} + +int fimc_is_ois_i2c_write_multi(struct i2c_client *client ,u16 addr, u8 *data, size_t size) +{ + int retries = I2C_RETRY_COUNT; + int ret = 0, err = 0, i = 0; + u8 buf[258] = {0,}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = size, + .buf = buf, + }; + + buf[0] = (addr & 0xFF00) >> 8; + buf[1] = addr & 0xFF; + + for (i = 0; i < size - 2; i++) { + buf[i + 2] = *(data + i); + } +#if 0 + pr_info("OISLOG %s : W(0x%02X%02X%02X)\n", __func__, buf[0], buf[1], buf[2]); +#endif + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + + usleep_range(10000,11000); + err = ret; + } while (--retries > 0); + + /* Retry occured */ + if (unlikely(retries < I2C_RETRY_COUNT)) { + err("i2c_write: error %d, write (%04X, %04X), retry %d\n", + err, addr, *data, I2C_RETRY_COUNT - retries); + } + + if (unlikely(ret != 1)) { + err("I2C does not work\n\n"); + return -EIO; + } + + return 0; +} + +static int fimc_is_ois_i2c_read_multi(struct i2c_client *client, u16 addr, u8 *data, size_t size) +{ + int err; + u8 rxbuf[256], txbuf[2]; + struct i2c_msg msg[2]; + + txbuf[0] = (addr & 0xff00) >> 8; + txbuf[1] = (addr & 0xff); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = txbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size; + msg[1].buf = rxbuf; + + err = i2c_transfer(client->adapter, msg, 2); + if (unlikely(err != 2)) { + pr_err("%s: register read fail\n", __func__); + return -EIO; + } + + memcpy(data, rxbuf, size); + return 0; +} + +int fimc_is_ois_gpio_on(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!pdata->gpio_cfg) { + err("gpio_cfg is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->gpio_cfg(device->pdev, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON); + if (ret) { + err("gpio_cfg is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_ois_gpio_off(struct fimc_is_device_companion *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!pdata->gpio_cfg) { + err("gpio_cfg is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->gpio_cfg(device->pdev, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF); + if (ret) { + err("gpio_cfg is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +void fimc_is_ois_enable(struct fimc_is_core *core) +{ + int ret = 0; + + pr_info("%s : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x02, 0x00); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x00, 0x01); + if (ret) { + err("i2c write fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + pr_info("%s : X\n", __FUNCTION__); +} + +int fimc_is_ois_sine_mode(struct fimc_is_core *core, int mode) +{ + int ret = 0; + + pr_info("%s : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + if (mode == OPTICAL_STABILIZATION_MODE_SINE_X) { + ret = fimc_is_ois_i2c_write(core->client1, 0x18, 0x01); + } else if (mode == OPTICAL_STABILIZATION_MODE_SINE_Y) { + ret = fimc_is_ois_i2c_write(core->client1, 0x18, 0x02); + } + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x19, 0x01); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x1A, 0x1E); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x02, 0x03); + if (ret) { + err("i2c write fail\n"); + } + + msleep(20); + + ret = fimc_is_ois_i2c_write(core->client1, 0x00, 0x01); + if (ret) { + err("i2c write fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + pr_info("%s : X\n", __FUNCTION__); + + return ret; +} +EXPORT_SYMBOL(fimc_is_ois_sine_mode); + +void fimc_is_ois_version(struct fimc_is_core *core) +{ + int ret = 0; + u8 val_c = 0, val_d = 0; + u16 version = 0; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x00FC, &val_c); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x00FD, &val_d); + if (ret) { + err("i2c write fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + version = (val_d << 8) | val_c; + pr_info("OIS version = 0x%04x\n", version); +} + +void fimc_is_ois_offset_test(struct fimc_is_core *core, long *raw_data_x, long *raw_data_y) +{ + int ret = 0, i = 0; + u8 val = 0, x = 0, y = 0; + int x_sum = 0, y_sum = 0, sum = 0; + int retries = 0, avg_count = 20; + + pr_info("%s : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x0014, 0x01); + if (ret) { + err("i2c write fail\n"); + } + + retries = avg_count; + do { + ret = fimc_is_ois_i2c_read(core->client1, 0x0014, &val); + if (ret != 0) { + break; + } + msleep(10); + if (--retries < 0) { + err("Read register failed!!!!, data = 0x%04x\n", val); + break; + } + } while (val); + + sum = 0; + retries = avg_count; + for (i = 0; i < retries; retries--) { + fimc_is_ois_i2c_read(core->client1, 0x0248, &val); + x = val; + fimc_is_ois_i2c_read(core->client1, 0x0249, &val); + x_sum = (val << 8) | x; + if (x_sum > 0x7FFF) { + x_sum = -((x_sum ^ 0xFFFF) + 1); + } + sum += x_sum; + } + sum = sum * 10 / avg_count; + *raw_data_x = sum * 1000 / 175 / 10; + + retries = avg_count; + for (i = 0; i < retries; retries--) { + fimc_is_ois_i2c_read(core->client1, 0x024A, &val); + y = val; + fimc_is_ois_i2c_read(core->client1, 0x024B, &val); + y_sum = (val << 8) | y; + if (y_sum > 0x7FFF) { + y_sum = -((y_sum ^ 0xFFFF) + 1); + } + sum += y_sum; + } + sum = sum * 10 / avg_count; + *raw_data_y = sum * 1000 / 175 / 10; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + fimc_is_ois_version(core); + pr_info("%s : X\n", __FUNCTION__); + return; +} + +void fimc_is_ois_get_offset_data(struct fimc_is_core *core, long *raw_data_x, long *raw_data_y) +{ + int i = 0; + u8 val = 0, x = 0, y = 0; + int x_sum = 0, y_sum = 0, sum = 0; + int retries = 0, avg_count = 20; + + pr_info("%s : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + retries = avg_count; + for (i = 0; i < retries; retries--) { + fimc_is_ois_i2c_read(core->client1, 0x0248, &val); + x = val; + fimc_is_ois_i2c_read(core->client1, 0x0249, &val); + x_sum = (val << 8) | x; + if (x_sum > 0x7FFF) { + x_sum = -((x_sum ^ 0xFFFF) + 1); + } + sum += x_sum; + } + sum = sum * 10 / avg_count; + *raw_data_x = sum * 1000 / 175 / 10; + + sum = 0; + retries = avg_count; + for (i = 0; i < retries; retries--) { + fimc_is_ois_i2c_read(core->client1, 0x024A, &val); + y = val; + fimc_is_ois_i2c_read(core->client1, 0x024B, &val); + y_sum = (val << 8) | y; + if (y_sum > 0x7FFF) { + y_sum = -((y_sum ^ 0xFFFF) + 1); + } + sum += y_sum; + } + sum = sum * 10 / avg_count; + *raw_data_y = sum * 1000 / 175 / 10; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + fimc_is_ois_version(core); + pr_info("%s : X\n", __FUNCTION__); + return; +} + +int fimc_is_ois_self_test(struct fimc_is_core *core) +{ + int ret = 0; + u8 val = 0; + int retries = 20; + + pr_info("%s : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x0014, 0x08); + if (ret) { + err("i2c write fail\n"); + } + + do { + ret = fimc_is_ois_i2c_read(core->client1, 0x0014, &val); + if (ret != 0) { + val = -EIO; + break; + } + msleep(10); + if (--retries < 0) { + err("Read register failed!!!!, data = 0x%04x\n", val); + break; + } + } while (val); + + ret = fimc_is_ois_i2c_read(core->client1, 0x0004, &val); + if (ret != 0) { + val = -EIO; + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + pr_info("%s(%d) : X\n", __FUNCTION__, val); + return (int)val; +} + +bool fimc_is_ois_diff_test(struct fimc_is_core *core, int *x_diff, int *y_diff) +{ + int ret = 0; + u8 val = 0, x = 0, y = 0; + u16 x_min = 0, y_min = 0, x_max = 0, y_max = 0; + int retries = 20, default_diff = 1100; + u8 read_x[2], read_y[2]; + + pr_info("(%s) : E\n", __FUNCTION__); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + +#ifdef CONFIG_AF_HOST_CONTROL + fimc_is_af_move_lens(core); + msleep(30); +#endif + ret = fimc_is_ois_i2c_read_multi(core->client1, 0x021A, read_x, 2); + ret |= fimc_is_ois_i2c_read_multi(core->client1, 0x021C, read_y, 2); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_write_multi(core->client1, 0x0022, read_x, 4); + ret |= fimc_is_ois_i2c_write_multi(core->client1, 0x0024, read_y, 4); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0002, 0x02); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0000, 0x01); + if (ret) { + err("i2c write fail\n"); + } + msleep(400); + pr_info("(%s) : OIS Position = Center\n", __FUNCTION__); + + ret = fimc_is_ois_i2c_write(core->client1, 0x0000, 0x00); + if (ret) { + err("i2c write fail\n"); + } + + do { + ret = fimc_is_ois_i2c_read(core->client1, 0x0001, &val); + if (ret != 0) { + break; + } + msleep(10); + if (--retries < 0) { + err("Read register failed!!!!, data = 0x%04x\n", val); + break; + } + } while (val != 1); + + ret = fimc_is_ois_i2c_write(core->client1, 0x0034, 0x64); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0230, 0x64); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0231, 0x00); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0232, 0x64); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0233, 0x00); + if (ret) { + err("i2c write fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x0034, &val); + err("OIS[read_val_0x0034::0x%04x]\n", val); + ret |= fimc_is_ois_i2c_read(core->client1, 0x0230, &val); + err("OIS[read_val_0x0230::0x%04x]\n", val); + ret |= fimc_is_ois_i2c_read(core->client1, 0x0231, &val); + err("OIS[read_val_0x0231::0x%04x]\n", val); + ret |= fimc_is_ois_i2c_read(core->client1, 0x0232, &val); + err("OIS[read_val_0x0232::0x%04x]\n", val); + ret |= fimc_is_ois_i2c_read(core->client1, 0x0233, &val); + err("OIS[read_val_0x0233::0x%04x]\n", val); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_write(core->client1, 0x020E, 0x00); + ret |= fimc_is_ois_i2c_write(core->client1, 0x020F, 0x00); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0210, 0x50); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0211, 0x50); + if (ret) { + err("i2c write fail\n"); + } + ret = fimc_is_ois_i2c_write(core->client1, 0x0013, 0x01); + if (ret) { + err("i2c write fail\n"); + } + +#if 0 + ret = fimc_is_ois_i2c_write(core->client1, 0x0012, 0x01); + if (ret) { + err("i2c write fail\n"); + } + + retries = 30; + do { //polarity check + ret = fimc_is_ois_i2c_read(core->client1, 0x0012, &val); + if (ret != 0) { + break; + } + msleep(100); + if (--retries < 0) { + err("Polarity check is not done or not [read_val_0x0012::0x%04x]\n", val); + break; + } + } while (val); + fimc_is_ois_i2c_read(core->client1, 0x0200, &val); + err("OIS[read_val_0x0200::0x%04x]\n", val); +#endif + + retries = 120; + do { + ret = fimc_is_ois_i2c_read(core->client1, 0x0013, &val); + if (ret != 0) { + break; + } + msleep(100); + if (--retries < 0) { + err("Read register failed!!!!, data = 0x%04x\n", val); + break; + } + } while (val); + + fimc_is_ois_i2c_read(core->client1, 0x0212, &val); + x = val; + fimc_is_ois_i2c_read(core->client1, 0x0213, &val); + x_max = (val << 8) | x; + fimc_is_ois_i2c_read(core->client1, 0x0214, &val); + x = val; + fimc_is_ois_i2c_read(core->client1, 0x0215, &val); + x_min = (val << 8) | x; + + fimc_is_ois_i2c_read(core->client1, 0x0216, &val); + y = val; + fimc_is_ois_i2c_read(core->client1, 0x0217, &val); + y_max = (val << 8) | y; + fimc_is_ois_i2c_read(core->client1, 0x0218, &val); + y = val; + fimc_is_ois_i2c_read(core->client1, 0x0219, &val); + y_min = (val << 8) | y; + + *x_diff = abs(x_max - x_min); + *y_diff = abs(y_max - y_min); + + pr_info("(%s) : X (default_diff:%d)(%d,%d)\n", __FUNCTION__, + default_diff, *x_diff, *y_diff); + + ret = fimc_is_ois_i2c_read_multi(core->client1, 0x021A, read_x, 2); + ret |= fimc_is_ois_i2c_read_multi(core->client1, 0x021C, read_y, 2); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_write_multi(core->client1, 0x0022, read_x, 4); + ret |= fimc_is_ois_i2c_write_multi(core->client1, 0x0024, read_y, 4); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0002, 0x02); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0000, 0x01); + msleep(400); + ret |= fimc_is_ois_i2c_write(core->client1, 0x0000, 0x00); + if (ret) { + err("i2c write fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + if (*x_diff > default_diff && *y_diff > default_diff) { + return true; + } else { + return false; + } +} + +u16 fimc_is_ois_calc_checksum(u8 *data, int size) +{ + int i = 0; + u16 result = 0; + + for(i = 0; i < size; i += 2) { + result = result + (0xFFFF & (((*(data + i + 1)) << 8) | (*(data + i)))); + } + + return result; +} + +void fimc_is_ois_exif_data(struct fimc_is_core *core) +{ + int ret = 0; + u8 error_reg[2], status_reg; + u16 error_sum; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x0004, &error_reg[0]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x0005, &error_reg[1]); + if (ret) { + err("i2c read fail\n"); + } + + error_sum = (error_reg[1] << 8) | error_reg[0]; + + ret = fimc_is_ois_i2c_read(core->client1, 0x0001, &status_reg); + if (ret) { + err("i2c read fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + ois_exif_data.error_data = error_sum; + ois_exif_data.status_data = status_reg; + + return; +} + +int fimc_is_ois_get_exif_data(struct fimc_is_ois_exif **exif_info) +{ + *exif_info = &ois_exif_data; + return 0; +} + +void fimc_is_ois_fw_version(struct fimc_is_core *core) +{ + int ret = 0; + char version[7] = {0, }; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x00F9, &version[0]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x00F8, &version[1]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x007C, &version[2]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x007D, &version[3]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x007E, &version[4]); + if (ret) { + err("i2c read fail\n"); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x007F, &version[5]); + if (ret) { + err("i2c read fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + memcpy(ois_minfo.header_ver, version, 6); + core->ois_ver_read = true; + + return; +} + +int fimc_is_ois_get_module_version(struct fimc_is_ois_info **minfo) +{ + *minfo = &ois_minfo; + return 0; +} + +int fimc_is_ois_get_phone_version(struct fimc_is_ois_info **pinfo) +{ + *pinfo = &ois_pinfo; + return 0; +} + +int fimc_is_ois_get_user_version(struct fimc_is_ois_info **uinfo) +{ + *uinfo = &ois_uinfo; + return 0; +} + + +int fimc_is_ois_fw_revision(char *fw_ver) +{ + int revision = 0; + revision = revision + ((int)fw_ver[FW_RELEASE_MONTH] - 64) * 100; + revision = revision + ((int)fw_ver[FW_RELEASE_COUNT] - 48) * 10; + revision = revision + (int)fw_ver[FW_RELEASE_COUNT + 1] - 48; + + return revision; +} + +bool fimc_is_ois_version_compare(char *fw_ver1, char *fw_ver2, char *fw_ver3) +{ + if (fw_ver1[FW_GYRO_SENSOR] != fw_ver2[FW_GYRO_SENSOR] + || fw_ver1[FW_DRIVER_IC] != fw_ver2[FW_DRIVER_IC] + || fw_ver1[FW_CORE_VERSION] != fw_ver2[FW_CORE_VERSION]) { + return false; + } + + if (fw_ver2[FW_GYRO_SENSOR] != fw_ver3[FW_GYRO_SENSOR] + || fw_ver2[FW_DRIVER_IC] != fw_ver3[FW_DRIVER_IC] + || fw_ver2[FW_CORE_VERSION] != fw_ver3[FW_CORE_VERSION]) { + return false; + } + + return true; +} + +bool fimc_is_ois_version_compare_default(char *fw_ver1, char *fw_ver2) +{ + if (fw_ver1[FW_GYRO_SENSOR] != fw_ver2[FW_GYRO_SENSOR] + || fw_ver1[FW_DRIVER_IC] != fw_ver2[FW_DRIVER_IC] + || fw_ver1[FW_CORE_VERSION] != fw_ver2[FW_CORE_VERSION]) { + return false; + } + + return true; +} + +bool fimc_is_ois_read_userdata(struct fimc_is_core *core) +{ + u8 SendData[2], RcvData[256]; + struct i2c_client *client = core->client1; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + fimc_is_ois_i2c_write(client ,0x000F, 0x40); + SendData[0] = 0x00; + SendData[1] = 0x00; + fimc_is_ois_i2c_write_multi(client, 0x0010, SendData, 4); + fimc_is_ois_i2c_write(client ,0x000E, 0x04); + + fimc_is_ois_i2c_read(client, 0x000E, &RcvData[0]); + if (RcvData[0] == 0x14) { + fimc_is_ois_i2c_read_multi(client, 0x0100, RcvData, FW_TRANS_SIZE); + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + memcpy(ois_uinfo.header_ver, RcvData, 6); + return true; + } else { + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + return false; + } +} + +int fimc_is_ois_open_fw(struct fimc_is_core *core, char *name, u8 **buf) +{ + int ret = 0; + u32 size = 0; + const struct firmware *fw_blob = NULL; + static char fw_name[100]; + struct file *fp = NULL; + mm_segment_t old_fs; + long nread; + int fw_requested = 1; + int retry_count = 0; + + fw_sdcard = false; + old_fs = get_fs(); + set_fs(KERNEL_DS); + snprintf(fw_name, sizeof(fw_name), "%s%s", FIMC_IS_OIS_SDCARD_PATH, name); + fp = filp_open(fw_name, O_RDONLY, 0); + if (IS_ERR_OR_NULL(fp)) { + err("failed to open SDCARD fw!!!"); + goto request_fw; + } + + fw_requested = 0; + size = fp->f_path.dentry->d_inode->i_size; + pr_info("start read sdcard, file path %s, size %d Bytes\n", fw_name, size); + + *buf = vmalloc(size); + if (!(*buf)) { + err("failed to allocate memory"); + ret = -ENOMEM; + goto p_err; + } + + nread = vfs_read(fp, (char __user *)(*buf), size, &fp->f_pos); + if (nread != size) { + err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto p_err; + } + + memcpy(ois_pinfo.header_ver, *buf + OIS_BIN_HEADER, OIS_FW_HEADER_SIZE); + memcpy(bootCode, *buf, OIS_BOOT_FW_SIZE); + memcpy(progCode, *buf + OIS_BIN_LEN - OIS_PROG_FW_SIZE, OIS_PROG_FW_SIZE); + fw_sdcard = true; + if (OIS_BIN_LEN >= nread) { + not_crc_bin = true; + err("ois fw binary size = %ld.\n", nread); + } + +request_fw: + if (fw_requested) { + snprintf(fw_name, sizeof(fw_name), "%s", name); + set_fs(old_fs); + retry_count = 3; + ret = request_firmware(&fw_blob, fw_name, &core->companion->pdev->dev); + while (--retry_count && ret == -EAGAIN) { + err("request_firmware retry(count:%d)", retry_count); + ret = request_firmware(&fw_blob, fw_name, &core->companion->pdev->dev); + } + + if (ret) { + err("request_firmware is fail(ret:%d)", ret); + ret = -EINVAL; + goto p_err; + } + + if (!fw_blob) { + err("fw_blob is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (!fw_blob->data) { + err("fw_blob->data is NULL"); + ret = -EINVAL; + goto p_err; + } + + size = fw_blob->size; + + *buf = vmalloc(size); + if (!(*buf)) { + err("failed to allocate memory"); + ret = -ENOMEM; + goto p_err; + } + memcpy((void *)(*buf), fw_blob->data, size); + memcpy(ois_pinfo.header_ver, *buf + OIS_BIN_HEADER, OIS_FW_HEADER_SIZE); + memcpy(bootCode, *buf, OIS_BOOT_FW_SIZE); + memcpy(progCode, *buf + OIS_BIN_LEN - OIS_PROG_FW_SIZE, OIS_PROG_FW_SIZE); + if (OIS_BIN_LEN >= size) { + not_crc_bin = true; + err("ois fw binary size = 0x%08x.\n", size); + } + pr_info("OIS firmware is loaded from Phone binary.\n"); + } + +p_err: + if (!fw_requested) { + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, current->files); + } + set_fs(old_fs); + } else { + if (!IS_ERR_OR_NULL(fw_blob)) { + release_firmware(fw_blob); + } + } + + return ret; +} + +void fimc_is_ois_check_fw(struct fimc_is_core *core) +{ + u8 *buf = NULL; + int ret = 0; + + fimc_is_ois_fw_version(core); + fimc_is_ois_read_userdata(core); + + if (ois_minfo.header_ver[2] == 'A') { + ret = fimc_is_ois_open_fw(core, FIMC_OIS_FW_NAME_DOM, &buf); + } else if (ois_minfo.header_ver[2] == 'B') { + ret = fimc_is_ois_open_fw(core, FIMC_OIS_FW_NAME_SEC, &buf); + } + if (ret) { + err("fimc_is_ois_open_fw failed(%d)\n", ret); + } + + if (buf) { + vfree(buf); + } + + return; +} + +u8 fimc_is_ois_read_status(struct fimc_is_core *core) +{ + int ret = 0; + u8 status = 0; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x0006, &status); + if (ret) { + err("i2c read fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + return status; +} + +u8 fimc_is_ois_read_cal_checksum(struct fimc_is_core *core) +{ + int ret = 0; + u8 status = 0; + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + ret = fimc_is_ois_i2c_read(core->client1, 0x0005, &status); + if (ret) { + err("i2c read fail\n"); + } + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + return status; +} + +void fimc_is_ois_fw_status(struct fimc_is_core *core, u8 *checksum, u8 *caldata) +{ + *checksum = fimc_is_ois_read_status(core); + *caldata = fimc_is_ois_read_cal_checksum(core); + + return; +} + +bool fimc_is_ois_crc_check(struct fimc_is_core *core, char *buf) +{ + u8 check_8[4] = {0, }; + u32 *buf32 = NULL; + u32 checksum; + u32 checksum_bin; + + if (not_crc_bin) { + err("ois binary does not conatin crc checksum.\n"); + return false; + } + + if (buf == NULL) { + err("buf is NULL. CRC check failed."); + return false; + } + + buf32 = (u32 *)buf; + + memcpy(check_8, buf + OIS_BIN_LEN, 4); + checksum_bin = (check_8[3] << 24) | (check_8[2] << 16) | (check_8[1] << 8) | (check_8[0]); + + checksum = getCRC((u16 *)&buf32[0], OIS_BIN_LEN / 2, NULL, NULL); + if (checksum != checksum_bin) { + return false; + } else { + return true; + } +} + +void fimc_is_ois_fw_update(struct fimc_is_core *core) +{ + u8 SendData[256], RcvData; + u16 RcvDataShort = 0; + u8 data[2] = {0, }; + int block, i, position = 0; + u16 checkSum; + u8 *buf = NULL; + int ret = 0, sum_size = 0, retry_count = 3; + int module_ver = 0, binary_ver = 0; + u8 error_status = 0; + bool forced_update = false, crc_result = false; + bool need_reset = false; + struct i2c_client *client = core->client1; + + /* OIS Status Check */ + error_status = fimc_is_ois_read_status(core); + if (error_status == 0x01) { + forced_update = true; + } + + fimc_is_ois_fw_version(core); + fimc_is_ois_read_userdata(core); + if (ois_minfo.header_ver[2] == 'A') { + ret = fimc_is_ois_open_fw(core, FIMC_OIS_FW_NAME_DOM, &buf); + } else if (ois_minfo.header_ver[2] == 'B') { + ret = fimc_is_ois_open_fw(core, FIMC_OIS_FW_NAME_SEC, &buf); + } + if (ret) { + err("fimc_is_ois_open_fw failed(%d)", ret); + goto p_err; + } + + if (buf == NULL) { + err("buf is NULL. OIS FW Update failed."); + return; + } + + if (!forced_update) { + if (ois_uinfo.header_ver[0] == 0xFF && ois_uinfo.header_ver[1] == 0xFF && + ois_uinfo.header_ver[2] == 0xFF) { + err("OIS userdata is not valid."); + goto p_err; + } + } + + /*Update OIS FW when Gyro sensor/Driver IC/ Core version is same*/ + if (!forced_update) { + if (!fimc_is_ois_version_compare(ois_minfo.header_ver, ois_pinfo.header_ver, + ois_uinfo.header_ver)) { + err("Does not update ois fw. OIS module ver = %s, binary ver = %s, userdata ver = %s", + ois_minfo.header_ver, ois_pinfo.header_ver, ois_uinfo.header_ver); + goto p_err; + } + } else { + if (!fimc_is_ois_version_compare_default(ois_minfo.header_ver, ois_pinfo.header_ver)) { + err("Does not update ois fw. OIS module ver = %s, binary ver = %s", + ois_minfo.header_ver, ois_pinfo.header_ver); + goto p_err; + } + } + + crc_result = fimc_is_ois_crc_check(core, buf); + if (crc_result == false) { + err("OIS CRC32 error.\n"); + goto p_err; + } + + if (!fw_sdcard && !forced_update) { + module_ver = fimc_is_ois_fw_revision(ois_minfo.header_ver); + binary_ver = fimc_is_ois_fw_revision(ois_pinfo.header_ver); + if (binary_ver <= module_ver) { + err("OIS module ver = %d, binary ver = %d", module_ver, binary_ver); + goto p_err; + } + } + info("OIS fw update started. module ver = %s, binary ver = %s, userdata ver = %s\n", + ois_minfo.header_ver, ois_pinfo.header_ver, ois_uinfo.header_ver); + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + +retry: + if (need_reset) { + fimc_is_ois_gpio_off(core->companion); + msleep(30); + fimc_is_ois_gpio_on(core->companion); + msleep(150); + } + + fimc_is_ois_i2c_read(client, 0x01, &RcvData); /* OISSTS Read */ + + if (RcvData != 0x01) {/* OISSTS != IDLE */ + err("RCV data return!!"); + goto p_err; + } + + /* Update a Boot Program */ + /* SET FWUP_CTRL REG */ + fimc_is_ois_i2c_write(client ,0x000C, 0x0B); + msleep(20); + + position = 0; + /* Write Boot Data */ + for (block = 4; block < 8; block++) { /* 0x1000 - 0x1FFF */ + for (i = 0; i < 4; i++) { + memcpy(SendData, bootCode + position, FW_TRANS_SIZE); + fimc_is_ois_i2c_write_multi(client, 0x0100, SendData, FW_TRANS_SIZE + 2); + position += FW_TRANS_SIZE; + if (i == 0 ) { + msleep(20); + } else if ((i == 1) || (i == 2)) { /* Wait Erase and Write process */ + msleep(10); /* Wait Write process */ + } else { + msleep(15); /* Wait Write and Verify process */ + } + } + } + + /* CHECKSUM (Boot) */ + sum_size = OIS_BOOT_FW_SIZE; + checkSum = fimc_is_ois_calc_checksum(&bootCode[0], sum_size); + SendData[0] = (checkSum & 0x00FF); + SendData[1] = (checkSum & 0xFF00) >> 8; + SendData[2] = 0x00; + SendData[3] = 0x00; + fimc_is_ois_i2c_write_multi(client, 0x08, SendData, 6); + msleep(10); /* (RUMBA Self Reset) */ + + fimc_is_ois_i2c_read_multi(client, 0x06, data, 2); /* Error Status read */ + info("%s: OISSTS Read = 0x%02x%02x\n", __func__, data[1], data[0]); + + if (data[1] == 0x00 && data[0] == 0x01) { /* Boot F/W Update Error check */ + /* Update a User Program */ + /* SET FWUP_CTRL REG */ + position = 0; + SendData[0] = 0x09; /* FWUP_DSIZE=256Byte, FWUP_MODE=PROG, FWUPEN=Start */ + fimc_is_ois_i2c_write(client, 0x0C, SendData[0]); /* FWUPCTRL REG(0x000C) 1Byte Send */ + + /* Write UserProgram Data */ + for (block = 4; block <= 27; block++) { /* 0x1000 -0x 6FFF (RUMBA-SA)*/ + for (i = 0; i < 4; i++) { + memcpy(SendData, &progCode[position], (size_t)FW_TRANS_SIZE); + fimc_is_ois_i2c_write_multi(client, 0x100, SendData, FW_TRANS_SIZE + 2); /* FLS_DATA REG(0x0100) 256Byte Send */ + position += FW_TRANS_SIZE; + if (i ==0 ) { + msleep(20); /* Wait Erase and Write process */ + } else if ((i==1) || (i==2)) { + msleep(10); /* Wait Write process */ + } else { + msleep(15); /* Wait Write and Verify process */ + } + } + } + + /* CHECKSUM (Program) */ + sum_size = OIS_PROG_FW_SIZE; + checkSum = fimc_is_ois_calc_checksum(&progCode[0], sum_size); + SendData[0] = (checkSum & 0x00FF); + SendData[1] = (checkSum & 0xFF00) >> 8; + SendData[2] = 0; /* Don't Care */ + SendData[3] = 0x80; /* Self Reset Request */ + + fimc_is_ois_i2c_write_multi(client, 0x08, SendData, 6); /* FWUP_CHKSUM REG(0x0008) 4Byte Send */ + msleep(20); /* (Wait RUMBA Self Reset) */ + fimc_is_ois_i2c_read(client, 0x6, (u8*)&RcvDataShort); /* Error Status read */ + + if (RcvDataShort == 0x0000) { + /* F/W Update Success Process */ + info("%s: OISLOG OIS program update success", __func__); + } else { + /* Retry Process */ + if (retry_count > 0) { + err("OISLOG OIS program update fail, retry update. count = %d", retry_count); + retry_count--; + need_reset = true; + goto retry; + } + + /* F/W Update Error Process */ + err("OISLOG OIS program update fail"); + goto p_err; + } + } else { + /* Retry Process */ + if (retry_count > 0) { + err("OISLOG OIS boot update fail, retry update. count = %d", retry_count); + retry_count--; + need_reset = true; + goto retry; + } + + /* Error Process */ + err("CAN'T process OIS program update"); + goto p_err; + } + + /* S/W Reset */ + fimc_is_ois_i2c_write(client ,0x000D, 0x01); + fimc_is_ois_i2c_write(client ,0x000E, 0x06); + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + msleep(30); + fimc_is_ois_fw_version(core); + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, true); + } + + if (!fimc_is_ois_version_compare_default(ois_minfo.header_ver, ois_pinfo.header_ver)) { + err("After update ois fw is not correct. OIS module ver = %s, binary ver = %s", + ois_minfo.header_ver, ois_pinfo.header_ver); + goto p_err; + } + + /* Param init - Flash to Rumba */ + fimc_is_ois_i2c_write(client ,0x0036, 0x03); + msleep(30); + +p_err: + info("OIS module ver = %s, binary ver = %s, userdata ver = %s\n", + ois_minfo.header_ver, ois_pinfo.header_ver, ois_uinfo.header_ver); + + if (core->use_ois_hsi2c) { + fimc_is_ois_i2c_config(core->client1, false); + } + + if (buf) { + vfree(buf); + } + return; +} + +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE +int fimc_is_ois_thread(void *data) +{ + struct fimc_is_core *core = data; + + fimc_is_ois_gpio_on(core->companion); + msleep(150); + fimc_is_ois_fw_update(core); + fimc_is_ois_gpio_off(core->companion); + + return 0; +} + +void fimc_is_ois_init_thread(struct fimc_is_core *core) +{ + pr_info("OIS fimc_is_ois_init_thread\n"); + + ois_ts = kthread_run(fimc_is_ois_thread, core, "ois_thread"); + if (IS_ERR_OR_NULL(ois_ts)) + err("failed to create a thread for ois fw update\n"); + + return; +} +#endif /* CONFIG_OIS_FW_UPDATE_THREAD_USE */ + +static int fimc_is_ois_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fimc_is_device_ois *device; + struct ois_i2c_platform_data *pdata; + struct fimc_is_core *core; + struct device *i2c_dev; + struct pinctrl *pinctrl_i2c = NULL; + + if (fimc_is_dev == NULL) { + warn("fimc_is_dev is not yet probed"); + client->dev.init_name = FIMC_IS_OIS_DEV_NAME; + return -EPROBE_DEFER; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) + return -EINVAL; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct ois_i2c_platform_data), GFP_KERNEL); + if (!pdata) { + err("Failed to allocate memory\n"); + return -ENOMEM; + } + } else { + pdata = client->dev.platform_data; + } + + if (pdata == NULL) { + err("%s : ois probe fail\n", __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err("No I2C functionality found\n"); + devm_kfree(&client->dev, pdata); + return -ENODEV; + } + + device = kzalloc(sizeof(struct fimc_is_device_ois), GFP_KERNEL); + if (!device) { + err("fimc_is_device_companion is NULL"); + devm_kfree(&client->dev, pdata); + return -ENOMEM; + } + + device->ois_hsi2c_status = false; + core->client1 = client; + i2c_set_clientdata(client, device); + fw_sdcard = false; + core->ois_ver_read = false; + not_crc_bin = false; + + /* Initial i2c pin */ + i2c_dev = client->dev.parent->parent; + pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "off_i2c"); + if (IS_ERR_OR_NULL(pinctrl_i2c)) { + printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__); + } else { + devm_pinctrl_put(pinctrl_i2c); + } + + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_FUNC, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-2", + PINCFG_PACK(PINCFG_TYPE_PUD, 0)); + pin_config_set(FIMC_IS_SPI_PINNAME, "gpc2-3", + PINCFG_PACK(PINCFG_TYPE_PUD, 0)); + + return 0; +} + +static int fimc_is_ois_remove(struct i2c_client *client) +{ +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE + if (ois_ts) { + kthread_stop(ois_ts); + ois_ts = NULL; + } +#endif + + return 0; +} + +static const struct i2c_device_id ois_id[] = { + {FIMC_IS_OIS_DEV_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ois_id); + +#ifdef CONFIG_OF +static struct of_device_id ois_dt_ids[] = { + { .compatible = "rumba,ois",}, + {}, +}; +#endif + +static struct i2c_driver ois_i2c_driver = { + .driver = { + .name = FIMC_IS_OIS_DEV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = ois_dt_ids, +#endif + }, + .probe = fimc_is_ois_probe, + .remove = fimc_is_ois_remove, + .id_table = ois_id, +}; +module_i2c_driver(ois_i2c_driver); + +MODULE_DESCRIPTION("OIS driver for Rumba"); +MODULE_AUTHOR("kyoungho yun "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.h new file mode 100644 index 000000000000..b5785c41562d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-ois.h @@ -0,0 +1,53 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS OIS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct fimc_is_device_ois { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + unsigned long state; + struct exynos_platform_fimc_is_sensor *pdata; + struct i2c_client *client; + int ois_en; + bool ois_hsi2c_status; +}; + +struct ois_i2c_platform_data { + int ois_en; +}; + +struct fimc_is_ois_exif { + int error_data; + int status_data; +}; + +int fimc_is_ois_i2c_read(struct i2c_client *client, u16 addr, u8 *data); +int fimc_is_ois_i2c_write(struct i2c_client *client ,u16 addr, u8 data); +void fimc_is_ois_enable(struct fimc_is_core *core); +void fimc_is_ois_offset_test(struct fimc_is_core *core, long *raw_data_x, long *raw_data_y); +int fimc_is_ois_self_test(struct fimc_is_core *core); +int fimc_is_ois_gpio_on(struct fimc_is_device_companion *device); +int fimc_is_ois_gpio_off(struct fimc_is_device_companion *device); +void fimc_is_ois_fw_update(struct fimc_is_core *core); +void fimc_is_ois_fw_version(struct fimc_is_core *core); +int fimc_is_ois_get_module_version(struct fimc_is_ois_info **minfo); +int fimc_is_ois_get_phone_version(struct fimc_is_ois_info **minfo); +int fimc_is_ois_get_user_version(struct fimc_is_ois_info **uinfo); +void fimc_is_ois_get_offset_data(struct fimc_is_core *core, long *raw_data_x, long *raw_data_y); +void fimc_is_ois_check_fw(struct fimc_is_core *core); +bool fimc_is_ois_diff_test(struct fimc_is_core *core, int *x_diff, int *y_diff); +#ifdef CONFIG_OIS_FW_UPDATE_THREAD_USE +void fimc_is_ois_init_thread(struct fimc_is_core *core); +#endif +bool fimc_is_ois_read_userdata(struct fimc_is_core *core); +void fimc_is_ois_exif_data(struct fimc_is_core *core); +int fimc_is_ois_get_exif_data(struct fimc_is_ois_exif **exif_info); +void fimc_is_ois_fw_status(struct fimc_is_core *core, u8 *checksum, u8 *caldata); \ No newline at end of file diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.c b/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.c new file mode 100644 index 000000000000..d4ec99e1b64f --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.c @@ -0,0 +1,2431 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-dt.h" +#include "fimc-is-dvfs.h" + +#include "sensor/fimc-is-device-6b2.h" +#include "sensor/fimc-is-device-imx134.h" +#include "sensor/fimc-is-device-imx135.h" +#include "fimc-is-device-sensor.h" +#ifdef CONFIG_COMPANION_USE +#include "fimc-is-companion-dt.h" +#endif + +extern struct device *camera_front_dev; +extern struct device *camera_rear_dev; +int fimc_is_sensor_runtime_resume(struct device *dev); +int fimc_is_sensor_runtime_suspend(struct device *dev); + +extern int fimc_is_sen_video_probe(void *data); +struct pm_qos_request exynos_sensor_qos_cam; +struct pm_qos_request exynos_sensor_qos_int; +struct pm_qos_request exynos_sensor_qos_mem; + +extern u32 __iomem *notify_fcount_sen0; +extern u32 __iomem *notify_fcount_sen1; +extern u32 __iomem *notify_fcount_sen2; +u32 notify_fcount_sen0_fw; +u32 notify_fcount_sen1_fw; +u32 notify_fcount_sen2_fw; +u32 notify_fcount_dummy; + +#define BINNING(x, y) roundup((x) * 1000 / (y), 250) + +int fimc_is_sensor_read8(struct i2c_client *client, + u16 addr, u8 *val) +{ + int ret = 0; + struct i2c_msg msg[2]; + u8 wbuf[2]; + + if (!client->adapter) { + err("Could not find adapter!\n"); + ret = -ENODEV; + goto p_err; + } + + /* 1. I2C operation for writing. */ + msg[0].addr = client->addr; + msg[0].flags = 0; /* write : 0, read : 1 */ + msg[0].len = 2; + msg[0].buf = wbuf; + /* TODO : consider other size of buffer */ + wbuf[0] = (addr & 0xFF00) >> 8; + wbuf[1] = (addr & 0xFF); + + /* 2. I2C operation for reading data. */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = val; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + err("i2c treansfer fail"); + goto p_err; + } + +#ifdef PRINT_I2CCMD + info("I2CR08(%d) [0x%04X] : 0x%04X\n", client->addr, addr, *val); +#endif + +p_err: + return ret; +} + +int fimc_is_sensor_read16(struct i2c_client *client, + u16 addr, u16 *val) +{ + int ret = 0; + struct i2c_msg msg[2]; + u8 wbuf[2], rbuf[2]; + + if (!client->adapter) { + err("Could not find adapter!\n"); + ret = -ENODEV; + goto p_err; + } + + /* 1. I2C operation for writing. */ + msg[0].addr = client->addr; + msg[0].flags = 0; /* write : 0, read : 1 */ + msg[0].len = 2; + msg[0].buf = wbuf; + /* TODO : consider other size of buffer */ + wbuf[0] = (addr & 0xFF00) >> 8; + wbuf[1] = (addr & 0xFF); + + /* 2. I2C operation for reading data. */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rbuf; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + err("i2c treansfer fail"); + goto p_err; + } + + *val = ((rbuf[0] << 8) | rbuf[1]); + +#ifdef PRINT_I2CCMD + info("I2CR16(%d) [0x%04X] : 0x%04X\n", client->addr, addr, *val); +#endif + +p_err: + return ret; +} + +int fimc_is_sensor_write(struct i2c_client *client, + u8 *buf, u32 size) +{ + int ret = 0; + int retry_count = 5; + struct i2c_msg msg = {client->addr, 0, size, buf}; + + do { + ret = i2c_transfer(client->adapter, &msg, 1); + if (likely(ret == 1)) + break; + msleep(10); + } while (retry_count-- > 0); + + if (ret != 1) { + dev_err(&client->dev, "%s: I2C is not working.\n", __func__); + return -EIO; + } + + return 0; +} + +int fimc_is_sensor_write8(struct i2c_client *client, + u16 addr, u8 val) +{ + int ret = 0; + struct i2c_msg msg[1]; + u8 wbuf[3]; + + if (!client->adapter) { + err("Could not find adapter!\n"); + ret = -ENODEV; + goto p_err; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 3; + msg->buf = wbuf; + wbuf[0] = (addr & 0xFF00) >> 8; + wbuf[1] = (addr & 0xFF); + wbuf[2] = val; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) { + err("i2c treansfer fail(%d)", ret); + goto p_err; + } + +#ifdef PRINT_I2CCMD + info("I2CW08(%d) [0x%04X] : 0x%04X\n", client->addr, addr, val); +#endif + +p_err: + return ret; +} + +int fimc_is_sensor_write16(struct i2c_client *client, + u16 addr, u16 val) +{ + int ret = 0; + struct i2c_msg msg[1]; + u8 wbuf[4]; + + if (!client->adapter) { + err("Could not find adapter!\n"); + ret = -ENODEV; + goto p_err; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 4; + msg->buf = wbuf; + wbuf[0] = (addr & 0xFF00) >> 8; + wbuf[1] = (addr & 0xFF); + wbuf[2] = (val & 0xFF00) >> 8; + wbuf[3] = (val & 0xFF); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) { + err("i2c treansfer fail(%d)", ret); + goto p_err; + } + +#ifdef PRINT_I2CCMD + info("I2CW16(%d) [0x%04X] : 0x%04X\n", client->addr, addr, val); +#endif + +p_err: + return ret; +} + +#if defined(CONFIG_PM_DEVFREQ) +inline static void fimc_is_sensor_set_qos_init(struct fimc_is_device_sensor *device, bool on) +{ + int cam_qos = 0; + int int_qos = 0; + int mif_qos = 0; + struct fimc_is_core *core = + (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + cam_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CAM, START_DVFS_LEVEL); +#if 0 /* For vision of L_version */ + int_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_INT, START_DVFS_LEVEL); +#endif + if (on) { + /* DEVFREQ lock */ + if (cam_qos > 0) { + if (device->request_cam_qos == false) { + pm_qos_add_request(&exynos_sensor_qos_cam, PM_QOS_CAM_THROUGHPUT, cam_qos); + device->request_cam_qos = true; + } else { + err("Adding sensor cam_qos is not allowed"); + } + } + + if (int_qos > 0) { + if (device->request_int_qos == false) { + pm_qos_add_request(&exynos_sensor_qos_int, PM_QOS_DEVICE_THROUGHPUT, int_qos); + device->request_int_qos = true; + } else { + err("Adding sensor int_qos is not allowed"); + } + } + + if (mif_qos > 0) { + if (device->request_mif_qos == false) { + pm_qos_add_request(&exynos_sensor_qos_mem, PM_QOS_BUS_THROUGHPUT, mif_qos); + device->request_mif_qos = true; + } else { + err("Adding sensor mif_qos is not allowed"); + } + } + minfo("[SEN:D] %s: QoS LOCK [INT(%d), MIF(%d), CAM(%d)]\n", device, + __func__, int_qos, mif_qos, cam_qos); + } else { + /* DEVFREQ unlock */ + if (cam_qos > 0) { + if (device->request_cam_qos == true) { + pm_qos_remove_request(&exynos_sensor_qos_cam); + device->request_cam_qos = false; + } else { + err("Removing sensor cam_qos is not allowed"); + } + } + + if (int_qos > 0) { + if (device->request_int_qos == true) { + pm_qos_remove_request(&exynos_sensor_qos_int); + device->request_int_qos = false; + } else { + err("Removing sensor int_qos is not allowed"); + } + } + + if (mif_qos > 0) { + if (device->request_mif_qos == true) { + pm_qos_remove_request(&exynos_sensor_qos_mem); + device->request_mif_qos = false; + } else { + err("Removing sensor mif_qos is not allowed"); + } + } + minfo("[SEN:D] %s: QoS UNLOCK\n", device, __func__); + } +} + +inline static void fimc_is_sensor_set_qos_update(struct fimc_is_device_sensor *device, u32 scenario) +{ + int cam_qos = 0; + int int_qos = 0; + int mif_qos = 0; + struct fimc_is_core *core = + (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + /* HACK: This is considerated only front camera vision scenario. */ + if (scenario == SENSOR_SCENARIO_VISION) { + cam_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CAM, FIMC_IS_SN_FRONT_PREVIEW); + int_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_INT, FIMC_IS_SN_FRONT_PREVIEW); + } + + /* DEVFREQ update */ + if (cam_qos > 0) + pm_qos_update_request(&exynos_sensor_qos_cam, cam_qos); + if (int_qos > 0) + pm_qos_update_request(&exynos_sensor_qos_int, int_qos); + if (mif_qos > 0) + pm_qos_update_request(&exynos_sensor_qos_mem, mif_qos); + + minfo("[SEN:D] %s: QoS UPDATE(%d) [INT(%d), MIF(%d), CAM(%d)]\n", device, + __func__, scenario, int_qos, mif_qos, cam_qos); +} +#endif + +static int get_sensor_mode(struct fimc_is_sensor_cfg *cfg, + u32 cfgs, u32 width, u32 height, u32 framerate) +{ + int mode = -1; + int idx = -1; + u32 i; + + /* find sensor mode by w/h and fps range */ + for (i = 0; i < cfgs; i++) { + if ((cfg[i].width == width) && + (cfg[i].height == height)) { + if (cfg[i].framerate == framerate) { + /* You don't need to find another sensor mode */ + mode = cfg[i].mode; + idx = i; + break; + } else if (cfg[i].framerate > framerate) { + /* try to find framerate smaller than previous */ + if (mode < 0) { + mode = cfg[i].mode; + idx = i; + } else { + /* try to compare previous with current */ + if (cfg[idx].framerate > cfg[i].framerate) { + mode = cfg[i].mode; + idx = i; + } + } + } + } + } + + if (idx < 0) + err("could not find proper sensor mode: %dx%d@%dfps", + width, height, framerate); + else + pr_info("sensor mode(%dx%d@%d) = %d\n", + cfg[idx].width, + cfg[idx].height, + cfg[idx].framerate, + cfg[idx].mode); + + return mode; +} + +static int fimc_is_sensor_mclk_on(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (test_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state)) { + merr("%s : already clk on", device, __func__); + goto p_err; + } + + if (!pdata->mclk_on) { + merr("mclk_on is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->mclk_on(device->pdev, pdata->scenario, pdata->mclk_ch); + if (ret) { + merr("mclk_on is fail(%d)", device, ret); + goto p_err; + } + + set_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_mclk_off(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!test_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state)) { + merr("%s : already clk off", device, __func__); + goto p_err; + } + + if (!pdata->mclk_off) { + merr("mclk_off is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->mclk_off(device->pdev, pdata->scenario, pdata->mclk_ch); + if (ret) { + merr("mclk_off is fail(%d)", device, ret); + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_iclk_on(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_core *core; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + BUG_ON(!device->private_data); + + core = device->private_data; + pdata = device->pdata; + + if (test_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state)) { + merr("%s : already clk on", device, __func__); + goto p_err; + } + + if (!pdata->iclk_cfg) { + merr("iclk_cfg is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (!pdata->iclk_on) { + merr("iclk_on is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->iclk_cfg(core->pdev, pdata->scenario, pdata->csi_ch); + if (ret) { + merr("iclk_cfg is fail(%d)", device, ret); + goto p_err; + } + + ret = pdata->iclk_on(core->pdev, pdata->scenario, pdata->csi_ch); + if (ret) { + merr("iclk_on is fail(%d)", device, ret); + goto p_err; + } + + set_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_iclk_off(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_core *core; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + BUG_ON(!device->private_data); + + core = device->private_data; + pdata = device->pdata; + + if (!test_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state)) { + merr("%s : already clk off", device, __func__); + goto p_err; + } + + if (!pdata->iclk_off) { + merr("iclk_off is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->iclk_off(core->pdev, pdata->scenario, pdata->csi_ch); + if (ret) { + merr("iclk_off is fail(%d)", device, ret); + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_gpio_on(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (test_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state)) { + merr("%s : already gpio on", device, __func__); + goto p_err; + } + + if (!pdata->gpio_cfg) { + merr("gpio_cfg is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (pdata->id == 0) { + core->running_rear_camera = true; + } else { + core->running_front_camera = true; + } + + ret = pdata->gpio_cfg(device->pdev, pdata->scenario, GPIO_SCENARIO_ON); + if (ret) { + merr("gpio_cfg is fail(%d)", device, ret); + goto p_err; + } + + set_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_gpio_off(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!test_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state)) { + merr("%s : already gpio off", device, __func__); + goto p_err; + } + + if (!pdata->gpio_cfg) { + merr("gpio_cfg is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->gpio_cfg(device->pdev, pdata->scenario, GPIO_SCENARIO_OFF); + if (ret) { + merr("gpio_cfg is fail(%d)", device, ret); + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state); + +p_err: + if (pdata->id == 0) { + core->running_rear_camera = false; + } else { + core->running_front_camera = false; + } + return ret; +} + +#ifdef ENABLE_DTP +static void fimc_is_sensor_dtp(unsigned long data) +{ + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_sensor *device = (struct fimc_is_device_sensor *)data; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + unsigned long flags; + u32 i; + + BUG_ON(!device); + + err("forcely reset due to 0x%08lx", device->force_stop); + device->force_stop = 0; + + set_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state); + set_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + + if (device->ischain) { + set_bit(FIMC_IS_GROUP_FORCE_STOP, &device->ischain->group_3aa.state); + set_bit(FIMC_IS_GROUP_FORCE_STOP, &device->ischain->group_isp.state); + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->ischain->group_3aa.state)) + up(&device->ischain->group_3aa.smp_trigger); + } + + vctx = device->vctx; + if (!vctx) { + err("vctx is NULL"); + return; + } + + queue = GET_DST_QUEUE(vctx); + framemgr = &queue->framemgr; + if ((framemgr->frame_cnt == 0) || (framemgr->frame_cnt > FRAMEMGR_MAX_REQUEST)) { + err("frame count of framemgr is invalid(%d)", framemgr->frame_cnt); + return; + } + + framemgr_e_barrier_irqs(framemgr, 0, flags); + + for (i = 0; i < framemgr->frame_cnt; i++) { + frame = &framemgr->frame[i]; + if (frame->state == FIMC_IS_FRAME_STATE_REQUEST) { + err("buffer done1!!!! %d", i); + fimc_is_frame_trans_req_to_com(framemgr, frame); + queue_done(vctx, queue, i, VB2_BUF_STATE_ERROR); + } else if (frame->state == FIMC_IS_FRAME_STATE_PROCESS) { + err("buffer done2!!!! %d", i); + fimc_is_frame_trans_pro_to_com(framemgr, frame); + queue_done(vctx, queue, i, VB2_BUF_STATE_ERROR); + } + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); +} +#endif + +static int fimc_is_sensor_start(struct fimc_is_device_sensor *device) +{ + int ret = 0; + BUG_ON(!device); + + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + struct v4l2_subdev *subdev; + + subdev = device->subdev_module; + if (!subdev) { + merr("subdev is NULL", device); + ret = -EINVAL; + goto p_err; + } + ret = v4l2_subdev_call(subdev, video, s_stream, true); + if (ret) { + merr("v4l2_subdev_call(s_stream) is fail(%d)", device, ret); + goto p_err; + } + } else { + struct fimc_is_device_ischain *ischain; + + ischain = device->ischain; + if (!ischain) { + merr("ischain is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_itf_stream_on(ischain); + if (ret) { + merr("fimc_is_itf_stream_on is fail(%d)", device, ret); + goto p_err; + } + } + +p_err: + return ret; +} + +static int fimc_is_sensor_stop(struct fimc_is_device_sensor *device) +{ + int ret = 0; + + BUG_ON(!device); + + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + struct v4l2_subdev *subdev; + + subdev = device->subdev_module; + if (!subdev) { + merr("subdev is NULL", device); + ret = -EINVAL; + goto p_err; + } + ret = v4l2_subdev_call(subdev, video, s_stream, false); + if (ret) { + merr("v4l2_subdev_call(s_stream) is fail(%d)", device, ret); + goto p_err; + } + } else { + struct fimc_is_device_ischain *ischain; + + ischain = device->ischain; + if (!ischain) { + merr("ischain is NULL", device); + ret = -EINVAL; + goto p_err; + } + ret = fimc_is_itf_stream_off(ischain); + if (ret) { + merr("fimc_is_itf_stream_off is fail(%d)", device, ret); + goto p_err; + } + } + +p_err: + return ret; +} + +static int fimc_is_sensor_tag(struct fimc_is_device_sensor *device, + struct fimc_is_frame *frame) +{ + int ret = 0; + frame->shot->dm.request.frameCount = frame->fcount; + frame->shot->dm.sensor.timeStamp = fimc_is_get_timestamp(); + + return ret; +} + +static void fimc_is_sensor_control(struct work_struct *data) +{ +/* + * HAL can't send meta data for vision + * We accepted vision control by s_ctrl + */ +#if 0 + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct camera2_sensor_ctl *rsensor_ctl; + struct camera2_sensor_ctl *csensor_ctl; + struct fimc_is_device_sensor *device; + + device = container_of(data, struct fimc_is_device_sensor, control_work); + subdev_module = device->subdev_module; + if (!subdev_module) { + err("subdev_module is NULL"); + return; + } + + module = v4l2_get_subdevdata(subdev_module); + rsensor_ctl = &device->control_frame->shot->ctl.sensor; + csensor_ctl = &device->sensor_ctl; + + if (rsensor_ctl->exposureTime != csensor_ctl->exposureTime) { + CALL_MOPS(module, s_exposure, subdev_module, rsensor_ctl->exposureTime); + csensor_ctl->exposureTime = rsensor_ctl->exposureTime; + } + + if (rsensor_ctl->frameDuration != csensor_ctl->frameDuration) { + CALL_MOPS(module, s_duration, subdev_module, rsensor_ctl->frameDuration); + csensor_ctl->frameDuration = rsensor_ctl->frameDuration; + } + + if (rsensor_ctl->sensitivity != csensor_ctl->sensitivity) { + CALL_MOPS(module, s_again, subdev_module, rsensor_ctl->sensitivity); + csensor_ctl->sensitivity = rsensor_ctl->sensitivity; + } +#endif +} + +static int fimc_is_sensor_notify_by_fstr(struct fimc_is_device_sensor *device, void *arg) +{ + int ret = 0; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!device); + BUG_ON(!arg); + + device->fcount = *(u32 *)arg; + framemgr = GET_DST_FRAMEMGR(device->vctx); + + if (device->instant_cnt) { + device->instant_cnt--; + if (device->instant_cnt == 0) + wake_up(&device->instant_wait); + } + + framemgr_e_barrier(framemgr, 0); + + fimc_is_frame_process_head(framemgr, &frame); + if (frame) { +#ifdef MEASURE_TIME +#ifdef EXTERNAL_TIME + do_gettimeofday(&frame->tzone[TM_FLITE_STR]); +#endif +#endif + if (frame->has_fcount) { + struct list_head *temp; + struct fimc_is_frame *next_frame; + bool finded = false; + + list_for_each(temp, &framemgr->frame_process_head) { + next_frame = list_entry(temp, struct fimc_is_frame, list); + if (next_frame->has_fcount) { + continue; + } else { + finded = true; + break; + } + } + + if (finded) { + /* finded frame in processing frame list */ + next_frame->has_fcount = true; + next_frame->fcount = device->fcount; + fimc_is_sensor_tag(device, next_frame); + } + } else { + frame->fcount = device->fcount; + fimc_is_sensor_tag(device, frame); + frame->has_fcount = true; + } + } +#ifdef TASKLET_MSG + if (!frame) { + merr("[SEN] process is empty", device); + fimc_is_frame_print_all(framemgr); + } +#endif + + framemgr_x_barrier(framemgr, 0); + + return ret; +} + +static int fimc_is_sensor_notify_by_fend(struct fimc_is_device_sensor *device, void *arg) +{ + int ret = 0; + struct fimc_is_frame *frame; + + BUG_ON(!device); + BUG_ON(!device->vctx); + +#ifdef ENABLE_DTP + if (device->dtp_check) { + device->dtp_check = false; + del_timer(&device->dtp_timer); + } + + if (device->force_stop) + fimc_is_sensor_dtp((unsigned long)device); +#endif + + frame = (struct fimc_is_frame *)arg; + if (frame) { + frame->has_fcount = false; + buffer_done(device->vctx, frame->index); + + /* device driving */ + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + device->control_frame = frame; + schedule_work(&device->control_work); + } + } + + return ret; +} + +static void fimc_is_sensor_notify(struct v4l2_subdev *subdev, + unsigned int notification, + void *arg) +{ + int ret = 0; + struct fimc_is_device_sensor *device; + + BUG_ON(!subdev); + + device = v4l2_get_subdev_hostdata(subdev); + + switch(notification) { + case FLITE_NOTIFY_FSTART: + ret = fimc_is_sensor_notify_by_fstr(device, arg); + if (ret) + merr("fimc_is_sensor_notify_by_fstr is fail(%d)", device, ret); + break; + case FLITE_NOTIFY_FEND: + ret = fimc_is_sensor_notify_by_fend(device, arg); + if (ret) + merr("fimc_is_sensor_notify_by_fend is fail(%d)", device, ret); + break; + } +} + +static void fimc_is_sensor_instanton(struct work_struct *data) +{ + int ret = 0; + u32 instant_cnt; + struct fimc_is_device_sensor *device; + + BUG_ON(!data); + + device = container_of(data, struct fimc_is_device_sensor, instant_work); + instant_cnt = device->instant_cnt; + + clear_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state); + clear_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + + ret = fimc_is_sensor_start(device); + if (ret) { + merr("fimc_is_sensor_start is fail(%d)\n", device, ret); + goto p_err; + } + set_bit(FIMC_IS_SENSOR_FRONT_START, &device->state); + +#ifdef ENABLE_DTP + if (device->dtp_check) { + setup_timer(&device->dtp_timer, fimc_is_sensor_dtp, (unsigned long)device); + mod_timer(&device->dtp_timer, jiffies + msecs_to_jiffies(300)); + info("DTP checking...\n"); + } +#endif + + if (instant_cnt) { + u32 timetowait, timetoelapse, timeout; + + timeout = FIMC_IS_FLITE_STOP_TIMEOUT + msecs_to_jiffies(instant_cnt*60); + timetowait = wait_event_timeout(device->instant_wait, + (device->instant_cnt == 0), + timeout); + if (!timetowait) { + merr("wait_event_timeout is invalid", device); + ret = -ETIME; + } + + fimc_is_sensor_front_stop(device); + + timetoelapse = (jiffies_to_msecs(timeout) - jiffies_to_msecs(timetowait)); + info("[FRT:D:%d] instant off(fcount : %d, time : %dms)", device->instance, + device->instant_cnt, + timetoelapse); + } + +p_err: + device->instant_ret = ret; +} + +static int fimc_is_sensor_probe(struct platform_device *pdev) +{ + int ret = 0; + u32 instance = -1; + atomic_t device_id; + struct fimc_is_core *core; + struct fimc_is_device_sensor *device; + struct device *dev; + void *pdata; + + BUG_ON(!pdev); + + if (fimc_is_dev == NULL) { + warn("fimc_is_dev is not yet probed"); + pdev->dev.init_name = FIMC_IS_SENSOR_DEV_NAME; + return -EPROBE_DEFER; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } + +#ifdef CONFIG_OF +#ifdef CONFIG_COMPANION_USE + ret = fimc_is_sensor_parse_dt_with_companion(pdev); + if (ret) { + err("parsing device tree is fail(%d)", ret); + goto p_err; + } +#else + ret = fimc_is_sensor_parse_dt(pdev); + if (ret) { + err("parsing device tree is fail(%d)", ret); + goto p_err; + } +#endif /* CONFIG_COMPANION_USE */ +#endif /* CONFIG_OF */ + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + err("pdata is NULL"); + ret = -EINVAL; + goto p_err; + } + + /* 1. get device */ + atomic_set(&device_id, pdev->id); + device = &core->sensor[pdev->id]; + + /* v4l2 device and device init */ + memset(&device->v4l2_dev, 0, sizeof(struct v4l2_device)); + instance = v4l2_device_set_name(&device->v4l2_dev, FIMC_IS_SENSOR_DEV_NAME, &device_id); + device->v4l2_dev.notify = fimc_is_sensor_notify; + device->instance = instance; + device->resourcemgr = &core->resourcemgr; + device->pdev = pdev; + device->private_data = core; + device->pdata = pdata; + platform_set_drvdata(pdev, device); + init_waitqueue_head(&device->instant_wait); + INIT_WORK(&device->instant_work, fimc_is_sensor_instanton); + INIT_WORK(&device->control_work, fimc_is_sensor_control); + spin_lock_init(&device->slock_state); + device_init_wakeup(&pdev->dev, true); + + /* 3. state init*/ + clear_bit(FIMC_IS_SENSOR_OPEN, &device->state); + clear_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_DRIVING, &device->state); + clear_bit(FIMC_IS_SENSOR_FRONT_START, &device->state); + clear_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state); + clear_bit(FIMC_IS_SENSOR_BACK_START, &device->state); + clear_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + +#ifdef ENABLE_DTP + device->dtp_check = false; +#endif + + ret = fimc_is_mem_probe(&device->mem, device->pdev); + if (ret) { + merr("fimc_is_mem_probe is fail(%d)", device, ret); + goto p_err; + } + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#if defined(CONFIG_VIDEOBUF2_ION) + if (device->mem.alloc_ctx) + vb2_ion_attach_iommu(device->mem.alloc_ctx); +#endif +#endif + +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_enable(&pdev->dev); +#endif + + ret = v4l2_device_register(&pdev->dev, &device->v4l2_dev); + if (ret) { + merr("v4l2_device_register is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_csi_probe(device, device->pdata->csi_ch); + if (ret) { + merr("fimc_is_csi_probe is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_flite_probe(device, device->pdata->flite_ch); + if (ret) { + merr("fimc_is_flite_probe is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_sen_video_probe(device); + if (ret) { + merr("fimc_is_sensor_video_probe is fail(%d)", device, ret); + goto p_err; + } + + dev = pdev->id ? camera_front_dev : camera_rear_dev; + if (dev) + dev_set_drvdata(dev, device->pdata); + +p_err: + info("[%d][SEN:D] %s(%d)\n", instance, __func__, ret); + return ret; +} + +static int fimc_is_sensor_remove(struct platform_device *pdev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +int fimc_is_sensor_open(struct fimc_is_device_sensor *device, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->subdev_csi); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (test_bit(FIMC_IS_SENSOR_OPEN, &device->state)) { + merr("already open", device); + ret = -EMFILE; + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_MCLK_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_ICLK_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state); + clear_bit(FIMC_IS_SENSOR_DRIVING, &device->state); + clear_bit(FIMC_IS_SENSOR_FRONT_START, &device->state); + clear_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state); + clear_bit(FIMC_IS_SENSOR_BACK_START, &device->state); + set_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + + device->vctx = vctx; + device->fcount = 0; + device->instant_cnt = 0; + device->instant_ret = 0; + device->ischain = NULL; + device->subdev_module = NULL; + device->exposure_time = 0; + device->frame_duration = 0; + device->force_stop = 0; + device->request_cam_qos = 0; + device->request_int_qos = 0; + device->request_mif_qos = 0; + memset(&device->sensor_ctl, 0, sizeof(struct camera2_sensor_ctl)); + memset(&device->lens_ctl, 0, sizeof(struct camera2_lens_ctl)); + memset(&device->flash_ctl, 0, sizeof(struct camera2_flash_ctl)); + + if (pdata->id == 0) { + core->running_rear_camera = true; + core->id = SENSOR_POSITION_REAR; + } else { + core->running_front_camera = true; + core->id = SENSOR_POSITION_FRONT; + } + + /* for mediaserver force close */ + ret = fimc_is_resource_get(device->resourcemgr, device->instance); + if (ret) { + merr("fimc_is_resource_get is fail", device); + goto p_err; + } + + ret = fimc_is_csi_open(device->subdev_csi); + if (ret) { + merr("fimc_is_csi_open is fail(%d)", device, ret); + goto p_err; + } + + ret = fimc_is_flite_open(device->subdev_flite, GET_DST_FRAMEMGR(vctx)); + if (ret) { + merr("fimc_is_flite_open is fail(%d)", device, ret); + goto p_err; + } + +#ifdef CONFIG_PM_RUNTIME + pm_runtime_get_sync(&device->pdev->dev); +#else + fimc_is_sensor_runtime_resume(&device->pdev->dev); +#endif + +#ifdef ENABLE_DTP + device->dtp_check = true; +#endif + + set_bit(FIMC_IS_SENSOR_OPEN, &device->state); + +p_err: + info("[SEN:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +int fimc_is_sensor_close(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct fimc_is_device_ischain *ischain; + struct fimc_is_group *group_3aa; + struct fimc_is_core *core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!test_bit(FIMC_IS_SENSOR_OPEN, &device->state)) { + merr("already close", device); + ret = -EMFILE; + goto p_err; + } + + /* for mediaserver force close */ + ischain = device->ischain; + if (ischain) { + group_3aa = &ischain->group_3aa; + if (test_bit(FIMC_IS_GROUP_READY, &group_3aa->state)) { + info("media server is dead, 3ax forcely done\n"); + set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &group_3aa->state); + } + } + + ret = fimc_is_sensor_back_stop(device); + if (ret) + merr("fimc_is_sensor_back_stop is fail(%d)", device, ret); + + ret = fimc_is_sensor_front_stop(device); + if (ret) + merr("fimc_is_sensor_front_stop is fail(%d)", device, ret); + + ret = fimc_is_csi_close(device->subdev_csi); + if (ret) + merr("fimc_is_flite_close is fail(%d)", device, ret); + + ret = fimc_is_flite_close(device->subdev_flite); + if (ret) + merr("fimc_is_flite_close is fail(%d)", device, ret); + +#if defined(CONFIG_PM_RUNTIME) + pm_runtime_put_sync(&device->pdev->dev); +#else + fimc_is_sensor_runtime_suspend(&device->pdev->dev); +#endif + + /* cancel a work and wait for it to finish */ + cancel_work_sync(&device->control_work); + cancel_work_sync(&device->instant_work); + + if (device->subdev_module) { + v4l2_device_unregister_subdev(device->subdev_module); + device->subdev_module = NULL; + } + + /* for mediaserver force close */ + ret = fimc_is_resource_put(device->resourcemgr, device->instance); + if (ret) + merr("fimc_is_resource_put is fail", device); + + clear_bit(FIMC_IS_SENSOR_OPEN, &device->state); + set_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + +p_err: + if (pdata->id == 0) { + core->running_rear_camera = false; + } else { + core->running_front_camera = false; + } + info("[SEN:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +int fimc_is_sensor_s_input(struct fimc_is_device_sensor *device, + u32 input, + u32 scenario) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + struct v4l2_subdev *subdev_csi; + struct v4l2_subdev *subdev_flite; + struct fimc_is_module_enum *module = NULL; + u32 sensor_ch, actuator_ch; +#if defined(CONFIG_OIS_USE) + u32 ois_ch, ois_addr; +#endif + u32 sensor_addr, actuator_addr; + u32 i = 0; + + BUG_ON(!device); + BUG_ON(!device->pdata); + BUG_ON(!device->subdev_csi); + BUG_ON(input >= SENSOR_NAME_END); + + for (i = 0; i < SENSOR_MAX_ENUM; i++) { + if (&device->module_enum[i] && + device->module_enum[i].id == input) { + module = &device->module_enum[i]; + + /* + * If it is not normal scenario, + * try to find proper sensor module which has a i2c client + */ + if (scenario != SENSOR_SCENARIO_NORMAL && + module->client == NULL) + continue; + else + break; + } + } + + if (i >= SENSOR_MAX_ENUM) { + merr("module is not probed", device); + ret = -EINVAL; + goto p_err; + } + + subdev_module = module->subdev; + if (!subdev_module) { + merr("module is not probed", device); + ret = -EINVAL; + goto p_err; + } + + /* change i2c channel info */ + if (module->ext.sensor_con.peri_type == SE_I2C) { + sensor_ch = device->pdata->i2c_ch & SENSOR_I2C_CH_MASK; + sensor_ch >>= SENSOR_I2C_CH_SHIFT; + sensor_addr = device->pdata->i2c_addr & SENSOR_I2C_ADDR_MASK; + sensor_addr >>= SENSOR_I2C_ADDR_SHIFT; + module->ext.sensor_con.peri_setting.i2c.channel = sensor_ch; + module->ext.sensor_con.peri_setting.i2c.slave_address = sensor_addr; + } + + if (module->ext.actuator_con.peri_type == SE_I2C) { + actuator_ch = device->pdata->i2c_ch & ACTUATOR_I2C_CH_MASK; + actuator_ch >>= ACTUATOR_I2C_CH_SHIFT; + actuator_addr = device->pdata->i2c_addr & ACTUATOR_I2C_ADDR_MASK; + actuator_addr >>= ACTUATOR_I2C_ADDR_SHIFT; + module->ext.actuator_con.peri_setting.i2c.channel = actuator_ch; + module->ext.actuator_con.peri_setting.i2c.slave_address = actuator_addr; + } + +#if defined(CONFIG_OIS_USE) + if (module->ext.ois_con.peri_type == SE_I2C) { + ois_ch = device->pdata->i2c_ch & OIS_I2C_CH_MASK; + ois_ch >>= OIS_I2C_CH_SHIFT; + ois_addr = device->pdata->i2c_addr & OIS_I2C_ADDR_MASK; + ois_addr >>= OIS_I2C_ADDR_SHIFT; + module->ext.ois_con.peri_setting.i2c.channel = ois_ch; + module->ext.ois_con.peri_setting.i2c.slave_address = ois_addr; + } +#endif + + /* send csi chennel to FW */ + module->ext.sensor_con.csi_ch = device->pdata->csi_ch; + module->ext.sensor_con.csi_ch |= 0x0100; + + module->ext.flash_con.peri_setting.gpio.first_gpio_port_no = device->pdata->flash_first_gpio; + module->ext.flash_con.peri_setting.gpio.second_gpio_port_no = device->pdata->flash_second_gpio; + +#ifdef CONFIG_COMPANION_USE + /* Data Type For Comapnion: + * Companion use user defined data type. + */ + if (module->ext.companion_con.product_name && + module->ext.companion_con.product_name != COMPANION_NAME_NOTHING) + device->image.format.field = V4L2_FIELD_INTERLACED; +#endif + + subdev_csi = device->subdev_csi; + subdev_flite = device->subdev_flite; + device->image.framerate = min_t(u32, SENSOR_DEFAULT_FRAMERATE, module->max_framerate); + device->image.window.width = module->pixel_width; + device->image.window.height = module->pixel_height; + device->image.window.o_width = device->image.window.width; + device->image.window.o_height = device->image.window.height; + + if (scenario) { + device->pdata->scenario = scenario; + set_bit(FIMC_IS_SENSOR_DRIVING, &device->state); + } else { + device->pdata->scenario = SENSOR_SCENARIO_NORMAL; + clear_bit(FIMC_IS_SENSOR_DRIVING, &device->state); + } + + if (device->subdev_module) { + mwarn("subdev_module is already registered", device); + v4l2_device_unregister_subdev(device->subdev_module); + } + + ret = v4l2_device_register_subdev(&device->v4l2_dev, subdev_module); + if (ret) { + merr("v4l2_device_register_subdev is fail(%d)", device, ret); + goto p_err; + } else { + device->subdev_module = subdev_module; + } + +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ set */ + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) + fimc_is_sensor_set_qos_init(device, true); +#endif + + /* configuration clock control */ + ret = fimc_is_sensor_iclk_on(device); + if (ret) { + merr("fimc_is_sensor_iclk_on is fail(%d)", device, ret); + goto p_err; + } + +#if defined(CONFIG_PM_DEVFREQ) + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) + fimc_is_sensor_set_qos_update(device, device->pdata->scenario); +#endif + + /* Sensor power on */ + ret = fimc_is_sensor_gpio_on(device); + if (ret) { + merr("fimc_is_sensor_gpio_on is fail(%d)", device, ret); + goto p_err; + } + + ret = v4l2_subdev_call(subdev_flite, core, init, device->pdata->csi_ch); + if (ret) { + merr("v4l2_flite_call(init) is fail(%d)", device, ret); + goto p_err; + } + + ret = v4l2_subdev_call(subdev_csi, core, init, (u32)module); + if (ret) { + merr("v4l2_csi_call(init) is fail(%d)", device, ret); + goto p_err; + } + + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + ret = v4l2_subdev_call(subdev_module, core, init, 0); + if (ret) { + merr("v4l2_module_call(init) is fail(%d)", device, ret); + goto p_err; + } + } + +p_err: + + minfo("[SEN:D] %s(%d, %d, %d)\n", device, __func__, input, scenario, ret); + return ret; +} + +int fimc_is_sensor_s_format(struct fimc_is_device_sensor *device, + struct fimc_is_fmt *format, + u32 width, + u32 height) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + struct v4l2_subdev *subdev_csi; + struct v4l2_subdev *subdev_flite; + struct fimc_is_module_enum *module; + struct v4l2_mbus_framefmt subdev_format; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + BUG_ON(!device->subdev_csi); + BUG_ON(!device->subdev_flite); + BUG_ON(!device->subdev_module); + BUG_ON(!format); + + subdev_module = device->subdev_module; + subdev_csi = device->subdev_csi; + subdev_flite = device->subdev_flite; + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev_module); + if (!module) { + merr("module is NULL", device); + goto p_err; + } + + /* Data Type For Comapnion: + * Companion use user defined data type. + */ + if (device->image.format.field == V4L2_FIELD_INTERLACED) + format->field = V4L2_FIELD_INTERLACED; + + device->image.format = *format; + device->image.window.offs_h = 0; + device->image.window.offs_v = 0; + device->image.window.width = width; + device->image.window.o_width = width; + device->image.window.height = height; + device->image.window.o_height = height; + + subdev_format.code = format->pixelformat; + subdev_format.field = format->field; + subdev_format.width = width; + subdev_format.height = height; + + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + ret = v4l2_subdev_call(subdev_module, video, s_mbus_fmt, &subdev_format); + if (ret) { + merr("v4l2_module_call(s_format) is fail(%d)", device, ret); + goto p_err; + } + } + + ret = v4l2_subdev_call(subdev_csi, video, s_mbus_fmt, &subdev_format); + if (ret) { + merr("v4l2_csi_call(s_format) is fail(%d)", device, ret); + goto p_err; + } + + ret = v4l2_subdev_call(subdev_flite, video, s_mbus_fmt, &subdev_format); + if (ret) { + merr("v4l2_flite_call(s_format) is fail(%d)", device, ret); + goto p_err; + } + + /* if sensor is driving mode, skip finding sensor mode */ + if (!test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) + device->mode = get_sensor_mode(module->cfg, module->cfgs, + device->image.window.width, device->image.window.height, + device->image.framerate); + else + device->mode = 0; + + /* can't find proper sensor mode */ + if (device->mode < 0) { + ret = -EINVAL; + goto p_err; + } +p_err: + return ret; +} + +int fimc_is_sensor_s_framerate(struct fimc_is_device_sensor *device, + struct v4l2_streamparm *param) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + struct v4l2_subdev *subdev_csi; + struct fimc_is_module_enum *module; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + u32 framerate = 0; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + BUG_ON(!device->subdev_csi); + BUG_ON(!param); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + if (!tpf->numerator) { + merr("numerator is 0", device); + ret = -EINVAL; + goto p_err; + } + + framerate = tpf->denominator / tpf->numerator; + subdev_module = device->subdev_module; + subdev_csi = device->subdev_csi; + + if (framerate == 0) { + mwarn("frame rate 0 request is ignored", device); + goto p_err; + } + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev_module); + if (!module) { + merr("module is NULL", device); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + merr("front is already stream on", device); + ret = -EINVAL; + goto p_err; + } + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + merr("type is invalid(%d)", device, param->type); + ret = -EINVAL; + goto p_err; + } + + if (framerate > module->max_framerate) { + merr("framerate is invalid(%d > %d)", device, framerate, module->max_framerate); + ret = -EINVAL; + goto p_err; + } + + ret = v4l2_subdev_call(subdev_csi, video, s_parm, param); + if (ret) { + merr("v4l2_csi_call(s_param) is fail(%d)", device, ret); + goto p_err; + } + + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) { + ret = v4l2_subdev_call(subdev_module, video, s_parm, param); + if (ret) { + merr("v4l2_module_call(s_param) is fail(%d)", device, ret); + goto p_err; + } + } + + device->image.framerate = framerate; + + device->mode = get_sensor_mode(module->cfg, module->cfgs, + device->image.window.width, device->image.window.height, + framerate); + + info("[SEN:D:%d] framerate: req@%dfps, cur@%dfps\n", device->instance, + framerate, device->image.framerate); + +p_err: + return ret; +} + +int fimc_is_sensor_s_ctrl(struct fimc_is_device_sensor *device, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + BUG_ON(!device->subdev_csi); + BUG_ON(!ctrl); + + subdev_module = device->subdev_module; + + ret = v4l2_subdev_call(subdev_module, core, s_ctrl, ctrl); + if (ret) { + err("s_ctrl is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_sensor_s_bns(struct fimc_is_device_sensor *device, + u32 ratio) +{ + int ret = 0; + struct v4l2_subdev *subdev_flite; + u32 sensor_width, sensor_height; + + BUG_ON(!device); + BUG_ON(!device->subdev_flite); + + subdev_flite = device->subdev_flite; + + sensor_width = fimc_is_sensor_g_width(device); + sensor_height = fimc_is_sensor_g_height(device); + if (!sensor_width || !sensor_height) { + merr("Sensor size is zero. Sensor set_format first.\n", device); + ret = -EINVAL; + goto p_err; + } + + device->image.window.otf_width + = rounddown((sensor_width * 1000 / ratio), 4); + device->image.window.otf_height + = rounddown((sensor_height * 1000 / ratio), 2); + +p_err: + return ret; +} + +int fimc_is_sensor_s_frame_duration(struct fimc_is_device_sensor *device, + u32 framerate) +{ + int ret = 0; + u64 frame_duration; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + + BUG_ON(!device); + + subdev_module = device->subdev_module; + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -EINVAL; + goto p_err; + } + + module = v4l2_get_subdevdata(subdev_module); + if (!module) { + err("module is NULL"); + ret = -EINVAL; + goto p_err; + } + + /* unit : nano */ + frame_duration = (1000 * 1000 * 1000) / framerate; + if (frame_duration <= 0) { + err("it is wrong frame duration(%lld)", frame_duration); + ret = -EINVAL; + goto p_err; + } + + if (device->frame_duration != frame_duration) { + CALL_MOPS(module, s_duration, subdev_module, frame_duration); + device->frame_duration = frame_duration; + } + +p_err: + return ret; +} + +int fimc_is_sensor_s_exposure_time(struct fimc_is_device_sensor *device, + u32 exposure_time) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + + BUG_ON(!device); + + subdev_module = device->subdev_module; + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -EINVAL; + goto p_err; + } + + module = v4l2_get_subdevdata(subdev_module); + if (!module) { + err("module is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (exposure_time <= 0) { + err("it is wrong exposure time (%d)", exposure_time); + ret = -EINVAL; + goto p_err; + } + + if (device->exposure_time != exposure_time) { + CALL_MOPS(module, s_exposure, subdev_module, exposure_time); + device->exposure_time = exposure_time; + } +p_err: + return ret; +} + +int fimc_is_sensor_g_ctrl(struct fimc_is_device_sensor *device, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + BUG_ON(!device->subdev_csi); + BUG_ON(!ctrl); + + subdev_module = device->subdev_module; + + ret = v4l2_subdev_call(subdev_module, core, g_ctrl, ctrl); + if (ret) { + err("g_ctrl is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + + +int fimc_is_sensor_g_instance(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + return device->instance; +} + +int fimc_is_sensor_g_fcount(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + return device->fcount; +} + +int fimc_is_sensor_g_framerate(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + return device->image.framerate; +} + +int fimc_is_sensor_g_width(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + return device->image.window.width; +} + +int fimc_is_sensor_g_height(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + return device->image.window.height; +} + +int fimc_is_sensor_g_bns_width(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + + if (device->image.window.otf_width) + return device->image.window.otf_width; + + return device->image.window.width; +} + +int fimc_is_sensor_g_bns_height(struct fimc_is_device_sensor *device) +{ + BUG_ON(!device); + if (device->image.window.otf_height) + return device->image.window.otf_height; + + return device->image.window.height; +} + +int fimc_is_sensor_g_bns_ratio(struct fimc_is_device_sensor *device) +{ + int binning = 0; + u32 sensor_width, sensor_height; + u32 bns_width, bns_height; + + BUG_ON(!device); + + sensor_width = fimc_is_sensor_g_width(device); + sensor_height = fimc_is_sensor_g_height(device); + bns_width = fimc_is_sensor_g_bns_width(device); + bns_height = fimc_is_sensor_g_bns_height(device); + + binning = min(BINNING(sensor_width, bns_width), + BINNING(sensor_height, bns_height)); + + return binning; +} + +int fimc_is_sensor_g_bratio(struct fimc_is_device_sensor *device) +{ + int binning = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(device->subdev_module); + if (!module) { + merr("module is NULL", device); + goto p_err; + } + + binning = min(BINNING(module->active_width, device->image.window.width), + BINNING(module->active_height, device->image.window.height)); + +p_err: + return binning; +} + +int fimc_is_sensor_g_module(struct fimc_is_device_sensor *device, + struct fimc_is_module_enum **module) +{ + int ret = 0; + + BUG_ON(!device); + BUG_ON(!device->subdev_module); + + *module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(device->subdev_module); + if (!*module) { + merr("module is NULL", device); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_sensor_buffer_queue(struct fimc_is_device_sensor *device, + u32 index) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_frame *frame; + struct fimc_is_framemgr *framemgr; + + if (index >= FRAMEMGR_MAX_REQUEST) { + err("index(%d) is invalid", index); + ret = -EINVAL; + goto p_err; + } + + framemgr = &device->vctx->q_dst->framemgr; + if (framemgr == NULL) { + err("framemgr is null\n"); + ret = EINVAL; + goto p_err; + } + + frame = &framemgr->frame[index]; + if (frame == NULL) { + err("frame is null\n"); + ret = EINVAL; + goto p_err; + } + + if (unlikely(!test_bit(FRAME_INI_MEM, &frame->memory))) { + err("frame %d is NOT init", index); + ret = EINVAL; + goto p_err; + } + + framemgr_e_barrier_irqs(framemgr, FMGR_IDX_2 + index, flags); + + if (frame->state == FIMC_IS_FRAME_STATE_FREE) { + fimc_is_frame_trans_fre_to_req(framemgr, frame); + } else { + err("frame(%d) is not free state(%d)", index, frame->state); + fimc_is_frame_print_all(framemgr); + } + + framemgr_x_barrier_irqr(framemgr, FMGR_IDX_2 + index, flags); + +p_err: + return ret; +} + +int fimc_is_sensor_buffer_finish(struct fimc_is_device_sensor *device, + u32 index) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_frame *frame; + struct fimc_is_framemgr *framemgr; + + if (index >= FRAMEMGR_MAX_REQUEST) { + err("index(%d) is invalid", index); + ret = -EINVAL; + goto exit; + } + + framemgr = &device->vctx->q_dst->framemgr; + frame = &framemgr->frame[index]; + + framemgr_e_barrier_irqs(framemgr, FMGR_IDX_3 + index, flags); + + if (frame->state == FIMC_IS_FRAME_STATE_COMPLETE) { + if (!frame->shot->dm.request.frameCount) + err("request.frameCount is 0\n"); + fimc_is_frame_trans_com_to_fre(framemgr, frame); + + frame->shot_ext->free_cnt = framemgr->frame_fre_cnt; + frame->shot_ext->request_cnt = framemgr->frame_req_cnt; + frame->shot_ext->process_cnt = framemgr->frame_pro_cnt; + frame->shot_ext->complete_cnt = framemgr->frame_com_cnt; + } else { + err("frame(%d) is not com state(%d)", index, frame->state); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, FMGR_IDX_3 + index, flags); + +exit: + return ret; +} + +int fimc_is_sensor_back_start(struct fimc_is_device_sensor *device) +{ + int ret = 0; + int enable; + struct v4l2_subdev *subdev_flite; + struct fimc_is_device_flite *flite; + + BUG_ON(!device); + BUG_ON(!device->subdev_flite); + + subdev_flite = device->subdev_flite; + enable = FLITE_ENABLE_FLAG; + + if (test_bit(FIMC_IS_SENSOR_BACK_START, &device->state)) { + err("already back start"); + ret = -EINVAL; + goto p_err; + } + + flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev_flite); + if (!flite) { + merr("flite is NULL", device); + ret = -EINVAL; + goto p_err; + } + + /* to determine flite buffer done mode (early/normal) when not vision mode */ + if (!test_bit(FIMC_IS_SENSOR_DRIVING, &device->state) && flite->chk_early_buf_done) { + flite->chk_early_buf_done(flite, device->image.framerate, + device->pdev->id); + } + + ret = v4l2_subdev_call(subdev_flite, video, s_stream, enable); + if (ret) { + merr("v4l2_flite_call(s_stream) is fail(%d)", device, ret); + goto p_err; + } + + set_bit(FIMC_IS_SENSOR_BACK_START, &device->state); + +p_err: + minfo("[SEN:D] %s(%dx%d, %d)\n", device, __func__, + device->image.window.width, device->image.window.height, ret); + return ret; +} + +int fimc_is_sensor_back_stop(struct fimc_is_device_sensor *device) +{ + int ret = 0; + int enable; + struct v4l2_subdev *subdev_flite; + + BUG_ON(!device); + BUG_ON(!device->subdev_flite); + + enable = 0; + subdev_flite = device->subdev_flite; + + if (!test_bit(FIMC_IS_SENSOR_BACK_START, &device->state)) { + warn("already back stop"); + goto p_err; + } + + if (test_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state)) { + warn("fimc_is_flite_stop, no waiting..."); + enable = FLITE_NOWAIT_FLAG << FLITE_NOWAIT_SHIFT; + } + + ret = v4l2_subdev_call(subdev_flite, video, s_stream, enable); + if (ret) { + merr("v4l2_flite_call(s_stream) is fail(%d)", device, ret); + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_BACK_START, &device->state); + +p_err: + minfo("[BAK:D] %s(%d)\n", device, __func__, ret); + return ret; +} + +int fimc_is_sensor_front_start(struct fimc_is_device_sensor *device, + u32 instant_cnt, + u32 nonblock) +{ + int ret = 0; + struct v4l2_subdev *subdev_module; + struct v4l2_subdev *subdev_csi; + struct fimc_is_module_enum *module; + + BUG_ON(!device); + BUG_ON(!device->pdata); + BUG_ON(!device->subdev_csi); + + if (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + merr("already front start", device); + ret = -EINVAL; + goto p_err; + } + + device->instant_cnt = instant_cnt; + subdev_csi = device->subdev_csi; + subdev_module = device->subdev_module; + if (!subdev_module) { + merr("subdev_module is NULL", device); + ret = -EINVAL; + goto p_err; + } + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev_module); + if (!module) { + merr("module is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = v4l2_subdev_call(subdev_csi, video, s_stream, IS_ENABLE_STREAM); + if (ret) { + merr("v4l2_csi_call(s_stream) is fail(%d)", device, ret); + goto p_err; + } + + mdbgd_sensor("%s(snesor id : %d, csi ch : %d, size : %d x %d)\n", device, + __func__, + module->id, + device->pdata->csi_ch, + device->image.window.width, + device->image.window.height); + + if (nonblock) { + schedule_work(&device->instant_work); + } else { + fimc_is_sensor_instanton(&device->instant_work); + if (device->instant_ret) { + merr("fimc_is_sensor_instanton is fail(%d)", device, device->instant_ret); + ret = device->instant_ret; + goto p_err; + } + } + +p_err: + return ret; +} + +int fimc_is_sensor_front_stop(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct v4l2_subdev *subdev_csi; + + BUG_ON(!device); + + if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + warn("already front stop"); + goto p_err; + } + + subdev_csi = device->subdev_csi; + + ret = fimc_is_sensor_stop(device); + if (ret) + merr("sensor stream off is failed(%d)\n", device, ret); + + ret = v4l2_subdev_call(subdev_csi, video, s_stream, IS_DISABLE_STREAM); + if (ret) + merr("v4l2_csi_call(s_stream) is fail(%d)", device, ret); + + set_bit(FIMC_IS_SENSOR_BACK_NOWAIT_STOP, &device->state); + clear_bit(FIMC_IS_SENSOR_FRONT_START, &device->state); + +p_err: + minfo("[FRT:D] %s(%d)\n", device, __func__, ret); + return ret; +} + +int fimc_is_sensor_gpio_off_softlanding(struct fimc_is_device_sensor *device) +{ + int ret = 0; + struct exynos_platform_fimc_is_sensor *pdata; + + BUG_ON(!device); + BUG_ON(!device->pdev); + BUG_ON(!device->pdata); + + pdata = device->pdata; + + if (!test_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state)) { + merr("%s : already gpio off", device, __func__); + goto p_err; + } + + if (!pdata->gpio_cfg) { + merr("gpio_cfg is NULL", device); + ret = -EINVAL; + goto p_err; + } + + ret = pdata->gpio_cfg(device->pdev, pdata->scenario, GPIO_SCENARIO_OFF); + if (ret) { + merr("gpio_cfg is fail(%d)", device, ret); + goto p_err; + } + + clear_bit(FIMC_IS_SENSOR_GPIO_ON, &device->state); + +p_err: + return ret; +} + +static int fimc_is_sensor_suspend(struct device *dev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +static int fimc_is_sensor_resume(struct device *dev) +{ + int ret = 0; + + info("%s\n", __func__); + + return ret; +} + +int fimc_is_sensor_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_device_sensor *device; + struct v4l2_subdev *subdev_csi; + + info("%s\n", __func__); + + BUG_ON(!pdev); + + device = (struct fimc_is_device_sensor *)platform_get_drvdata(pdev); + if (!device) { + err("device is NULL"); + return -EINVAL; + } + +#if !(defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)) +#if defined(CONFIG_VIDEOBUF2_ION) + if (device->mem.alloc_ctx) + vb2_ion_detach_iommu(device->mem.alloc_ctx); +#endif +#endif + + subdev_csi = device->subdev_csi; + if (!subdev_csi) + mwarn("subdev_csi is NULL", device); + + /* gpio uninit */ + if(device->pdata->is_softlanding == false) { + ret = fimc_is_sensor_gpio_off(device); + if (ret) { + mwarn("fimc_is_sensor_gpio_off is fail(%d)", device, ret); + } + } + + /* GSCL internal clock off */ + ret = fimc_is_sensor_iclk_off(device); + if (ret) + mwarn("fimc_is_sensor_iclk_off is fail(%d)", device, ret); + + /* Sensor clock on */ + ret = fimc_is_sensor_mclk_off(device); + if (ret) + mwarn("fimc_is_sensor_mclk_off is fail(%d)", device, ret); + + ret = v4l2_subdev_call(subdev_csi, core, s_power, 0); + if (ret) + mwarn("v4l2_csi_call(s_power) is fail(%d)", device, ret); + +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ set */ + if (test_bit(FIMC_IS_SENSOR_DRIVING, &device->state)) + fimc_is_sensor_set_qos_init(device, false); +#endif + + info("[SEN:D:%d] %s(%d)\n", device->instance, __func__, ret); + return 0; +} + +int fimc_is_sensor_runtime_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_device_sensor *device; + struct v4l2_subdev *subdev_csi; + + device = (struct fimc_is_device_sensor *)platform_get_drvdata(pdev); + if (!device) { + err("device is NULL"); + return -EINVAL; + } + + subdev_csi = device->subdev_csi; + if (!subdev_csi) { + merr("subdev_csi is NULL", device); + ret = -EINVAL; + goto p_err; + } + +/* HACK */ +/* at xyref 4415, when runtime_suspend operating, isp0 power is off thoroughly + so it needs to power on operation at sensor_runtime_resume operation */ +#if defined(CONFOG_SOC_EXYNOS4415) && !defined(CONFIG_PM_RUNTIME) + { + u32 val; + /* ISP0 */ + /* 1. set feedback mode */ + val = __raw_readl(PMUREG_ISP0_OPTION); + val = (val & ~(0x3<< 0)) | (0x2 << 0); + __raw_writel(val, PMUREG_ISP0_OPTION); + + /* 2. power on isp0 */ + val = __raw_readl(PMUREG_ISP0_CONFIGURATION); + val = (val & ~(0x7 << 0)) | (0x7 << 0); + __raw_writel(val, PMUREG_ISP0_CONFIGURATION); + } +#endif + + /* 1. Enable MIPI */ + ret = v4l2_subdev_call(subdev_csi, core, s_power, 1); + if (ret) { + merr("v4l2_csi_call(s_power) is fail(%d)", device, ret); + goto p_err; + } + + /* 2. Sensor clock on */ + ret = fimc_is_sensor_mclk_on(device); + if (ret) { + merr("fimc_is_sensor_mclk_on is fail(%d)", device, ret); + goto p_err; + } + +#if !(defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)) +#if defined(CONFIG_VIDEOBUF2_ION) + if (device->mem.alloc_ctx) + vb2_ion_attach_iommu(device->mem.alloc_ctx); + pr_debug("FIMC_IS runtime resume - ion attach complete\n"); +#endif +#endif + +p_err: + info("[SEN:D:%d] %s(%d)\n", device->instance, __func__, ret); + return ret; +} + +static const struct dev_pm_ops fimc_is_sensor_pm_ops = { + .suspend = fimc_is_sensor_suspend, + .resume = fimc_is_sensor_resume, + .runtime_suspend = fimc_is_sensor_runtime_suspend, + .runtime_resume = fimc_is_sensor_runtime_resume, +}; + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-sensor", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_fimc_is_sensor_match); + +static struct platform_driver fimc_is_sensor_driver = { + .probe = fimc_is_sensor_probe, + .remove = fimc_is_sensor_remove, + .driver = { + .name = FIMC_IS_SENSOR_DEV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_sensor_pm_ops, + .of_match_table = exynos_fimc_is_sensor_match, + } +}; + +module_platform_driver(fimc_is_sensor_driver); +#else +static struct platform_device_id fimc_is_sensor_driver_ids[] = { + { + .name = FIMC_IS_SENSOR_DEV_NAME, + .driver_data = 0, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fimc_is_sensor_driver_ids); + +static struct platform_driver fimc_is_sensor_driver = { + .probe = fimc_is_sensor_probe, + .remove = __devexit_p(fimc_is_sensor_remove), + .id_table = fimc_is_sensor_driver_ids, + .driver = { + .name = FIMC_IS_SENSOR_DEV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_is_sensor_pm_ops, + } +}; + +static int __init fimc_is_sensor_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&fimc_is_sensor_driver); + if (ret) + err("platform_driver_register failed: %d\n", ret); + + return ret; +} + +static void __exit fimc_is_sensor_exit(void) +{ + platform_driver_unregister(&fimc_is_sensor_driver); +} +module_init(fimc_is_sensor_init); +module_exit(fimc_is_sensor_exit); +#endif + +MODULE_AUTHOR("Gilyeon lim"); +MODULE_DESCRIPTION("Exynos FIMC_IS_SENSOR driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.h b/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.h new file mode 100644 index 000000000000..a0ae8a1b75c4 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-device-sensor.h @@ -0,0 +1,262 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef fimc_is_device_sensor_H +#define fimc_is_device_sensor_H + +#include +#include "fimc-is-mem.h" +#include "fimc-is-video.h" +#include "fimc-is-resourcemgr.h" +#include "fimc-is-device-flite.h" +#include "fimc-is-device-csi.h" + +struct fimc_is_video_ctx; +struct fimc_is_device_ischain; + +#define SENSOR_MAX_ENUM 20 +#define SENSOR_DEFAULT_FRAMERATE 30 + +#define SENSOR_SCENARIO_MASK 0xF0000000 +#define SENSOR_SCENARIO_SHIFT 28 +#define SENSOR_MODULE_MASK 0x0FFFFFFF +#define SENSOR_MODULE_SHIFT 0 + +#define SENSOR_SSTREAM_MASK 0x0000000F +#define SENSOR_SSTREAM_SHIFT 0 +#define SENSOR_INSTANT_MASK 0x0FFF0000 +#define SENSOR_INSTANT_SHIFT 16 +#define SENSOR_NOBLOCK_MASK 0xF0000000 +#define SENSOR_NOBLOCK_SHIFT 28 + +#define SENSOR_I2C_CH_MASK 0xFF +#define SENSOR_I2C_CH_SHIFT 0 +#define ACTUATOR_I2C_CH_MASK 0xFF00 +#define ACTUATOR_I2C_CH_SHIFT 8 +#define OIS_I2C_CH_MASK 0xFF0000 +#define OIS_I2C_CH_SHIFT 16 + +#define SENSOR_I2C_ADDR_MASK 0xFF +#define SENSOR_I2C_ADDR_SHIFT 0 +#define ACTUATOR_I2C_ADDR_MASK 0xFF00 +#define ACTUATOR_I2C_ADDR_SHIFT 8 +#define OIS_I2C_ADDR_MASK 0xFF0000 +#define OIS_I2C_ADDR_SHIFT 16 + +#define FIMC_IS_SENSOR_CFG(w, h, f, s, m) { \ + .width = w, \ + .height = h, \ + .framerate = f, \ + .settle = s, \ + .mode = m, \ +} + +enum fimc_is_sensor_output_entity { + FIMC_IS_SENSOR_OUTPUT_NONE = 0, + FIMC_IS_SENSOR_OUTPUT_FRONT, +}; + +enum fimc_is_sensor_force_stop { + FIMC_IS_BAD_FRAME_STOP = 0, + FIMC_IS_MIF_THROTTLING_STOP = 1, + FIMC_IS_FLITE_OVERFLOW_STOP = 2 +}; + +struct fimc_is_sensor_cfg { + u32 width; + u32 height; + u32 framerate; + u32 settle; + int mode; +}; + +struct fimc_is_sensor_ops { + int (*stream_on)(struct v4l2_subdev *subdev); + int (*stream_off)(struct v4l2_subdev *subdev); + + int (*s_duration)(struct v4l2_subdev *subdev, u64 duration); + int (*g_min_duration)(struct v4l2_subdev *subdev); + int (*g_max_duration)(struct v4l2_subdev *subdev); + + int (*s_exposure)(struct v4l2_subdev *subdev, u64 exposure); + int (*g_min_exposure)(struct v4l2_subdev *subdev); + int (*g_max_exposure)(struct v4l2_subdev *subdev); + + int (*s_again)(struct v4l2_subdev *subdev, u64 sensivity); + int (*g_min_again)(struct v4l2_subdev *subdev); + int (*g_max_again)(struct v4l2_subdev *subdev); + + int (*s_dgain)(struct v4l2_subdev *subdev); + int (*g_min_dgain)(struct v4l2_subdev *subdev); + int (*g_max_dgain)(struct v4l2_subdev *subdev); +}; + +struct fimc_is_module_enum { + u32 id; + struct v4l2_subdev *subdev; /* connected module subdevice */ + u32 device; /* connected sensor device */ + u32 pixel_width; + u32 pixel_height; + u32 active_width; + u32 active_height; + u32 max_framerate; + u32 position; + u32 mode; + u32 lanes; + u32 vcis; /* vci is valid only if mode is vc mode */ + struct fimc_is_vci *vci; + u32 cfgs; + struct fimc_is_sensor_cfg *cfg; + struct i2c_client *client; + struct sensor_open_extended ext; + struct fimc_is_sensor_ops *ops; + char *sensor_maker; + char *sensor_name; + char *setfile_name; + void *private_data; + int (*power_setpin)(struct device *); +}; + +enum fimc_is_sensor_state { + FIMC_IS_SENSOR_OPEN, + FIMC_IS_SENSOR_MCLK_ON, + FIMC_IS_SENSOR_ICLK_ON, + FIMC_IS_SENSOR_GPIO_ON, + FIMC_IS_SENSOR_DRIVING, + FIMC_IS_SENSOR_FRONT_START, + FIMC_IS_SENSOR_FRONT_DTP_STOP, + FIMC_IS_SENSOR_BACK_START, + FIMC_IS_SENSOR_BACK_NOWAIT_STOP +}; + +struct fimc_is_device_sensor { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + struct fimc_is_mem mem; + + u32 instance; + struct fimc_is_image image; + + struct fimc_is_video_ctx *vctx; + struct fimc_is_video video; + + struct fimc_is_device_ischain *ischain; + struct fimc_is_resourcemgr *resourcemgr; + struct fimc_is_module_enum module_enum[SENSOR_MAX_ENUM]; + + /* current control value */ + struct camera2_sensor_ctl sensor_ctl; + struct camera2_lens_ctl lens_ctl; + struct camera2_flash_ctl flash_ctl; + struct work_struct control_work; + struct fimc_is_frame *control_frame; + + u32 fcount; + u32 instant_cnt; + int instant_ret; + wait_queue_head_t instant_wait; + struct work_struct instant_work; + unsigned long state; + spinlock_t slock_state; + + /* hardware configuration */ + struct v4l2_subdev *subdev_module; + struct v4l2_subdev *subdev_csi; + struct v4l2_subdev *subdev_flite; + + int mode; + /* gain boost */ + int min_target_fps; + int max_target_fps; + int scene_mode; + + /* for vision control */ + int exposure_time; + u64 frame_duration; + + /* ENABLE_DTP */ + bool dtp_check; + struct timer_list dtp_timer; + unsigned long force_stop; + + struct exynos_platform_fimc_is_sensor *pdata; + void *private_data; + + /* DVFS state */ + bool request_cam_qos; + bool request_int_qos; + bool request_mif_qos; +}; + +int fimc_is_sensor_open(struct fimc_is_device_sensor *device, + struct fimc_is_video_ctx *vctx); +int fimc_is_sensor_close(struct fimc_is_device_sensor *device); +int fimc_is_sensor_s_input(struct fimc_is_device_sensor *device, + u32 input, + u32 scenario); +int fimc_is_sensor_s_format(struct fimc_is_device_sensor *device, + struct fimc_is_fmt *format, + u32 width, + u32 height); +int fimc_is_sensor_s_ctrl(struct fimc_is_device_sensor *device, + struct v4l2_control *ctrl); +int fimc_is_sensor_buffer_queue(struct fimc_is_device_sensor *device, + u32 index); +int fimc_is_sensor_buffer_finish(struct fimc_is_device_sensor *device, + u32 index); + +int fimc_is_sensor_front_start(struct fimc_is_device_sensor *device, + u32 instant_cnt, + u32 nonblock); +int fimc_is_sensor_front_stop(struct fimc_is_device_sensor *device); +int fimc_is_sensor_back_start(struct fimc_is_device_sensor *device); +int fimc_is_sensor_back_stop(struct fimc_is_device_sensor *device); + +int fimc_is_sensor_s_framerate(struct fimc_is_device_sensor *device, + struct v4l2_streamparm *param); +int fimc_is_sensor_s_bns(struct fimc_is_device_sensor *device, + u32 reatio); + +int fimc_is_sensor_s_frame_duration(struct fimc_is_device_sensor *device, + u32 frame_duration); +int fimc_is_sensor_s_exposure_time(struct fimc_is_device_sensor *device, + u32 exposure_time); + +int fimc_is_sensor_g_ctrl(struct fimc_is_device_sensor *device, + struct v4l2_control *ctrl); +int fimc_is_sensor_g_instance(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_framerate(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_fcount(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_width(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_height(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_bns_width(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_bns_height(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_bns_ratio(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_bratio(struct fimc_is_device_sensor *device); +int fimc_is_sensor_g_module(struct fimc_is_device_sensor *device, + struct fimc_is_module_enum **module); +int fimc_is_sensor_gpio_off_softlanding(struct fimc_is_device_sensor *device); + +/* sensor driver */ +int fimc_is_sensor_read8(struct i2c_client *client, + u16 addr, u8 *val); +int fimc_is_sensor_read16(struct i2c_client *client, + u16 addr, u16 *val); +int fimc_is_sensor_write(struct i2c_client *client, + u8 *buf, u32 size); +int fimc_is_sensor_write8(struct i2c_client *client, + u16 addr, u8 val); +int fimc_is_sensor_write16(struct i2c_client *client, + u16 addr, u16 val); + +#define CALL_MOPS(s, op, args...) (((s)->ops->op) ? ((s)->ops->op(args)) : 0) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-dt.c b/drivers/media/platform/exynos/fimc-is/fimc-is-dt.c new file mode 100644 index 000000000000..d14e8d26007a --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-dt.c @@ -0,0 +1,597 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif + +#include "fimc-is-core.h" +#include "fimc-is-dt.h" + +#ifdef CONFIG_OF +int get_pin_lookup_state(struct device *dev, + struct exynos_platform_fimc_is_sensor *pdata) +{ + int ret = 0; + u32 i, j, k; + char ch_name[30]; + struct exynos_sensor_pin (*pin_ctrls)[2][GPIO_CTRL_MAX]; + struct pinctrl_state *s; + + pin_ctrls = pdata->pin_ctrls; + + for (i = 0; i < SENSOR_SCENARIO_MAX; ++i) { + for (j = 0; j < GPIO_SCENARIO_MAX; ++j) { + for (k = 0; k < GPIO_CTRL_MAX; ++k) { + if (pin_ctrls[i][j][k].act == PIN_FUNCTION) { + snprintf(ch_name, sizeof(ch_name), "%s%d", + pin_ctrls[i][j][k].name, + pdata->csi_ch); + s = pinctrl_lookup_state(pdata->pinctrl, ch_name); + if (IS_ERR(s)) { + err("cam %s, ch %d pinctrl_lookup_state is failed", ch_name, pdata->csi_ch); + ret = -EINVAL; + goto p_err; + } else { + pin_ctrls[i][j][k].pin = (unsigned long)s; + pr_info("[%d][%d][%d][%s] gpio function cfg is seted", i, j, k, ch_name); + } + } + } + } + } + +p_err: + return ret; +} + +static int parse_gate_info(struct exynos_platform_fimc_is *pdata, struct device_node *np) +{ + int ret = 0; + struct device_node *group_np = NULL; + struct device_node *gate_info_np; + struct property *prop; + struct property *prop2; + const __be32 *p; + const char *s; + u32 i = 0, u = 0; + struct exynos_fimc_is_clk_gate_info *gate_info; + + /* get subip of fimc-is info */ + gate_info = kzalloc(sizeof(struct exynos_fimc_is_clk_gate_info), GFP_KERNEL); + if (!gate_info) { + printk(KERN_ERR "%s: no memory for fimc_is gate_info\n", __func__); + return -EINVAL; + } + + s = NULL; + /* get gate register info */ + prop2 = of_find_property(np, "clk_gate_strs", NULL); + of_property_for_each_u32(np, "clk_gate_enums", prop, p, u) { + printk(KERN_INFO "int value: %d\n", u); + s = of_prop_next_string(prop2, s); + if (s != NULL) { + printk(KERN_INFO "String value: %d-%s\n", u, s); + gate_info->gate_str[u] = s; + } + } + + /* gate info */ + gate_info_np = of_find_node_by_name(np, "clk_gate_ctrl"); + if (!gate_info_np) { + printk(KERN_ERR "%s: can't find fimc_is clk_gate_ctrl node\n", __func__); + ret = -ENOENT; + goto p_err; + } + i = 0; + while ((group_np = of_get_next_child(gate_info_np, group_np))) { + struct exynos_fimc_is_clk_gate_group *group = + &gate_info->groups[i]; + of_property_for_each_u32(group_np, "mask_clk_on_org", prop, p, u) { + printk(KERN_INFO "(%d) int1 value: %d\n", i, u); + group->mask_clk_on_org |= (1 << u); + } + of_property_for_each_u32(group_np, "mask_clk_off_self_org", prop, p, u) { + printk(KERN_INFO "(%d) int2 value: %d\n", i, u); + group->mask_clk_off_self_org |= (1 << u); + } + of_property_for_each_u32(group_np, "mask_clk_off_depend", prop, p, u) { + printk(KERN_INFO "(%d) int3 value: %d\n", i, u); + group->mask_clk_off_depend |= (1 << u); + } + of_property_for_each_u32(group_np, "mask_cond_for_depend", prop, p, u) { + printk(KERN_INFO "(%d) int4 value: %d\n", i, u); + group->mask_cond_for_depend |= (1 << u); + } + i++; + printk(KERN_INFO "(%d) [0x%x , 0x%x, 0x%x, 0x%x\n", i, + group->mask_clk_on_org, + group->mask_clk_off_self_org, + group->mask_clk_off_depend, + group->mask_cond_for_depend + ); + } + + pdata->gate_info = gate_info; + pdata->gate_info->user_clk_gate = exynos_fimc_is_set_user_clk_gate; + pdata->gate_info->clk_on_off = exynos_fimc_is_clk_gate; + + return 0; +p_err: + kfree(gate_info); + return ret; +} + +static int parse_dvfs_data(struct exynos_platform_fimc_is *pdata, struct device_node *np) +{ + u32 temp; + char *pprop; + + memset(pdata->dvfs_data, 0, sizeof(pdata->dvfs_data)); + DT_READ_U32(np, "default_int", pdata->dvfs_data[FIMC_IS_SN_DEFAULT][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "default_cam", pdata->dvfs_data[FIMC_IS_SN_DEFAULT][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "default_mif", pdata->dvfs_data[FIMC_IS_SN_DEFAULT][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "default_i2c", pdata->dvfs_data[FIMC_IS_SN_DEFAULT][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "front_preview_int", pdata->dvfs_data[FIMC_IS_SN_FRONT_PREVIEW][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "front_preview_cam", pdata->dvfs_data[FIMC_IS_SN_FRONT_PREVIEW][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "front_preview_mif", pdata->dvfs_data[FIMC_IS_SN_FRONT_PREVIEW][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "front_preview_i2c", pdata->dvfs_data[FIMC_IS_SN_FRONT_PREVIEW][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "front_capture_int", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAPTURE][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "front_capture_cam", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAPTURE][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "front_capture_mif", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAPTURE][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "front_capture_i2c", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAPTURE][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "front_camcording_int", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAMCORDING][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "front_camcording_cam", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAMCORDING][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "front_camcording_mif", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAMCORDING][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "front_camcording_i2c", pdata->dvfs_data[FIMC_IS_SN_FRONT_CAMCORDING][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "front_vt1_int", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT1][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "front_vt1_cam", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT1][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "front_vt1_mif", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT1][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "front_vt1_i2c", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT1][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "front_vt2_int", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT2][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "front_vt2_cam", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT2][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "front_vt2_mif", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT2][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "front_vt2_i2c", pdata->dvfs_data[FIMC_IS_SN_FRONT_VT2][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_preview_fhd_bns_off_int", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_preview_fhd_bns_off_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_preview_fhd_bns_off_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_preview_fhd_bns_off_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_preview_fhd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_preview_fhd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_preview_fhd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_preview_fhd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_I2C]); + /* if there's no FHD preview(with BNS off) dvfa data, set value of FHD recording data */ + if (!(pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_MIF])) { + pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_INT] = pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_INT]; + pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_CAM] = pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_CAM]; + pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_MIF] = pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_MIF]; + pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF][FIMC_IS_DVFS_I2C] = pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_FHD][FIMC_IS_DVFS_I2C]; + } + DT_READ_U32(np, "rear_preview_whd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_WHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_preview_whd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_WHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_preview_whd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_WHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_preview_whd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_WHD][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_preview_uhd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_UHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_preview_uhd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_UHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_preview_uhd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_UHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_preview_uhd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_PREVIEW_UHD][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_capture_int", pdata->dvfs_data[FIMC_IS_SN_REAR_CAPTURE][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_capture_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_CAPTURE][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_capture_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_CAPTURE][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_capture_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_CAPTURE][FIMC_IS_DVFS_I2C]); + + DT_READ_U32(np, "rear_camcording_fhd_bns_off_int", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_camcording_fhd_bns_off_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_camcording_fhd_bns_off_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_camcording_fhd_bns_off_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_camcording_fhd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_camcording_fhd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_camcording_fhd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_camcording_fhd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_camcording_whd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_WHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_camcording_whd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_WHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_camcording_whd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_WHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_camcording_whd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_WHD][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "rear_camcording_uhd_int", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_UHD][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "rear_camcording_uhd_cam", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_UHD][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "rear_camcording_uhd_mif", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_UHD][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "rear_camcording_uhd_i2c", pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_UHD][FIMC_IS_DVFS_I2C]); + /* if there's no FHD recording(with BNS off) dvfa data, set value of FHD recording data */ + if (!(pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_MIF])) { + pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_INT] = pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_INT]; + pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_CAM] = pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_CAM]; + pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_MIF] = pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_MIF]; + pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF][FIMC_IS_DVFS_I2C] = pdata->dvfs_data[FIMC_IS_SN_REAR_CAMCORDING_FHD][FIMC_IS_DVFS_I2C]; + } + DT_READ_U32(np, "dual_preview_int", pdata->dvfs_data[FIMC_IS_SN_DUAL_PREVIEW][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "dual_preview_cam", pdata->dvfs_data[FIMC_IS_SN_DUAL_PREVIEW][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "dual_preview_mif", pdata->dvfs_data[FIMC_IS_SN_DUAL_PREVIEW][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "dual_preview_i2c", pdata->dvfs_data[FIMC_IS_SN_DUAL_PREVIEW][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "dual_capture_int", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAPTURE][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "dual_capture_cam", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAPTURE][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "dual_capture_mif", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAPTURE][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "dual_capture_i2c", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAPTURE][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "dual_camcording_int", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAMCORDING][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "dual_camcording_cam", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAMCORDING][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "dual_camcording_mif", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAMCORDING][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "dual_camcording_i2c", pdata->dvfs_data[FIMC_IS_SN_DUAL_CAMCORDING][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "high_speed_fps_int", pdata->dvfs_data[FIMC_IS_SN_HIGH_SPEED_FPS][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "high_speed_fps_cam", pdata->dvfs_data[FIMC_IS_SN_HIGH_SPEED_FPS][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "high_speed_fps_mif", pdata->dvfs_data[FIMC_IS_SN_HIGH_SPEED_FPS][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "high_speed_fps_i2c", pdata->dvfs_data[FIMC_IS_SN_HIGH_SPEED_FPS][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "dis_enable_int", pdata->dvfs_data[FIMC_IS_SN_DIS_ENABLE][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "dis_enable_cam", pdata->dvfs_data[FIMC_IS_SN_DIS_ENABLE][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "dis_enable_mif", pdata->dvfs_data[FIMC_IS_SN_DIS_ENABLE][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "dis_enable_i2c", pdata->dvfs_data[FIMC_IS_SN_DIS_ENABLE][FIMC_IS_DVFS_I2C]); + DT_READ_U32(np, "max_int", pdata->dvfs_data[FIMC_IS_SN_MAX][FIMC_IS_DVFS_INT]); + DT_READ_U32(np, "max_cam", pdata->dvfs_data[FIMC_IS_SN_MAX][FIMC_IS_DVFS_CAM]); + DT_READ_U32(np, "max_mif", pdata->dvfs_data[FIMC_IS_SN_MAX][FIMC_IS_DVFS_MIF]); + DT_READ_U32(np, "max_i2c", pdata->dvfs_data[FIMC_IS_SN_MAX][FIMC_IS_DVFS_I2C]); + + return 0; +} + +static int parse_subip_info(struct exynos_platform_fimc_is *pdata, struct device_node *np) +{ + u32 temp; + char *pprop; + struct exynos_fimc_is_subip_info *subip_info; + + /* get subip of fimc-is info */ + subip_info = kzalloc(sizeof(struct exynos_fimc_is_subip_info), GFP_KERNEL); + if (!subip_info) { + printk(KERN_ERR "%s: no memory for fimc_is subip_info\n", __func__); + return -EINVAL; + } + + DT_READ_U32(np, "num_of_mcuctl", subip_info->_mcuctl.valid); + DT_READ_U32(np, "num_of_3a0", subip_info->_3a0.valid); + DT_READ_U32(np, "num_of_3a1", subip_info->_3a1.valid); + DT_READ_U32(np, "num_of_isp", subip_info->_isp.valid); + DT_READ_U32(np, "num_of_drc", subip_info->_drc.valid); + DT_READ_U32(np, "num_of_scc", subip_info->_scc.valid); + DT_READ_U32(np, "num_of_odc", subip_info->_odc.valid); + DT_READ_U32(np, "num_of_dis", subip_info->_dis.valid); + DT_READ_U32(np, "num_of_dnr", subip_info->_dnr.valid); + DT_READ_U32(np, "num_of_scp", subip_info->_scp.valid); + DT_READ_U32(np, "num_of_fd", subip_info->_fd.valid); + + DT_READ_U32(np, "full_bypass_mcuctl", subip_info->_mcuctl.full_bypass); + DT_READ_U32(np, "full_bypass_3a0", subip_info->_3a0.full_bypass); + DT_READ_U32(np, "full_bypass_3a1", subip_info->_3a1.full_bypass); + DT_READ_U32(np, "full_bypass_isp", subip_info->_isp.full_bypass); + DT_READ_U32(np, "full_bypass_drc", subip_info->_drc.full_bypass); + DT_READ_U32(np, "full_bypass_scc", subip_info->_scc.full_bypass); + DT_READ_U32(np, "full_bypass_odc", subip_info->_odc.full_bypass); + DT_READ_U32(np, "full_bypass_dis", subip_info->_dis.full_bypass); + DT_READ_U32(np, "full_bypass_dnr", subip_info->_dnr.full_bypass); + DT_READ_U32(np, "full_bypass_scp", subip_info->_scp.full_bypass); + DT_READ_U32(np, "full_bypass_fd", subip_info->_fd.full_bypass); + + DT_READ_U32(np, "version_mcuctl", subip_info->_mcuctl.version); + DT_READ_U32(np, "version_3a0", subip_info->_3a0.version); + DT_READ_U32(np, "version_3a1", subip_info->_3a1.version); + DT_READ_U32(np, "version_isp", subip_info->_isp.version); + DT_READ_U32(np, "version_drc", subip_info->_drc.version); + DT_READ_U32(np, "version_scc", subip_info->_scc.version); + DT_READ_U32(np, "version_odc", subip_info->_odc.version); + DT_READ_U32(np, "version_dis", subip_info->_dis.version); + DT_READ_U32(np, "version_dnr", subip_info->_dnr.version); + DT_READ_U32(np, "version_scp", subip_info->_scp.version); + DT_READ_U32(np, "version_fd", subip_info->_fd.version); + + pdata->subip_info = subip_info; + + return 0; +} + +int fimc_is_power_initpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + int gpio_none = 0; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + pdata = dev->platform_data; + + if (!pdata->sensor_id || (pdata->sensor_id >= SENSOR_NAME_END)) { + err("check the sensor id. sensor_id %d", pdata->sensor_id); + return -ENODEV; + } + + /* POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, NULL, 0, PIN_END); + + /* POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} + +int fimc_is_power_setpin(struct device *dev, int position, int sensor_id) +{ + struct fimc_is_core *core; + struct fimc_is_device_sensor *sensor; + int ret = 0; + int i, pos, found; + + if (!fimc_is_dev) { + err("fimc_is_dev is not yet probed"); + return -ENODEV; + } + + if (!sensor_id || (sensor_id >= SENSOR_NAME_END)) { + err("check the sensor id. sensor_id %d", sensor_id); + return -ENODEV; + } + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core is NULL"); + return -EINVAL; + } + + if (!atomic_read(&core->resourcemgr.rsccount_module)) + err("sensor driver not probed"); + + sensor = &core->sensor[position]; + + /* Call power_setpin and return */ + for (i = 0; i < atomic_read(&core->resourcemgr.rsccount_module); i++) { + if (sensor_id == sensor->module_enum[i].id) { + info("%s: sensor found(id %d). %s\n", __func__, sensor_id, + (position == sensor->module_enum[i].position) ? "" : "position not matched"); + + if (sensor->module_enum[i].power_setpin) + ret = sensor->module_enum[i].power_setpin(dev); + + return ret; + } + } + + /* Enumerate probed sensor lists if not found */ + for (pos = 0, found = 0; pos < FIMC_IS_MAX_NODES; pos++, found = 0) { + sensor = &core->sensor[pos]; + for (i = 0; i < atomic_read(&core->resourcemgr.rsccount_module); i++) { + if (sensor->module_enum[i].id) { + info("Camera sensor %d: id %3d. %s\n", pos, sensor->module_enum[i].id, + sensor->module_enum[i].setfile_name ? + sensor->module_enum[i].setfile_name : ""); + found++; + } + } + + if (!found) + info("Camera sensor %d: none\n", pos); + } + + err("sensor not found (pos %d, id %d)", position, sensor_id); + + return 0; +} + +struct exynos_platform_fimc_is *fimc_is_parse_dt(struct device *dev) +{ + void *ret = NULL; + struct exynos_platform_fimc_is *pdata; + struct device_node *subip_info_np; + struct device_node *dvfs_np; + struct device_node *np = dev->of_node; +#if defined CONFIG_COMPANION_USE || defined CONFIG_USE_VENDER_FEATURE + int retVal = 0; +#endif + + if (!np) + return ERR_PTR(-ENOENT); + + pdata = kzalloc(sizeof(struct exynos_platform_fimc_is), GFP_KERNEL); + if (!pdata) { + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + return ERR_PTR(-ENOMEM); + } + + pdata->clk_cfg = exynos_fimc_is_cfg_clk; + pdata->clk_on = exynos_fimc_is_clk_on; + pdata->clk_off = exynos_fimc_is_clk_off; + pdata->print_clk = exynos_fimc_is_print_clk; + pdata->print_cfg = exynos_fimc_is_print_cfg; + pdata->print_pwr = exynos_fimc_is_print_pwr; + + dev->platform_data = pdata; + +#ifdef CONFIG_COMPANION_USE + retVal = of_property_read_u32(np, "companion_spi_channel", &pdata->companion_spi_channel); + if (retVal) { + err("spi_channel read is fail(%d)", retVal); + } + + pdata->use_two_spi_line = of_property_read_bool(np, "use_two_spi_line"); +#endif +#ifdef CONFIG_USE_VENDER_FEATURE + retVal = of_property_read_u32(np, "use_sensor_dynamic_voltage_mode", &pdata->use_sensor_dynamic_voltage_mode); + if (retVal) { + err("use_sensor_dynamic_voltage_mode read is fail(%d)", retVal); + pdata->use_sensor_dynamic_voltage_mode = 0; + } +#ifdef CONFIG_OIS_USE + pdata->use_ois = of_property_read_bool(np, "use_ois"); + if (!pdata->use_ois) { + err("use_ois not use(%d)", pdata->use_ois); + } +#endif /* CONFIG_OIS_USE */ + pdata->use_ois_hsi2c = of_property_read_bool(np, "use_ois_hsi2c"); + if (!pdata->use_ois_hsi2c) { + err("use_ois_hsi2c not use(%d)", pdata->use_ois_hsi2c); + } + + pdata->use_module_check = of_property_read_bool(np, "use_module_check"); + if (!pdata->use_module_check) { + err("use_module_check not use(%d)", pdata->use_module_check); + } +#endif + subip_info_np = of_find_node_by_name(np, "subip_info"); + if (!subip_info_np) { + printk(KERN_ERR "%s: can't find fimc_is subip_info node\n", __func__); + ret = ERR_PTR(-ENOENT); + goto p_err; + } + parse_subip_info(pdata, subip_info_np); + + if (parse_gate_info(pdata, np) < 0) + printk(KERN_ERR "%s: can't parse clock gate info node\n", __func__); + + dvfs_np = of_find_node_by_name(np, "fimc_is_dvfs"); + if (!dvfs_np) { + printk(KERN_ERR "%s: can't find fimc_is_dvfs node\n", __func__); + ret = ERR_PTR(-ENOENT); + goto p_err; + } + parse_dvfs_data(pdata, dvfs_np); + + return pdata; +p_err: + kfree(pdata); + return ret; +} + +int fimc_is_sensor_parse_dt(struct platform_device *pdev) +{ + int ret = 0; + u32 temp; + char *pprop; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + struct device *dev; + const char *name; + u32 id; + + BUG_ON(!pdev); + BUG_ON(!pdev->dev.of_node); + + dev = &pdev->dev; + dnode = dev->of_node; + + pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_sensor), GFP_KERNEL); + if (!pdata) { + pr_err("%s: no memory for platform data\n", __func__); + return -ENOMEM; + } + + pdata->gpio_cfg = exynos_fimc_is_sensor_pins_cfg; + pdata->iclk_cfg = exynos_fimc_is_sensor_iclk_cfg; + pdata->iclk_on = exynos_fimc_is_sensor_iclk_on; + pdata->iclk_off = exynos_fimc_is_sensor_iclk_off; + pdata->mclk_on = exynos_fimc_is_sensor_mclk_on; + pdata->mclk_off = exynos_fimc_is_sensor_mclk_off; + + ret = of_property_read_u32(dnode, "scenario", &pdata->scenario); + if (ret) { + err("scenario read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch); + if (ret) { + err("mclk_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "csi_ch", &pdata->csi_ch); + if (ret) { + err("csi_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "flite_ch", &pdata->flite_ch); + if (ret) { + err("flite_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "i2c_ch", &pdata->i2c_ch); + if (ret) { + err("i2c_ch read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "i2c_addr", &pdata->i2c_addr); + if (ret) { + err("i2c_addr read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "is_bns", &pdata->is_bns); + if (ret) { + err("is_bns read is fail(%d)", ret); + goto p_err; + } + + ret = of_property_read_u32(dnode, "id", &id); + if (ret) { + err("id read is fail(%d)", ret); + goto p_err; + } + + DT_READ_U32(dnode, "flash_first_gpio", pdata->flash_first_gpio); + DT_READ_U32(dnode, "flash_second_gpio", pdata->flash_second_gpio); + + ret = of_property_read_string(dnode, "sensor_name", &name); + if (ret) { + err("sensor_name read is fail(%d)", ret); + goto p_err; + } + strcpy(pdata->sensor_name, name); + + ret = of_property_read_u32(dnode, "sensor_id", &pdata->sensor_id); + if (ret) { + err("sensor_id read is fail(%d)", ret); + goto p_err; + } + + dev->platform_data = pdata; + + ret = fimc_is_power_setpin(dev, id, pdata->sensor_id); + if (ret) + err("power_setpin failed(%d). id %d", ret, id); + + pdev->id = id; + + pdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pdata->pinctrl)) { + err("devm_pinctrl_get is fail"); + goto p_err; + } else { + ret = get_pin_lookup_state(dev, pdata); + if (ret < 0) { + err("fimc_is_get_pin_lookup_state is fail"); + goto p_err; + } + } + + return ret; + +p_err: + kfree(pdata); + return ret; +} +#else +struct exynos_platform_fimc_is *fimc_is_parse_dt(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-dt.h b/drivers/media/platform/exynos/fimc-is/fimc-is-dt.h new file mode 100644 index 000000000000..81b4591d12ef --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-dt.h @@ -0,0 +1,37 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#ifndef FIMC_IS_DT_H +#define FIMC_IS_DT_H + +#define DT_READ_U32(node, key, value) do {\ + pprop = key; \ + temp = 0; \ + if (of_property_read_u32((node), key, &temp)) \ + pr_warn("%s: no property in the node.\n", pprop);\ + (value) = temp; \ + } while (0) + +#define DT_READ_STR(node, key, value) do {\ + pprop = key; \ + if (of_property_read_string((node), key, &name)) \ + pr_warn("%s: no property in the node.\n", pprop);\ + (value) = name; \ + } while (0) + +int get_pin_lookup_state(struct device *dev, struct exynos_platform_fimc_is_sensor *pdata); +int fimc_is_power_initpin(struct device *dev); +int fimc_is_power_setpin(struct device *dev, int position, int sensor_id); +struct exynos_platform_fimc_is *fimc_is_parse_dt(struct device *dev); +int fimc_is_sensor_parse_dt(struct platform_device *pdev); +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.c b/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.c new file mode 100644 index 000000000000..48ef92121875 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.c @@ -0,0 +1,762 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "fimc-is-core.h" +#include "fimc-is-dvfs.h" + +extern struct pm_qos_request exynos_isp_qos_cpu_min; +extern struct pm_qos_request exynos_isp_qos_cpu_max; +extern struct pm_qos_request exynos_isp_qos_int; +extern struct pm_qos_request exynos_isp_qos_mem; +extern struct pm_qos_request exynos_isp_qos_cam; +extern struct pm_qos_request exynos_isp_qos_disp; + +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAPTURE); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAMCORDING); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_PREVIEW); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_HIGH_SPEED_FPS); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_WHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_UHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_WHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_UHD); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT1); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT2); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_PREVIEW); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAPTURE); +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DIS_ENABLE); + +#if defined(ENABLE_DVFS) +/* + * Static Scenario Set + * You should describe static scenario by priorities of scenario. + * And you should name array 'static_scenarios' + */ +static struct fimc_is_dvfs_scenario static_scenarios[] = { + { + .scenario_id = FIMC_IS_SN_DUAL_CAMCORDING, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_DUAL_CAMCORDING), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAMCORDING), + }, { + .scenario_id = FIMC_IS_SN_DUAL_PREVIEW, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_DUAL_PREVIEW), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_PREVIEW), + }, { + .scenario_id = FIMC_IS_SN_HIGH_SPEED_FPS, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_HIGH_SPEED_FPS), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_HIGH_SPEED_FPS), + }, { + .scenario_id = FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF), + }, { + .scenario_id = FIMC_IS_SN_REAR_CAMCORDING_FHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_CAMCORDING_FHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD), + }, { + .scenario_id = FIMC_IS_SN_REAR_CAMCORDING_WHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_CAMCORDING_WHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_WHD), + }, { + .scenario_id = FIMC_IS_SN_REAR_CAMCORDING_UHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_CAMCORDING_UHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_UHD), + }, { + .scenario_id = FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF), + }, { + .scenario_id = FIMC_IS_SN_REAR_PREVIEW_FHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_PREVIEW_FHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD), + }, { + .scenario_id = FIMC_IS_SN_REAR_PREVIEW_WHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_PREVIEW_WHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_WHD), + }, { + .scenario_id = FIMC_IS_SN_REAR_PREVIEW_UHD, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_PREVIEW_UHD), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_UHD), + }, { + .scenario_id = FIMC_IS_SN_FRONT_VT1, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_FRONT_VT1), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT1), + }, { + .scenario_id = FIMC_IS_SN_FRONT_VT2, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_FRONT_VT2), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT2), + }, { + .scenario_id = FIMC_IS_SN_FRONT_PREVIEW, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_FRONT_PREVIEW), + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_PREVIEW), + }, +}; + +/* + * Dynamic Scenario Set + * You should describe static scenario by priorities of scenario. + * And you should name array 'dynamic_scenarios' + */ +static struct fimc_is_dvfs_scenario dynamic_scenarios[] = { + { + .scenario_id = FIMC_IS_SN_DUAL_CAPTURE, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_DUAL_CAPTURE), + .keep_frame_tick = KEEP_FRAME_TICK_DEFAULT, + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAPTURE), + }, + { + .scenario_id = FIMC_IS_SN_REAR_CAPTURE, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_REAR_CAPTURE), + .keep_frame_tick = KEEP_FRAME_TICK_DEFAULT, + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAPTURE), + }, + { + .scenario_id = FIMC_IS_SN_DIS_ENABLE, + .scenario_nm = DVFS_SN_STR(FIMC_IS_SN_DIS_ENABLE), + .keep_frame_tick = KEEP_FRAME_TICK_DEFAULT, + .check_func = GET_DVFS_CHK_FUNC(FIMC_IS_SN_DIS_ENABLE), + }, +}; +#else +/* + * Default Scenario can not be seleted, this declaration is for static variable. + */ +static struct fimc_is_dvfs_scenario static_scenarios[] = { + { + .scenario_id = FIMC_IS_SN_DEFAULT, + .scenario_nm = NULL, + .keep_frame_tick = 0, + .check_func = NULL, + }, +}; +static struct fimc_is_dvfs_scenario dynamic_scenarios[] = { + { + .scenario_id = FIMC_IS_SN_DEFAULT, + .scenario_nm = NULL, + .keep_frame_tick = 0, + .check_func = NULL, + }, +}; +#endif + +static inline int fimc_is_get_open_sensor_cnt(struct fimc_is_core *core) +{ + int i, sensor_cnt = 0; + + for (i = 0; i < FIMC_IS_MAX_NODES; i++) + if (test_bit(FIMC_IS_SENSOR_OPEN, &(core->sensor[i].state))) + sensor_cnt++; + + return sensor_cnt; +} + +static inline u32 fimc_is_chk_req(struct fimc_is_frame *frame, enum fimc_is_video_dev_num vid) +{ + int i; + struct camera2_node *node; + u32 ret = 0; + + if (frame == NULL) + return ret; + + for (i = 0; i < CAPTURE_NODE_MAX; i++) { + node = &frame->shot_ext->node_group.capture[i]; + if (node->vid == vid) { + ret = node->request; + break; + } + } + + return ret; +} + +/* dual capture */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAPTURE) +{ + struct fimc_is_core *core; + int sensor_cnt = 0; + core = (struct fimc_is_core *)device->interface->core; + sensor_cnt = fimc_is_get_open_sensor_cnt(core); + + if ((sensor_cnt >= 2) && + (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state))) + return 1; + else + return 0; +} + +/* dual camcording */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_CAMCORDING) +{ + struct fimc_is_core *core; + int sensor_cnt = 0; + core = (struct fimc_is_core *)device->interface->core; + sensor_cnt = fimc_is_get_open_sensor_cnt(core); + + if ((sensor_cnt >= 2) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + == ISS_SUB_SCENARIO_DUAL_VIDEO)) + return 1; + else + return 0; +} + +/* dual preview */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DUAL_PREVIEW) +{ + struct fimc_is_core *core; + int sensor_cnt = 0; + core = (struct fimc_is_core *)device->interface->core; + sensor_cnt = fimc_is_get_open_sensor_cnt(core); + + if ((sensor_cnt >= 2) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + != ISS_SUB_SCENARIO_DUAL_VIDEO)) + return 1; + else + return 0; +} + +/* high speed fps */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_HIGH_SPEED_FPS) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) > 30)) + return 1; + else + return 0; +} + +/* rear camcording FHD with BNS off */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD_BNS_OFF) +{ + u32 mask = (device->setfile & FIMC_IS_SETFILE_MASK); + bool setfile_flag = (mask == ISS_SUB_SCENARIO_VIDEO) || + (mask == ISS_SUB_SCENARIO_VIDEO_WDR); + + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height <= SIZE_FHD) && + (fimc_is_sensor_g_bns_ratio(device->sensor) <= 1000) && + setfile_flag) + return 1; + else + return 0; +} + +/* rear camcording FHD*/ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_FHD) +{ + u32 mask = (device->setfile & FIMC_IS_SETFILE_MASK); + bool setfile_flag = (mask == ISS_SUB_SCENARIO_VIDEO) || + (mask == ISS_SUB_SCENARIO_VIDEO_WDR); + + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height <= SIZE_FHD) && + (fimc_is_sensor_g_bns_ratio(device->sensor) > 1000) && + setfile_flag) + return 1; + else + return 0; +} + +/* rear camcording WHD*/ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_WHD) +{ + u32 mask = (device->setfile & FIMC_IS_SETFILE_MASK); + bool setfile_flag = (mask == ISS_SUB_SCENARIO_VIDEO) || + (mask == ISS_SUB_SCENARIO_VIDEO_WDR); + + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height > SIZE_FHD) && + (device->chain3_width * device->chain3_height <= SIZE_WHD) && + setfile_flag) + return 1; + else + return 0; +} + +/* rear camcording UHD*/ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAMCORDING_UHD) +{ + u32 mask = (device->setfile & FIMC_IS_SETFILE_MASK); + bool setfile_flag = (mask == ISS_SUB_SCENARIO_UHD_30FPS) || + (mask == ISS_SUB_SCENARIO_UHD_30FPS_WDR); + + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height > SIZE_WHD) && + (device->chain3_width * device->chain3_height <= SIZE_UHD) && + setfile_flag) + return 1; + else + return 0; +} + +/* rear preview FHD with BNS off */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD_BNS_OFF) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height <= SIZE_FHD) && + (fimc_is_sensor_g_bns_ratio(device->sensor) <= 1000) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + != ISS_SUB_SCENARIO_VIDEO)) + + return 1; + else + return 0; +} + +/* rear preview FHD */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_FHD) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height <= SIZE_FHD) && + (fimc_is_sensor_g_bns_ratio(device->sensor) > 1000) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + != ISS_SUB_SCENARIO_VIDEO)) + + return 1; + else + return 0; +} + +/* rear preview WHD */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_WHD) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height > SIZE_FHD) && + (device->chain3_width * device->chain3_height <= SIZE_WHD) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + != ISS_SUB_SCENARIO_VIDEO)) + return 1; + else + return 0; +} + +/* rear preview UHD */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_PREVIEW_UHD) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + (fimc_is_sensor_g_framerate(device->sensor) <= 30) && + (device->chain3_width * device->chain3_height > SIZE_WHD) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + != ISS_SUB_SCENARIO_VIDEO)) + return 1; + else + return 0; +} + +/* front vt1 */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT1) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_FRONT) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + == ISS_SUB_SCENARIO_FRONT_VT1)) + return 1; + else + return 0; +} + +/* front vt2 */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_VT2) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_FRONT) && + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + == ISS_SUB_SCENARIO_FRONT_VT2)) + return 1; + else + return 0; +} + +/* front preview */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_FRONT_PREVIEW) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_FRONT) && + !(((device->setfile & FIMC_IS_SETFILE_MASK) \ + == ISS_SUB_SCENARIO_FRONT_VT1) || + ((device->setfile & FIMC_IS_SETFILE_MASK) \ + == ISS_SUB_SCENARIO_FRONT_VT2))) + return 1; + else + return 0; +} + +/* rear capture */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_REAR_CAPTURE) +{ + if ((device->sensor->pdev->id == SENSOR_POSITION_REAR) && + ((test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) || + fimc_is_chk_req(frame, FIMC_IS_VIDEO_SCC_NUM))) + return 1; + else + return 0; +} + +/* dis */ +DECLARE_DVFS_CHK_FUNC(FIMC_IS_SN_DIS_ENABLE) +{ + if (test_bit(FIMC_IS_SUBDEV_START, &device->dis.state)) + return 1; + else + return 0; +} + +static int fimc_is_set_pwm(struct fimc_is_device_ischain *device, u32 pwm_qos) +{ + int ret = 0; + u32 base_addr; + void __iomem *addr; + + base_addr = GET_FIMC_IS_ADDR_OF_SUBIP2(device, pwm); + + if (base_addr) { + addr = ioremap(base_addr + FIMC_IS_PWM_TCNTB0, SZ_4); + writel(pwm_qos, addr); + dbg("PWM SFR Read(%08X), pwm_qos(%08X)\n", readl(addr), pwm_qos); + iounmap(addr); + } + + return ret; +} + +int fimc_is_dvfs_init(struct fimc_is_resourcemgr *resourcemgr) +{ + int i; + pr_info("%s\n", __func__); + + BUG_ON(!resourcemgr); + + resourcemgr->dvfs_ctrl.cur_cpu_min_qos = 0; + resourcemgr->dvfs_ctrl.cur_cpu_max_qos = 0; + resourcemgr->dvfs_ctrl.cur_int_qos = 0; + resourcemgr->dvfs_ctrl.cur_mif_qos = 0; + resourcemgr->dvfs_ctrl.cur_cam_qos = 0; + resourcemgr->dvfs_ctrl.cur_i2c_qos = 0; + resourcemgr->dvfs_ctrl.cur_disp_qos = 0; + + /* init spin_lock for clock gating */ + mutex_init(&resourcemgr->dvfs_ctrl.lock); + + if (!(resourcemgr->dvfs_ctrl.static_ctrl)) + resourcemgr->dvfs_ctrl.static_ctrl = + kzalloc(sizeof(struct fimc_is_dvfs_scenario_ctrl), GFP_KERNEL); + if (!(resourcemgr->dvfs_ctrl.dynamic_ctrl)) + resourcemgr->dvfs_ctrl.dynamic_ctrl = + kzalloc(sizeof(struct fimc_is_dvfs_scenario_ctrl), GFP_KERNEL); + + if (!resourcemgr->dvfs_ctrl.static_ctrl || !resourcemgr->dvfs_ctrl.dynamic_ctrl) { + err("dvfs_ctrl alloc is failed!!\n"); + return -ENOMEM; + } + + /* set priority by order */ + for (i = 0; i < ARRAY_SIZE(static_scenarios); i++) + static_scenarios[i].priority = i; + for (i = 0; i < ARRAY_SIZE(dynamic_scenarios); i++) + dynamic_scenarios[i].priority = i; + + resourcemgr->dvfs_ctrl.static_ctrl->cur_scenario_id = -1; + resourcemgr->dvfs_ctrl.static_ctrl->cur_scenario_idx = -1; + resourcemgr->dvfs_ctrl.static_ctrl->scenarios = static_scenarios; + if (static_scenarios[0].scenario_id == FIMC_IS_SN_DEFAULT) + resourcemgr->dvfs_ctrl.static_ctrl->scenario_cnt = 0; + else + resourcemgr->dvfs_ctrl.static_ctrl->scenario_cnt = ARRAY_SIZE(static_scenarios); + + resourcemgr->dvfs_ctrl.dynamic_ctrl->cur_scenario_id = -1; + resourcemgr->dvfs_ctrl.dynamic_ctrl->cur_scenario_idx = -1; + resourcemgr->dvfs_ctrl.dynamic_ctrl->cur_frame_tick = -1; + resourcemgr->dvfs_ctrl.dynamic_ctrl->scenarios = dynamic_scenarios; + if (static_scenarios[0].scenario_id == FIMC_IS_SN_DEFAULT) + resourcemgr->dvfs_ctrl.dynamic_ctrl->scenario_cnt = 0; + else + resourcemgr->dvfs_ctrl.dynamic_ctrl->scenario_cnt = ARRAY_SIZE(dynamic_scenarios); + + return 0; +} + +int fimc_is_dvfs_sel_scenario(u32 type, struct fimc_is_device_ischain *device, struct fimc_is_frame *frame) +{ + struct fimc_is_dvfs_ctrl *dvfs_ctrl; + struct fimc_is_dvfs_scenario_ctrl *static_ctrl, *dynamic_ctrl; + struct fimc_is_dvfs_scenario *scenarios; + struct fimc_is_dvfs_scenario *cur_scenario; + struct fimc_is_resourcemgr *resourcemgr; + int i, scenario_id, scenario_cnt; + + if (device == NULL) { + err("device is NULL\n"); + return -EINVAL; + } + + resourcemgr = device->resourcemgr; + dvfs_ctrl = &(resourcemgr->dvfs_ctrl); + static_ctrl = dvfs_ctrl->static_ctrl; + dynamic_ctrl = dvfs_ctrl->dynamic_ctrl; + + if (type == FIMC_IS_DYNAMIC_SN) { + /* dynamic scenario */ + if (!dynamic_ctrl) { + err("dynamic_dvfs_ctrl is NULL\n"); + return -EINVAL; + } + + if (dynamic_ctrl->scenario_cnt == 0) { + pr_debug("dynamic_scenario's count is jero\n"); + return -EINVAL; + } + + scenarios = dynamic_ctrl->scenarios; + scenario_cnt = dynamic_ctrl->scenario_cnt; + + if (dynamic_ctrl->cur_frame_tick >= 0) { + (dynamic_ctrl->cur_frame_tick)--; + /* + * when cur_frame_tick is lower than 0, clear current scenario. + * This means that current frame tick to keep dynamic scenario + * was expired. + */ + if (dynamic_ctrl->cur_frame_tick < 0) { + dynamic_ctrl->cur_scenario_id = -1; + dynamic_ctrl->cur_scenario_idx = -1; + } + } + } else { + /* static scenario */ + if (!static_ctrl) { + err("static_dvfs_ctrl is NULL\n"); + return -EINVAL; + } + + if (static_ctrl->scenario_cnt == 0) { + pr_debug("static_scenario's count is jero\n"); + return -EINVAL; + } + + scenarios = static_ctrl->scenarios; + scenario_cnt = static_ctrl->scenario_cnt; + } + + for (i = 0; i < scenario_cnt; i++) { + if (!scenarios[i].check_func) { + warn("check_func[%d] is NULL\n", i); + continue; + } + + if ((scenarios[i].check_func(device, frame)) > 0) { + scenario_id = scenarios[i].scenario_id; + + if (type == FIMC_IS_DYNAMIC_SN) { + cur_scenario = &scenarios[dynamic_ctrl->cur_scenario_idx]; + + /* + * if condition 1 or 2 is true + * condition 1 : There's no dynamic scenario applied. + * condition 2 : Finded scenario's prority was higher than current + */ + if ((dynamic_ctrl->cur_scenario_id <= 0) || + (scenarios[i].priority < (cur_scenario->priority))) { + dynamic_ctrl->cur_scenario_id = scenarios[i].scenario_id; + dynamic_ctrl->cur_scenario_idx = i; + dynamic_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick; + } else { + /* if finded scenario is same */ + if (scenarios[i].priority == (cur_scenario->priority)) + dynamic_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick; + return -EAGAIN; + } + } else { + static_ctrl->cur_scenario_id = scenario_id; + static_ctrl->cur_scenario_idx = i; + static_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick; + } + + return scenario_id; + } + } + + if (type == FIMC_IS_DYNAMIC_SN) + return -EAGAIN; + + { + struct fimc_is_core *core; + int sensor_cnt = 0; + core = (struct fimc_is_core *)device->interface->core; + sensor_cnt = fimc_is_get_open_sensor_cnt(core); + + warn("couldn't find static dvfs scenario [sensor:(%d/%d)/fps:%d/setfile:%d/scp size:(%d/%d)]\n", + sensor_cnt, + device->sensor->pdev->id, + fimc_is_sensor_g_framerate(device->sensor), + (device->setfile & FIMC_IS_SETFILE_MASK), + device->chain3_width, + device->chain3_height); + } + + static_ctrl->cur_scenario_id = FIMC_IS_SN_DEFAULT; + static_ctrl->cur_scenario_idx = -1; + static_ctrl->cur_frame_tick = -1; + + return FIMC_IS_SN_DEFAULT; +} + +int fimc_is_get_qos(struct fimc_is_core *core, u32 type, u32 scenario_id) +{ + struct exynos_platform_fimc_is *pdata = NULL; + int qos = 0; + + pdata = core->pdata; + if (pdata == NULL) { + err("pdata is NULL\n"); + return -EINVAL; + } + + if (!pdata->get_int_qos || !pdata->get_mif_qos) + goto struct_qos; + + switch (type) { + case FIMC_IS_DVFS_INT: + qos = pdata->get_int_qos(scenario_id); + break; + case FIMC_IS_DVFS_MIF: + qos = pdata->get_mif_qos(scenario_id); + break; + case FIMC_IS_DVFS_I2C: + if (pdata->get_i2c_qos) + qos = pdata->get_i2c_qos(scenario_id); + break; + } + goto exit; + +struct_qos: + if (max(0, (int)type) >= FIMC_IS_DVFS_END) { + err("Cannot find DVFS value"); + return -EINVAL; + } + + qos = pdata->dvfs_data[scenario_id][type]; + +exit: + return qos; +} + +int fimc_is_set_dvfs(struct fimc_is_device_ischain *device, u32 scenario_id) +{ + int ret = 0; + int cpu_min_qos, cpu_max_qos, int_qos, mif_qos, i2c_qos, cam_qos, disp_qos, pwm_qos = 0; + int refcount; + struct fimc_is_core *core; + struct fimc_is_resourcemgr *resourcemgr; + struct fimc_is_dvfs_ctrl *dvfs_ctrl; + + if (device == NULL) { + err("device is NULL\n"); + return -EINVAL; + } + + core = (struct fimc_is_core *)device->interface->core; + resourcemgr = device->resourcemgr; + dvfs_ctrl = &(resourcemgr->dvfs_ctrl); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount < 0) { + err("invalid ischain refcount"); + goto exit; + } + + cpu_min_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CPU_MIN, scenario_id); + cpu_max_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CPU_MAX, scenario_id); + int_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_INT, scenario_id); + mif_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_MIF, scenario_id); + i2c_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_I2C, scenario_id); + cam_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CAM, scenario_id); + disp_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_DISP, scenario_id); + pwm_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_PWM, scenario_id); + + if ((int_qos < 0) || (mif_qos < 0) || (i2c_qos < 0) + || (cam_qos < 0) || (disp_qos < 0) || (pwm_qos < 0)) { + err("getting qos value is failed!!\n"); + return -EINVAL; + } + + if (dvfs_ctrl->cur_cpu_min_qos != cpu_min_qos) { + pm_qos_update_request(&exynos_isp_qos_cpu_min, cpu_min_qos); + dvfs_ctrl->cur_cpu_min_qos = cpu_min_qos; + } + + if (dvfs_ctrl->cur_cpu_max_qos != cpu_max_qos) { + pm_qos_update_request(&exynos_isp_qos_cpu_max, cpu_max_qos); + dvfs_ctrl->cur_cpu_max_qos = cpu_max_qos; + } + + /* check current qos */ + if (int_qos && dvfs_ctrl->cur_int_qos != int_qos) { + if (i2c_qos) { + ret = fimc_is_itf_i2c_lock(device, i2c_qos, true); + if (ret) { + err("fimc_is_itf_i2_clock fail\n"); + goto exit; + } + } + + if (pwm_qos) { + fimc_is_set_pwm(device, pwm_qos); + if (ret) { + err("fimc_is_set_pwm fail\n"); + goto exit; + } + } + + pm_qos_update_request(&exynos_isp_qos_int, int_qos); + dvfs_ctrl->cur_int_qos = int_qos; + + if (i2c_qos) { + /* i2c unlock */ + ret = fimc_is_itf_i2c_lock(device, i2c_qos, false); + if (ret) { + err("fimc_is_itf_i2c_unlock fail\n"); + goto exit; + } + } + } + + if (mif_qos && dvfs_ctrl->cur_mif_qos != mif_qos) { + pm_qos_update_request(&exynos_isp_qos_mem, mif_qos); + dvfs_ctrl->cur_mif_qos = mif_qos; + } + + if (cam_qos && dvfs_ctrl->cur_cam_qos != cam_qos) { + pm_qos_update_request(&exynos_isp_qos_cam, cam_qos); + dvfs_ctrl->cur_cam_qos = cam_qos; + } + + if (disp_qos && dvfs_ctrl->cur_disp_qos != disp_qos) { + pm_qos_update_request(&exynos_isp_qos_disp, disp_qos); + dvfs_ctrl->cur_disp_qos = disp_qos; + } + + dbg("[RSC:%d]: New QoS [INT(%d), MIF(%d), CAM(%d), DISP(%d), I2C(%d), PWM(%d) CPU(%d/%d)]\n", + device->instance, int_qos, mif_qos, + cam_qos, disp_qos, i2c_qos, pwm_qos, cpu_min_qos, cpu_max_qos); +exit: + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.h b/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.h new file mode 100644 index 000000000000..6ffd9e4b3874 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-dvfs.h @@ -0,0 +1,68 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DVFS_H +#define FIMC_IS_DVFS_H + +#include +#include +#include +#include + +#include "fimc-is-time.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-groupmgr.h" +#include "fimc-is-device-ischain.h" + +#define KEEP_FRAME_TICK_DEFAULT (5) +#define DVFS_SN_STR(__SCENARIO) #__SCENARIO +#define GET_DVFS_CHK_FUNC(__SCENARIO) check_ ## __SCENARIO +#define DECLARE_DVFS_CHK_FUNC(__SCENARIO) \ + int check_ ## __SCENARIO \ + (struct fimc_is_device_ischain *device, struct fimc_is_frame *frame, ...) + +#define SIZE_FHD (1920 * 1080) +#define SIZE_WHD (2560 * 1440) +#define SIZE_UHD (3840 * 2160) + +enum FIMC_IS_DVFS_SCENARIO_TYPE { + FIMC_IS_STATIC_SN, + FIMC_IS_DYNAMIC_SN, +}; + +struct fimc_is_dvfs_scenario { + u32 scenario_id; /* scenario_id */ + char *scenario_nm; /* string of scenario_id */ + int priority; /* priority for dynamic scenario */ + int keep_frame_tick; /* keep qos lock during specific frames when dynamic scenario */ + + /* function pointer to check a scenario */ + int (*check_func)(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame, ...); +}; + +struct fimc_is_dvfs_scenario_ctrl { + int cur_scenario_id; /* selected scenario idx */ + int cur_frame_tick; /* remained frame tick to keep qos lock in dynamic scenario */ + int scenario_cnt; /* total scenario count */ + int cur_scenario_idx; /* selected scenario idx for scenarios */ + struct fimc_is_dvfs_scenario *scenarios; +}; + +int fimc_is_dvfs_init(struct fimc_is_resourcemgr *resourcemgr); +int fimc_is_dvfs_sel_scenario(u32 type, struct fimc_is_device_ischain *device, struct fimc_is_frame *frame); +int fimc_is_get_qos(struct fimc_is_core *core, u32 type, u32 scenario_id); +int fimc_is_set_dvfs(struct fimc_is_device_ischain *device, u32 scenario_id); +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-err.h b/drivers/media/platform/exynos/fimc-is/fimc-is-err.h new file mode 100644 index 000000000000..c83edfe5d071 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-err.h @@ -0,0 +1,300 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_ERR_H +#define FIMC_IS_ERR_H + +#define IS_ERROR_VER 014 /* IS ERROR VERSION 0.14 */ + +#define IS_ERROR_SUCCESS 0 +/* General 1 ~ 100 */ +#define IS_ERROR_INVALID_COMMAND (IS_ERROR_SUCCESS+1) +#define IS_ERROR_REQUEST_FAIL (IS_ERROR_INVALID_COMMAND+1) +#define IS_ERROR_INVALID_SCENARIO (IS_ERROR_REQUEST_FAIL+1) +#define IS_ERROR_INVALID_SENSORID (IS_ERROR_INVALID_SCENARIO+1) +#define IS_ERROR_INVALID_MODE_CHANGE (IS_ERROR_INVALID_SENSORID+1) +#define IS_ERROR_INVALID_MAGIC_NUMBER (IS_ERROR_INVALID_MODE_CHANGE+1) +#define IS_ERROR_INVALID_SETFILE_HDR (IS_ERROR_INVALID_MAGIC_NUMBER+1) +#define IS_ERROR_ISP_SETFILE_VERSION_MISMATCH (IS_ERROR_INVALID_SETFILE_HDR+1) +#define IS_ERROR_ISP_SETFILE_REVISION_MISMATCH\ + (IS_ERROR_ISP_SETFILE_VERSION_MISMATCH+1) +#define IS_ERROR_BUSY (IS_ERROR_ISP_SETFILE_REVISION_MISMATCH+1) +#define IS_ERROR_SET_PARAMETER (IS_ERROR_BUSY+1) +#define IS_ERROR_INVALID_PATH (IS_ERROR_SET_PARAMETER+1) +#define IS_ERROR_OPEN_SENSOR_FAIL (IS_ERROR_INVALID_PATH+1) +#define IS_ERROR_ENTRY_MSG_THREAD_DOWN (IS_ERROR_OPEN_SENSOR_FAIL+1) +#define IS_ERROR_ISP_FRAME_END_NOT_DONE (IS_ERROR_ENTRY_MSG_THREAD_DOWN+1) +#define IS_ERROR_DRC_FRAME_END_NOT_DONE (IS_ERROR_ISP_FRAME_END_NOT_DONE+1) +#define IS_ERROR_SCALERC_FRAME_END_NOT_DONE (IS_ERROR_DRC_FRAME_END_NOT_DONE+1) +#define IS_ERROR_DIS_FRAME_END_NOT_DONE (IS_ERROR_SCALERC_FRAME_END_NOT_DONE+1) +#define IS_ERROR_TDNR_FRAME_END_NOT_DONE (IS_ERROR_DIS_FRAME_END_NOT_DONE+1) +#define IS_ERROR_SCALERP_FRAME_END_NOT_DONE (IS_ERROR_TDNR_FRAME_END_NOT_DONE+1) +#define IS_ERROR_WAIT_STREAM_OFF_NOT_DONE\ + (IS_ERROR_SCALERP_FRAME_END_NOT_DONE+1) +#define IS_ERROR_NO_MSG_IS_RECEIVED (IS_ERROR_WAIT_STREAM_OFF_NOT_DONE+1) +#define IS_ERROR_SENSOR_MSG_FAIL (IS_ERROR_NO_MSG_IS_RECEIVED+1) +#define IS_ERROR_ISP_MSG_FAIL (IS_ERROR_SENSOR_MSG_FAIL+1) +#define IS_ERROR_DRC_MSG_FAIL (IS_ERROR_ISP_MSG_FAIL+1) +#define IS_ERROR_SCALERC_MSG_FAIL (IS_ERROR_DRC_MSG_FAIL+1) +#define IS_ERROR_DIS_MSG_FAIL (IS_ERROR_ODC_MSG_FAIL+1) +#define IS_ERROR_TDNR_MSG_FAIL (IS_ERROR_DIS_MSG_FAIL+1) +#define IS_ERROR_SCALERP_MSG_FAIL (IS_ERROR_TDNR_MSG_FAIL+1) +#define IS_ERROR_LHFD_MSG_FAIL (IS_ERROR_SCALERP_MSG_FAIL+1) +#define IS_ERROR_INTERNAL_STOP (IS_ERROR_LHFD_MSG_FAIL+1) +#define IS_ERROR_UNKNOWN 1000 +#define IS_ERROR_TIME_OUT_FLAG 0x80000000 + +/* Sensor 100 ~ 200 */ +#define IS_ERROR_SENSOR_PWRDN_FAIL 100 +#define IS_ERROR_SENSOR_STREAM_ON_FAIL (IS_ERROR_SENSOR_PWRDN_FAIL+1) +#define IS_ERROR_SENSOR_STREAM_OFF_FAIL (IS_ERROR_SENSOR_STREAM_ON_FAIL+1) + +/* ISP 200 ~ 300 */ +#define IS_ERROR_ISP_PWRDN_FAIL 200 +#define IS_ERROR_ISP_MULTIPLE_INPUT (IS_ERROR_ISP_PWRDN_FAIL+1) +#define IS_ERROR_ISP_ABSENT_INPUT (IS_ERROR_ISP_MULTIPLE_INPUT+1) +#define IS_ERROR_ISP_ABSENT_OUTPUT (IS_ERROR_ISP_ABSENT_INPUT+1) +#define IS_ERROR_ISP_NONADJACENT_OUTPUT (IS_ERROR_ISP_ABSENT_OUTPUT+1) +#define IS_ERROR_ISP_FORMAT_MISMATCH (IS_ERROR_ISP_NONADJACENT_OUTPUT+1) +#define IS_ERROR_ISP_WIDTH_MISMATCH (IS_ERROR_ISP_FORMAT_MISMATCH+1) +#define IS_ERROR_ISP_HEIGHT_MISMATCH (IS_ERROR_ISP_WIDTH_MISMATCH+1) +#define IS_ERROR_ISP_BITWIDTH_MISMATCH (IS_ERROR_ISP_HEIGHT_MISMATCH+1) +#define IS_ERROR_ISP_FRAME_END_TIME_OUT (IS_ERROR_ISP_BITWIDTH_MISMATCH+1) + +/* DRC 300 ~ 400 */ +#define IS_ERROR_DRC_PWRDN_FAIL 300 +#define IS_ERROR_DRC_MULTIPLE_INPUT (IS_ERROR_DRC_PWRDN_FAIL+1) +#define IS_ERROR_DRC_ABSENT_INPUT (IS_ERROR_DRC_MULTIPLE_INPUT+1) +#define IS_ERROR_DRC_NONADJACENT_INTPUT (IS_ERROR_DRC_ABSENT_INPUT+1) +#define IS_ERROR_DRC_ABSENT_OUTPUT (IS_ERROR_DRC_NONADJACENT_INTPUT+1) +#define IS_ERROR_DRC_NONADJACENT_OUTPUT (IS_ERROR_DRC_ABSENT_OUTPUT+1) +#define IS_ERROR_DRC_FORMAT_MISMATCH (IS_ERROR_DRC_NONADJACENT_OUTPUT+1) +#define IS_ERROR_DRC_WIDTH_MISMATCH (IS_ERROR_DRC_FORMAT_MISMATCH+1) +#define IS_ERROR_DRC_HEIGHT_MISMATCH (IS_ERROR_DRC_WIDTH_MISMATCH+1) +#define IS_ERROR_DRC_BITWIDTH_MISMATCH (IS_ERROR_DRC_HEIGHT_MISMATCH+1) +#define IS_ERROR_DRC_FRAME_END_TIME_OUT (IS_ERROR_DRC_BITWIDTH_MISMATCH+1) + +/*SCALERC(400~500)*/ +#define IS_ERROR_SCALERC_PWRDN_FAIL 400 + +/*DIS(600~700)*/ +#define IS_ERROR_DIS_PWRDN_FAIL 600 + +/*TDNR(700~800) */ +#define IS_ERROR_TDNR_PWRDN_FAIL 700 + +/*SCALERP(800~900)*/ +#define IS_ERROR_SCALERP_PWRDN_FAIL 800 + +/*FD(900~1000)*/ +#define IS_ERROR_FD_PWRDN_FAIL 900 +#define IS_ERROR_FD_MULTIPLE_INPUT (IS_ERROR_FD_PWRDN_FAIL+1) +#define IS_ERROR_FD_ABSENT_INPUT (IS_ERROR_FD_MULTIPLE_INPUT+1) +#define IS_ERROR_FD_NONADJACENT_INPUT (IS_ERROR_FD_ABSENT_INPUT+1) +#define IS_ERROR_LHFD_FRAME_END_TIME_OUT \ + (IS_ERROR_FD_NONADJACENT_INPUT+1) + +/* Set parameter error enum */ +enum error { + /* Common error (0~99) */ + ERROR_COMMON_NO = 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_NO = ERROR_COMMON_NO, + ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */ + ERROR_CONTROL_BUF = 12, /* invalid buffer info */ + + ERROR_OTF_INPUT_NO = ERROR_COMMON_NO, + /* 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_NO = ERROR_COMMON_NO, + /* 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_NO = ERROR_COMMON_NO, + /* 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_NO = ERROR_COMMON_NO, + 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_NO = ERROR_COMMON_NO, + + /* SENSOR Error(100~199) */ + ERROR_SENSOR_NO = ERROR_COMMON_NO, + 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_NO = ERROR_COMMON_NO, + ERROR_ISP_AF_BUSY = 201, + ERROR_ISP_AF_INVALID_COMMAND = 202, + ERROR_ISP_AF_INVALID_MODE = 203, + ERROR_ISP_FLASH_NO = ERROR_COMMON_NO, + ERROR_ISP_AWB_NO = ERROR_COMMON_NO, + ERROR_ISP_IMAGE_EFFECT_NO = ERROR_COMMON_NO, + ERROR_ISP_IMAGE_EFFECT_INVALID = 231, + ERROR_ISP_ISO_NO = ERROR_COMMON_NO, + ERROR_ISP_ADJUST_NO = ERROR_COMMON_NO, + ERROR_ISP_METERING_NO = ERROR_COMMON_NO, + ERROR_ISP_AFC_NO = ERROR_COMMON_NO, + + /* DRC Error (300~399) */ + + /* FD Error (400~499) */ + ERROR_FD_NO = ERROR_COMMON_NO, + /* 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_NO = ERROR_COMMON_NO, + 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, + +}; + +enum ShotErrorType { + IS_SHOT_SUCCESS = 0, + /* Un-known state.(Normally under processing.) */ + IS_SHOT_UNKNOWN, + /* Bad frame. Ndone is occured at provious group. */ + IS_SHOT_BAD_FRAME, + /* Metadata is not valid. For example, sirc sdk's fd is not valid. */ + IS_SHOT_CORRUPTED_FRAME, + /* Processing of previous group is not complete. */ + IS_SHOT_EARLY_FRAME, + /* Shot is too late at OTF mode. */ + IS_SHOT_LATE_FRAME, + /* Shot is coming when group is process-stop. */ + IS_SHOT_GROUP_PROCESSSTOP, + /* Frame number is not allocated. */ + IS_SHOT_INVALID_FRAMENUMBER, + /* Overflow is occred during processing. */ + IS_SHOT_OVERFLOW, + /* Shot is time-out during processing.(Unknown reason.) */ + IS_SHOT_SAME_FRAME_COUNT, + /* Shot is time-out during processing.(Unknown reason.) */ + IS_SHOT_TIMEOUT, + /* Shot is droped at BufferEntry. */ + IS_SHOT_DROP, + IS_SHOT_3AA_FRAME_END, + IS_SHOT_IP_CLOCK_OFF, + IS_SHOT_ENDPROC_DELAY +}; + +enum REPORT_ERR_TYPE { + REPORT_ERR_CIS_ID = 0, + REPORT_ERR_CIS_CRC = 1, + REPORT_ERR_CIS_ECC = 2, + REPORT_ERR_CIS_OVERFLOW_VC0 = 3, + REPORT_ERR_CIS_LOST_FE_VC0 = 4, + REPORT_ERR_CIS_LOST_FS_VC0 = 5, + REPORT_ERR_CIS_SOT_VC0 = 6, + REPORT_ERR_CIS_SOT_VC1 = 7, + REPORT_ERR_CIS_SOT_VC2 = 8, + REPORT_ERR_CIS_SOT_VC3 = 9, + REPORT_ERR_3AA_OVERFLOW = 10, + REPORT_ERR_FLITE_D_OVERFLOW = 11, + REPORT_ERR_COMPANION_LOST_FRAME = 12, + REPORT_ERR_3A_ALGORITHM_DELAYED = 13, + REPORT_ERR_END +}; + +#define ENOBASE_IS 0x10000 +#define ENOSHOT (ENOBASE_IS + 1) /* shot error */ +#define ENOMDONE (ENOBASE_IS + 2) /* meta done error */ + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.c b/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.c new file mode 100644 index 000000000000..7f7640981609 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +#include "fimc-is-fan53555.h" + +/** + * FAN53555 Vout Tables + */ +static const struct fan53555_vout vout_tables[] = { + {0, FAN53555_VOUT_1P00, "1.00"}, /* defualt voltage */ + {1, FAN53555_VOUT_0P88, "0.88"}, + {2, FAN53555_VOUT_0P90, "0.90"}, + {3, FAN53555_VOUT_0P93, "0.93"}, + {4, FAN53555_VOUT_0P95, "0.95"}, + {5, FAN53555_VOUT_0P98, "0.98"}, + {6, FAN53555_VOUT_1P00, "1.00"}, +}; + +/** + * fan53555_get_vout_val: get i2c register value to set vout of dcdc regulator. + */ +int fan53555_get_vout_val(int sel) +{ + int i, vout = vout_tables[0].val; + + if (sel < 0) + pr_err("%s: error, invalid sel %d\n", __func__, sel); + + for (i = 0; ARRAY_SIZE(vout_tables); i++) { + if (vout_tables[i].sel == sel) { + return vout_tables[i].val; + } + } + + pr_err("%s: warning, default voltage selected. sel %d\n", __func__, sel); + + return vout; +} + +/** + * fan53555_get_vout_name: get voltage name of vout as string. + */ + const char *fan53555_get_vout_str(int sel) +{ + const char *vout = vout_tables[0].vout; + int i; + + if (sel < 0) + pr_err("%s: error, invalid sel %d\n", __func__, sel); + + for (i = 0; ARRAY_SIZE(vout_tables); i++) { + if (vout_tables[i].sel == sel) { + return vout_tables[i].vout; + } + } + + pr_err("%s: warning, default voltage selected. sel %d\n", __func__, sel); + + return vout; +} + +int fan53555_enable_vsel0(struct i2c_client *client, int on_off) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if(ret < 0){ + pr_err("%s: read error = %d , try again", __func__, ret); + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if (ret < 0) + pr_err("%s: read 2nd error = %d", __func__, ret); + } + + ret &= (~VSEL0_BUCK_EN0); + ret |= (on_off << VSEL0_BUCK_EN0_SHIFT); + + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, (BYTE)ret); + if (ret < 0){ + pr_err("%s: write error = %d , try again", __func__, ret); + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, (BYTE)ret); + if (ret < 0) + pr_err("%s: write 2nd error = %d", __func__, ret); + } + return ret; +} + +/** + * fan53555_set_vsel0_vout: set dcdc vout with i2c register value. + */ +int fan53555_set_vsel0_vout(struct i2c_client *client, int vout) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if(ret < 0){ + pr_err("%s: read error = %d , try again", __func__, ret); + ret = i2c_smbus_read_byte_data(client, REG_VSEL0); + if (ret < 0) + pr_err("%s: read 2nd error = %d", __func__, ret); + } + + ret &= (~VSEL0_NSEL0); + ret |= vout; + + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, (BYTE)ret); + if (ret < 0){ + pr_err("%s: write error = %d , try again", __func__, ret); + ret = i2c_smbus_write_byte_data(client, REG_VSEL0, (BYTE)ret); + if (ret < 0) + pr_err("%s: write 2nd error = %d", __func__, ret); + } + return ret; +} + + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.h b/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.h new file mode 100644 index 000000000000..24e5f915ed30 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-fan53555.h @@ -0,0 +1,110 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "fimc-is-core.h" + +typedef unsigned char BYTE; + +#define REG_VSEL0 0x00 +#define VSEL0_NSEL0 0x3f +#define VSEL0_BUCK_EN0_SHIFT 7 +#define VSEL0_BUCK_EN0 (0x01 << VSEL0_BUCK_EN0_SHIFT) +#define VSEL0_INIT_VAL VSEL0_BUCK_EN0 | FAN53555_VOUT_1P00 + +// FAN53555UC08X : Vout = 0.60V + NSELx * 10mV +enum{ + FAN53555_VOUT_0P60 = 0, + FAN53555_VOUT_0P61, + FAN53555_VOUT_0P62, + FAN53555_VOUT_0P63, + FAN53555_VOUT_0P64, + FAN53555_VOUT_0P65, + FAN53555_VOUT_0P66, + FAN53555_VOUT_0P67, + FAN53555_VOUT_0P68, + FAN53555_VOUT_0P69, + + FAN53555_VOUT_0P70 = 10, + FAN53555_VOUT_0P71, + FAN53555_VOUT_0P72, + FAN53555_VOUT_0P73, + FAN53555_VOUT_0P74, + FAN53555_VOUT_0P75, + FAN53555_VOUT_0P76, + FAN53555_VOUT_0P77, + FAN53555_VOUT_0P78, + FAN53555_VOUT_0P79, + + FAN53555_VOUT_0P80 = 20, + FAN53555_VOUT_0P81, + FAN53555_VOUT_0P82, + FAN53555_VOUT_0P83, + FAN53555_VOUT_0P84, + FAN53555_VOUT_0P85, + FAN53555_VOUT_0P86, + FAN53555_VOUT_0P87, + FAN53555_VOUT_0P88, + FAN53555_VOUT_0P89, + + FAN53555_VOUT_0P90 = 30, + FAN53555_VOUT_0P91, + FAN53555_VOUT_0P92, + FAN53555_VOUT_0P93, + FAN53555_VOUT_0P94, + FAN53555_VOUT_0P95, + FAN53555_VOUT_0P96, + FAN53555_VOUT_0P97, + FAN53555_VOUT_0P98, + FAN53555_VOUT_0P99, + + FAN53555_VOUT_1P00 = 40, + FAN53555_VOUT_1P01, + FAN53555_VOUT_1P02, // VSEL0 default, 08X + FAN53555_VOUT_1P03, + FAN53555_VOUT_1P04, + FAN53555_VOUT_1P05, + FAN53555_VOUT_1P06, + FAN53555_VOUT_1P07, + FAN53555_VOUT_1P08, + FAN53555_VOUT_1P09, + + FAN53555_VOUT_1P10 = 50, + FAN53555_VOUT_1P11, + FAN53555_VOUT_1P12, + FAN53555_VOUT_1P13, + FAN53555_VOUT_1P14, + FAN53555_VOUT_1P15, // VSEL1 default, 08X + FAN53555_VOUT_1P16, + FAN53555_VOUT_1P17, + FAN53555_VOUT_1P18, + FAN53555_VOUT_1P19, + + FAN53555_VOUT_1P20 = 60, + FAN53555_VOUT_1P21, + FAN53555_VOUT_1P22, + FAN53555_VOUT_1P23, +}; + +struct fan53555_vout { + int sel; /* selector, unique value for vout entry and indepedant to dcdc vendor */ + int val; /* dcdc-specific value for vout register */ + char vout[7]; /* voltage level string */ +}; + +int fan53555_get_vout_val(int sel); +const char *fan53555_get_vout_str(int sel); +int fan53555_enable_vsel0(struct i2c_client *client, int on_off); +int fan53555_set_vsel0_vout(struct i2c_client *client, int vout); + + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.c b/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.c new file mode 100644 index 000000000000..ccb74e5828c8 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.c @@ -0,0 +1,643 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +#include "fimc-is-device-sensor.h" + +int fimc_is_frame_s_free_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (item) { + item->state = FIMC_IS_FRAME_STATE_FREE; + + list_add_tail(&item->list, &this->frame_free_head); + this->frame_fre_cnt++; + +#ifdef TRACE_FRAME + fimc_is_frame_print_free_list(this); +#endif + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + + +int fimc_is_frame_g_free_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + int ret = 0; + + if (item) { + if (this->frame_fre_cnt) { + *item = container_of(this->frame_free_head.next, + struct fimc_is_frame, list); + list_del(&(*item)->list); + this->frame_fre_cnt--; + + (*item)->state = FIMC_IS_FRAME_STATE_INVALID; + } else { + *item = NULL; + } + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +void fimc_is_frame_free_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + if (this->frame_fre_cnt) + *item = container_of(this->frame_free_head.next, + struct fimc_is_frame, list); + else + *item = NULL; +} + +void fimc_is_frame_print_free_list(struct fimc_is_framemgr *this) +{ + struct list_head *temp; + struct fimc_is_frame *shot; + + if (!(TRACE_ID & this->id)) + return; + + printk(KERN_ERR "[FRM] fre(%d, %d) :", this->id, this->frame_fre_cnt); + + list_for_each(temp, &this->frame_free_head) { + shot = list_entry(temp, struct fimc_is_frame, list); + printk(KERN_CONT "%d->", shot->index); + } + + printk(KERN_CONT "X\n"); +} + +int fimc_is_frame_s_request_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (item) { + list_add_tail(&item->list, &this->frame_request_head); + this->frame_req_cnt++; + + item->state = FIMC_IS_FRAME_STATE_REQUEST; + +#ifdef TRACE_FRAME + fimc_is_frame_print_request_list(this); +#endif + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +int fimc_is_frame_g_request_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + int ret = 0; + + if (item) { + if (this->frame_req_cnt) { + *item = container_of(this->frame_request_head.next, + struct fimc_is_frame, list); + list_del(&(*item)->list); + this->frame_req_cnt--; + + (*item)->state = FIMC_IS_FRAME_STATE_INVALID; + } else { + *item = NULL; + } + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +void fimc_is_frame_request_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + if (this->frame_req_cnt) + *item = container_of(this->frame_request_head.next, + struct fimc_is_frame, list); + else + *item = NULL; +} + +void fimc_is_frame_print_request_list(struct fimc_is_framemgr *this) +{ + struct list_head *temp; + struct fimc_is_frame *shot; + + if (!(TRACE_ID & this->id)) + return; + + printk(KERN_ERR "[FRM] req(%d, %d) :", + this->id, this->frame_req_cnt); + + list_for_each(temp, &this->frame_request_head) { + shot = list_entry(temp, struct fimc_is_frame, list); + printk(KERN_CONT "%d->", shot->index); + } + + printk(KERN_CONT "X\n"); +} + +int fimc_is_frame_s_process_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (item) { + list_add_tail(&item->list, &this->frame_process_head); + this->frame_pro_cnt++; + + item->state = FIMC_IS_FRAME_STATE_PROCESS; + +#ifdef TRACE_FRAME + fimc_is_frame_print_process_list(this); +#endif + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +int fimc_is_frame_g_process_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + int ret = 0; + + if (item) { + if (this->frame_pro_cnt) { + *item = container_of(this->frame_process_head.next, + struct fimc_is_frame, list); + list_del(&(*item)->list); + this->frame_pro_cnt--; + + (*item)->state = FIMC_IS_FRAME_STATE_INVALID; + } else { + *item = NULL; + } + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +void fimc_is_frame_process_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + if (this->frame_pro_cnt) + *item = container_of(this->frame_process_head.next, + struct fimc_is_frame, list); + else + *item = NULL; +} + +void fimc_is_frame_print_process_list(struct fimc_is_framemgr *this) +{ + struct list_head *temp; + struct fimc_is_frame *shot; + + if (!(TRACE_ID & this->id)) + return; + + printk(KERN_ERR "[FRM] pro(%d, %d) :", + this->id, this->frame_pro_cnt); + + list_for_each(temp, &this->frame_process_head) { + shot = list_entry(temp, struct fimc_is_frame, list); + printk(KERN_CONT "%d(%d)->", shot->index, shot->fcount); + } + + printk(KERN_CONT "X\n"); +} + +int fimc_is_frame_s_complete_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (item) { + list_add_tail(&item->list, &this->frame_complete_head); + this->frame_com_cnt++; + + item->state = FIMC_IS_FRAME_STATE_COMPLETE; + +#ifdef TRACE_FRAME + fimc_is_frame_print_complete_list(this); +#endif + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + + +int fimc_is_frame_g_complete_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + int ret = 0; + + if (item) { + if (this->frame_com_cnt) { + *item = container_of(this->frame_complete_head.next, + struct fimc_is_frame, list); + list_del(&(*item)->list); + this->frame_com_cnt--; + + (*item)->state = FIMC_IS_FRAME_STATE_INVALID; + } else { + *item = NULL; + } + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +void fimc_is_frame_complete_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **item) +{ + if (this->frame_com_cnt) + *item = container_of(this->frame_complete_head.next, + struct fimc_is_frame, list); + else + *item = NULL; +} + +void fimc_is_frame_print_complete_list(struct fimc_is_framemgr *this) +{ + struct list_head *temp; + struct fimc_is_frame *shot; + + if (!(TRACE_ID & this->id)) + return; + + printk(KERN_ERR "[FRM] com(%d, %d) :", + this->id, this->frame_com_cnt); + + list_for_each(temp, &this->frame_complete_head) { + shot = list_entry(temp, struct fimc_is_frame, list); + printk(KERN_CONT "%d->", shot->index); + } + + printk(KERN_CONT "X\n"); +} + +int fimc_is_frame_trans_fre_to_req(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_fre_cnt) { + err("shot free count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_fre_cnt--; + + fimc_is_frame_s_request_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_req_to_pro(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_req_cnt) { + err("shot request count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_req_cnt--; + + fimc_is_frame_s_process_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_req_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_req_cnt) { + err("shot request count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_req_cnt--; + + fimc_is_frame_s_complete_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_req_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_req_cnt) { + err("shot request count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_req_cnt--; + + fimc_is_frame_s_free_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_pro_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_pro_cnt) { + err("shot process count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_pro_cnt--; + + fimc_is_frame_s_complete_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_pro_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_pro_cnt) { + err("shot process count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_pro_cnt--; + + fimc_is_frame_s_free_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_fre_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_fre_cnt) { + err("shot free count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_fre_cnt--; + + fimc_is_frame_s_complete_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_trans_com_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *item) +{ + int ret = 0; + + if (!this->frame_com_cnt) { + err("shot complete count is zero\n"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + this->frame_com_cnt--; + + fimc_is_frame_s_free_shot(this, item); + +exit: + return ret; +} + +int fimc_is_frame_swap_process_head(struct fimc_is_framemgr *this) +{ + int ret = 0; + struct fimc_is_frame *head; + struct fimc_is_frame *next; + + if (!this->frame_pro_cnt) { + err("shot process count is zero\n"); + ret = -EFAULT; + goto exit; + } + + fimc_is_frame_process_head(this, &head); + + list_del(&head->list); + this->frame_pro_cnt--; + + /* list swap operation */ + fimc_is_frame_process_head(this, &next); + if (next) { + list_del(&next->list); + this->frame_pro_cnt--; + fimc_is_frame_s_process_shot(this, next); + head->has_fcount = false; + } + + fimc_is_frame_s_process_shot(this, head); + +exit: + return ret; +} + +int fimc_is_frame_probe(struct fimc_is_framemgr *this, u32 id) +{ + int ret = 0; + + this->id = id; + spin_lock_init(&this->slock); + + return ret; +} + +int fimc_is_frame_open(struct fimc_is_framemgr *this, u32 buffers) +{ + int ret = 0; + u32 i, j; + + INIT_LIST_HEAD(&this->frame_free_head); + INIT_LIST_HEAD(&this->frame_request_head); + INIT_LIST_HEAD(&this->frame_process_head); + INIT_LIST_HEAD(&this->frame_complete_head); + + this->frame_cnt = buffers; + this->frame_fre_cnt = 0; + this->frame_req_cnt = 0; + this->frame_pro_cnt = 0; + this->frame_com_cnt = 0; + + for (i = 0; i < buffers; ++i) { + clear_bit(FRAME_INI_MEM, &this->frame[i].memory); + clear_bit(FRAME_MAP_MEM, &this->frame[i].memory); + this->frame[i].work_data1 = NULL; + this->frame[i].work_data2 = NULL; + this->frame[i].index = i; + this->frame[i].fcount = 0; + this->frame[i].rcount = 0; + this->frame[i].req_flag = 0; + this->frame[i].out_flag = 0; + + this->frame[i].vb = NULL; + this->frame[i].shot = NULL; + this->frame[i].shot_ext = NULL; + this->frame[i].shot_size = 0; + + this->frame[i].stream = NULL; + this->frame[i].stream_size = 0; + + this->frame[i].planes = 0; + for (j = 0; j < FIMC_IS_MAX_PLANES; ++j) { + this->frame[i].kvaddr_buffer[j] = 0; + this->frame[i].dvaddr_buffer[j] = 0; + } + + this->frame[i].kvaddr_shot = 0; + this->frame[i].dvaddr_shot = 0; + this->frame[i].has_fcount = false; + fimc_is_frame_s_free_shot(this, &this->frame[i]); + } + + return ret; +} + +int fimc_is_frame_close(struct fimc_is_framemgr *this) +{ + int ret = 0; + u32 buffers; + u32 i, j; + + buffers = this->frame_cnt; + + for (i = 0; i < buffers; ++i) { + clear_bit(FRAME_INI_MEM, &this->frame[i].memory); + clear_bit(FRAME_MAP_MEM, &this->frame[i].memory); + this->frame[i].index = i; + this->frame[i].fcount = 0; + this->frame[i].rcount = 0; + this->frame[i].req_flag = 0; + + this->frame[i].vb = NULL; + this->frame[i].shot = NULL; + this->frame[i].shot_ext = NULL; + this->frame[i].shot_size = 0; + + this->frame[i].stream = NULL; + this->frame[i].stream_size = 0; + + this->frame[i].planes = 0; + for (j = 0; j < FIMC_IS_MAX_PLANES; ++j) { + this->frame[i].kvaddr_buffer[j] = 0; + this->frame[i].dvaddr_buffer[j] = 0; + } + + this->frame[i].kvaddr_shot = 0; + this->frame[i].dvaddr_shot = 0; + this->frame[i].has_fcount = false; + } + + return ret; +} + +void fimc_is_frame_print_all(struct fimc_is_framemgr *this) +{ + fimc_is_frame_print_free_list(this); + fimc_is_frame_print_request_list(this); + fimc_is_frame_print_process_list(this); + fimc_is_frame_print_complete_list(this); +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.h b/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.h new file mode 100644 index 000000000000..7b373cab3f2f --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-framemgr.h @@ -0,0 +1,238 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#ifndef FIMC_IS_FRAME_MGR_H +#define FIMC_IS_FRAME_MGR_H + +#define FIMC_IS_MAX_BUFS VIDEO_MAX_FRAME +#define FIMC_IS_MAX_PLANES VIDEO_MAX_PLANES + +#define FRAMEMGR_ID_INVALID 0x000000 +#define FRAMEMGR_ID_SENSOR 0x000100 +#define FRAMEMGR_ID_3AA_GRP 0x000200 +#define FRAMEMGR_ID_3AAC 0x000400 +#define FRAMEMGR_ID_3AAP 0x000800 +#define FRAMEMGR_ID_ISP_GRP 0x001000 +#define FRAMEMGR_ID_SCC 0x002000 +#define FRAMEMGR_ID_DIS 0x004000 +#define FRAMEMGR_ID_DIS_GRP 0x008000 +#define FRAMEMGR_ID_SCP 0x010000 +#define FRAMEMGR_ID_SHOT (FRAMEMGR_ID_SENSOR | FRAMEMGR_ID_3AA_GRP | \ + FRAMEMGR_ID_ISP_GRP | FRAMEMGR_ID_DIS_GRP) +#define FRAMEMGR_ID_STREAM (FRAMEMGR_ID_3AAC | FRAMEMGR_ID_3AAP | \ + FRAMEMGR_ID_SCC | FRAMEMGR_ID_DIS | \ + FRAMEMGR_ID_SCP) +/* #define TRACE_FRAME */ +#define TRACE_ID (FRAMEMGR_ID_SHOT | FRAMEMGR_ID_STREAM) + +#define FRAMEMGR_MAX_REQUEST VIDEO_MAX_FRAME + +/*flite frame start tasklet*/ +#define FMGR_IDX_0 (0x10) +/*flite frame end tasklet*/ +#define FMGR_IDX_1 (0x20) +/*sensor queue*/ +#define FMGR_IDX_2 (0x30) +/*sensor dequeue*/ +#define FMGR_IDX_3 (0x40) +/*dev framedone*/ +#define FMGR_IDX_4 (0x50) +/*scc framedone*/ +#define FMGR_IDX_5 (0x60) +/*scp framedone*/ +#define FMGR_IDX_6 (0x70) +/*isp framedone*/ +#define FMGR_IDX_7 (0x80) +/*scc callback*/ +#define FMGR_IDX_8 (0x90) +/*scp callback*/ +#define FMGR_IDX_9 (0xA0) +/*3a0c callback*/ +#define FMGR_IDX_10 (0xB0) +/*3a1c callback*/ +#define FMGR_IDX_11 (0xC0) + +#define framemgr_e_barrier_irqs(this, index, flag) \ + spin_lock_irqsave(&this->slock, flag) +#define framemgr_x_barrier_irqr(this, index, flag) \ + spin_unlock_irqrestore(&this->slock, flag) +#define framemgr_e_barrier_irq(this, index) \ + spin_lock_irq(&this->slock) +#define framemgr_x_barrier_irq(this, index) \ + spin_unlock_irq(&this->slock) +#define framemgr_e_barrier(this, index) \ + spin_lock(&this->slock) +#define framemgr_x_barrier(this, index) \ + spin_unlock(&this->slock) + +enum fimc_is_frame_shot_state { + FIMC_IS_FRAME_STATE_FREE, + FIMC_IS_FRAME_STATE_REQUEST, + FIMC_IS_FRAME_STATE_PROCESS, + FIMC_IS_FRAME_STATE_COMPLETE, + FIMC_IS_FRAME_STATE_INVALID +}; + +enum fimc_is_frame_reqeust { + /* SCC, SCP frame done, + ISP meta done */ + REQ_FRAME, + /* 3AA shot done */ + REQ_3AA_SHOT, + /* ISP shot done */ + REQ_ISP_SHOT, + /* DIS shot done */ + REQ_DIS_SHOT +}; + +enum fimc_is_frame_output { + /* 3AAC frame done */ + OUT_3AAC_FRAME, + /* 3AAP frame done */ + OUT_3AAP_FRAME, + /* SCC frame done */ + OUT_SCC_FRAME, + /* DIS frame done */ + OUT_DIS_FRAME, + /* SCP frame done */ + OUT_SCP_FRAME +}; + +enum fimc_is_frame_mem { + /* initialized memory */ + FRAME_INI_MEM, + /* mapped memory */ + FRAME_MAP_MEM +}; + +struct fimc_is_frame { + struct list_head list; + struct kthread_work work; + void *work_data1; + void *work_data2; + + /* group leader use */ + struct camera2_shot *shot; + struct camera2_shot_ext *shot_ext; + u32 kvaddr_shot; + u32 dvaddr_shot; + u32 cookie_shot; + u32 shot_size; + + /* stream use */ + struct camera2_stream *stream; + u32 stream_size; + + /* common use */ + u32 planes; + u32 kvaddr_buffer[FIMC_IS_MAX_PLANES]; + u32 dvaddr_buffer[FIMC_IS_MAX_PLANES]; + + /* internal use */ + unsigned long memory; + u32 state; + u32 fcount; + u32 rcount; + u32 index; + unsigned long req_flag; + unsigned long out_flag; + struct vb2_buffer *vb; + + /* for overwriting framecount check */ + bool has_fcount; + + /* time measure externally */ + struct timeval *tzone; + /* time measure internally */ + struct timeval time_queued; + struct timeval time_shot; + struct timeval time_shotdone; + struct timeval time_dequeued; +}; + +struct fimc_is_framemgr { + struct fimc_is_frame frame[FRAMEMGR_MAX_REQUEST]; + + struct list_head frame_free_head; + struct list_head frame_request_head; + struct list_head frame_process_head; + struct list_head frame_complete_head; + + spinlock_t slock; + + u32 frame_cnt; + u32 frame_fre_cnt; + u32 frame_req_cnt; + u32 frame_pro_cnt; + u32 frame_com_cnt; + + u32 id; +}; + +int fimc_is_frame_probe(struct fimc_is_framemgr *this, u32 id); +int fimc_is_frame_open(struct fimc_is_framemgr *this, u32 buffers); +int fimc_is_frame_close(struct fimc_is_framemgr *this); +void fimc_is_frame_print_all(struct fimc_is_framemgr *this); + +int fimc_is_frame_s_free_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_g_free_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_free_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_print_free_list(struct fimc_is_framemgr *this); + +int fimc_is_frame_s_request_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_g_request_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_request_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_print_request_list(struct fimc_is_framemgr *this); + +int fimc_is_frame_s_process_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_g_process_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_process_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_print_process_list(struct fimc_is_framemgr *this); + +int fimc_is_frame_s_complete_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_g_complete_shot(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_complete_head(struct fimc_is_framemgr *this, + struct fimc_is_frame **frame); +void fimc_is_frame_print_complete_list(struct fimc_is_framemgr *this); + +int fimc_is_frame_trans_fre_to_req(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_fre_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_req_to_pro(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_req_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_req_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *item); +int fimc_is_frame_trans_pro_to_com(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_pro_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); +int fimc_is_frame_trans_com_to_fre(struct fimc_is_framemgr *this, + struct fimc_is_frame *frame); + +int fimc_is_frame_swap_process_head(struct fimc_is_framemgr *this); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.c b/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.c new file mode 100644 index 000000000000..722324316faa --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.c @@ -0,0 +1,1932 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-framemgr.h" +#include "fimc-is-groupmgr.h" +#include "fimc-is-cmd.h" +#include "fimc-is-dvfs.h" + +/* sysfs variable for debug */ +extern struct fimc_is_sysfs_debug sysfs_debug; + +static void fimc_is_gframe_s_info(struct fimc_is_group_frame *item, + u32 group_id, struct fimc_is_frame *frame) +{ + BUG_ON(!item); + BUG_ON(!frame); + BUG_ON(!frame->shot_ext); + + memcpy(&item->group_cfg[group_id], &frame->shot_ext->node_group, + sizeof(struct camera2_node_group)); +} + +static void fimc_is_gframe_free_head(struct fimc_is_group_framemgr *framemgr, + struct fimc_is_group_frame **item) +{ + if (framemgr->frame_free_cnt) + *item = container_of(framemgr->frame_free_head.next, + struct fimc_is_group_frame, list); + else + *item = NULL; +} + +static void fimc_is_gframe_s_free(struct fimc_is_group_framemgr *framemgr, + struct fimc_is_group_frame *item) +{ + BUG_ON(!framemgr); + BUG_ON(!item); + + list_add_tail(&item->list, &framemgr->frame_free_head); + framemgr->frame_free_cnt++; +} + +static void fimc_is_gframe_print_free(struct fimc_is_group_framemgr *framemgr) +{ + struct list_head *temp; + struct fimc_is_group_frame *gframe; + + BUG_ON(!framemgr); + + printk(KERN_ERR "[GFRM] fre(%d) :", framemgr->frame_free_cnt); + + list_for_each(temp, &framemgr->frame_free_head) { + gframe = list_entry(temp, struct fimc_is_group_frame, list); + printk(KERN_CONT "%d->", gframe->fcount); + } + + printk(KERN_CONT "X\n"); +} + +static void fimc_is_gframe_group_head(struct fimc_is_group *group, + struct fimc_is_group_frame **item) +{ + if (group->frame_group_cnt) + *item = container_of(group->frame_group_head.next, + struct fimc_is_group_frame, list); + else + *item = NULL; +} + +static void fimc_is_gframe_s_group(struct fimc_is_group *group, + struct fimc_is_group_frame *item) +{ + BUG_ON(!group); + BUG_ON(!item); + + list_add_tail(&item->list, &group->frame_group_head); + group->frame_group_cnt++; +} + +static void fimc_is_gframe_print_group(struct fimc_is_group *group) +{ + struct list_head *temp; + struct fimc_is_group_frame *gframe; + + BUG_ON(!group); + + printk(KERN_ERR "[GFRM] req(%d, %d) :", group->id, group->frame_group_cnt); + + list_for_each(temp, &group->frame_group_head) { + gframe = list_entry(temp, struct fimc_is_group_frame, list); + printk(KERN_CONT "%d->", gframe->fcount); + } + + printk(KERN_CONT "X\n"); +} + +static int fimc_is_gframe_trans_fre_to_grp(struct fimc_is_group_framemgr *prev, + struct fimc_is_group *next, + struct fimc_is_group_frame *item) +{ + int ret = 0; + + BUG_ON(!prev); + BUG_ON(!next); + BUG_ON(!item); + + if (!prev->frame_free_cnt) { + err("frame_free_cnt is zero"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + prev->frame_free_cnt--; + + fimc_is_gframe_s_group(next, item); + +exit: + return ret; +} + +static int fimc_is_gframe_trans_grp_to_grp(struct fimc_is_group *prev, + struct fimc_is_group *next, + struct fimc_is_group_frame *item) +{ + int ret = 0; + u32 *input_crop, *output_crop; + + BUG_ON(!prev); + BUG_ON(!next); + BUG_ON(!item); + + if (!prev->frame_group_cnt) { + err("frame_group_cnt is zero"); + ret = -EFAULT; + goto p_err; + } + + list_del(&item->list); + prev->frame_group_cnt--; + + fimc_is_gframe_s_group(next, item); + + if (item->group_cfg[prev->id].capture[0].vid == next->source_vid) { + output_crop = item->group_cfg[prev->id].capture[0].output.cropRegion; + input_crop = item->group_cfg[next->id].leader.input.cropRegion; + } else if (item->group_cfg[prev->id].capture[1].vid == next->source_vid) { + output_crop = item->group_cfg[prev->id].capture[1].output.cropRegion; + input_crop = item->group_cfg[next->id].leader.input.cropRegion; + } else { + merr("vid(%d) is invalid", prev, next->source_vid); + ret = -EINVAL; + goto p_err; + } + + if ((output_crop[0] != input_crop[0]) || (output_crop[1] != input_crop[1]) || + (output_crop[2] != input_crop[2]) || (output_crop[3] != input_crop[3])) { + mwarn("GRP%d & GRP%d is incoincidence((%d,%d,%d,%d) != (%d,%d,%d,%d))", + prev, prev->id, next->id, + output_crop[0], output_crop[1], output_crop[2], output_crop[3], + input_crop[0], input_crop[1], input_crop[2], input_crop[3]); + input_crop[0] = output_crop[0]; + input_crop[1] = output_crop[1]; + input_crop[2] = output_crop[2]; + input_crop[3] = output_crop[3]; + } + +p_err: + return ret; +} + +static int fimc_is_gframe_trans_grp_to_fre(struct fimc_is_group *prev, + struct fimc_is_group_framemgr *next, + struct fimc_is_group_frame *item) +{ + int ret = 0; + + BUG_ON(!prev); + BUG_ON(!next); + BUG_ON(!item); + + if (!prev->frame_group_cnt) { + err("frame_group_cnt is zero"); + ret = -EFAULT; + goto exit; + } + + list_del(&item->list); + prev->frame_group_cnt--; + + fimc_is_gframe_s_free(next, item); + +exit: + return ret; +} + +int fimc_is_gframe_cancel(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 target_fcount) +{ + int ret = -EINVAL; + struct fimc_is_group_framemgr *gframemgr; + struct fimc_is_group_frame *gframe, *temp; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + + gframemgr = &groupmgr->framemgr[group->instance]; + + spin_lock_irq(&gframemgr->frame_slock); + + list_for_each_entry_safe(gframe, temp, &group->frame_group_head, list) { + if (gframe->fcount == target_fcount) { + list_del(&gframe->list); + group->frame_group_cnt--; + mwarn("gframe%d is cancelled", group, target_fcount); + fimc_is_gframe_s_free(gframemgr, gframe); + ret = 0; + break; + } + } + + spin_unlock_irq(&gframemgr->frame_slock); + + return ret; +} + +void * fimc_is_gframe_rewind(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 target_fcount) +{ + struct fimc_is_group_framemgr *gframemgr; + struct fimc_is_group_frame *gframe, *temp; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + + gframemgr = &groupmgr->framemgr[group->instance]; + + list_for_each_entry_safe(gframe, temp, &group->frame_group_head, list) { + if (gframe->fcount == target_fcount) + break; + + if (gframe->fcount > target_fcount) { + mwarn("target fcount is invalid(%d > %d)", group, + gframe->fcount, target_fcount); + gframe = NULL; + break; + } + + list_del(&gframe->list); + group->frame_group_cnt--; + mwarn("gframe%d is cancelled(count : %d)", group, + gframe->fcount, group->frame_group_cnt); + fimc_is_gframe_s_free(gframemgr, gframe); + } + + if (!group->frame_group_cnt) { + merr("gframe%d can't be found", group, target_fcount); + gframe = NULL; + } + + return gframe; +} + +int fimc_is_gframe_flush(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group) +{ + int ret = 0; + struct fimc_is_group_framemgr *gframemgr; + struct fimc_is_group_frame *gframe, *temp; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + + gframemgr = &groupmgr->framemgr[group->instance]; + + spin_lock_irq(&gframemgr->frame_slock); + + list_for_each_entry_safe(gframe, temp, &group->frame_group_head, list) { + list_del(&gframe->list); + group->frame_group_cnt--; + mwarn("gframe%d is flushed(count : %d)", group, + gframe->fcount, group->frame_group_cnt); + fimc_is_gframe_s_free(gframemgr, gframe); + } + + spin_unlock_irq(&gframemgr->frame_slock); + + return ret; +} + +static void fimc_is_group_3a0_cancel(struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct fimc_is_video_ctx *vctx, + u32 instance) +{ + BUG_ON(!vctx); + BUG_ON(!framemgr); + BUG_ON(!frame); + BUG_ON(!queue); + + pr_err("[3A0:D:%d:%d] GRP0 CANCEL(%d, %d)\n", instance, + V4L2_TYPE_IS_OUTPUT(queue->vbq->type), + frame->fcount, frame->index); + + fimc_is_frame_trans_req_to_com(framemgr, frame); + queue_done(vctx, queue, frame->index, VB2_BUF_STATE_ERROR); +} + +static void fimc_is_group_3a1_cancel(struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_queue *queue, + struct fimc_is_video_ctx *vctx, + u32 instance) +{ + BUG_ON(!vctx); + BUG_ON(!framemgr); + BUG_ON(!frame); + BUG_ON(!queue); + + pr_err("[3A1:D:%d:%d] GRP1 CANCEL(%d, %d)\n", instance, + V4L2_TYPE_IS_OUTPUT(queue->vbq->type), + frame->fcount, frame->index); + + fimc_is_frame_trans_req_to_com(framemgr, frame); + queue_done(vctx, queue, frame->index, VB2_BUF_STATE_ERROR); +} + +static void fimc_is_group_isp_cancel(struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_video_ctx *vctx, + u32 instance) +{ + struct fimc_is_queue *queue; + + BUG_ON(!framemgr); + BUG_ON(!frame); + + pr_err("[ISP:D:%d] GRP2 CANCEL(%d, %d)\n", instance, + frame->fcount, frame->index); + + queue = GET_SRC_QUEUE(vctx); + + fimc_is_frame_trans_req_to_com(framemgr, frame); + queue_done(vctx, queue, frame->index, VB2_BUF_STATE_ERROR); +} + +static void fimc_is_group_dis_cancel(struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_video_ctx *vctx, + u32 instance) +{ + struct fimc_is_queue *queue; + + BUG_ON(!framemgr); + BUG_ON(!frame); + + pr_err("[DIS:D:%d] GRP3 CANCEL(%d, %d)\n", instance, + frame->fcount, frame->index); + + queue = GET_SRC_QUEUE(vctx); + + fimc_is_frame_trans_req_to_com(framemgr, frame); + queue_done(vctx, queue, frame->index, VB2_BUF_STATE_ERROR); +} + +static void fimc_is_group_cancel(struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame) +{ + unsigned long flags; + struct fimc_is_video_ctx *vctx; + struct fimc_is_framemgr *ldr_framemgr; + /* for M2M device */ + struct fimc_is_framemgr *sub_framemgr = NULL; + struct fimc_is_queue *ldr_queue, *sub_queue; + struct fimc_is_frame *sub_frame; + + BUG_ON(!group); + BUG_ON(!ldr_frame); + + vctx = group->leader.vctx; + if (!vctx) { + merr("vctx is NULL, critical error", group); + return; + } + + ldr_framemgr = GET_SRC_FRAMEMGR(vctx); + if (!ldr_framemgr) { + merr("ldr_framemgr is NULL, critical error", group); + return; + } + + framemgr_e_barrier_irqs(ldr_framemgr, 0, flags); + + switch (group->id) { + case GROUP_ID_3A0: + ldr_queue = GET_SRC_QUEUE(vctx); + fimc_is_group_3a0_cancel(ldr_framemgr, ldr_frame, + ldr_queue, vctx, group->instance); + /* for M2M device */ + sub_framemgr = GET_DST_FRAMEMGR(vctx); + break; + case GROUP_ID_3A1: + ldr_queue = GET_SRC_QUEUE(vctx); + fimc_is_group_3a1_cancel(ldr_framemgr, ldr_frame, + ldr_queue, vctx, group->instance); + /* for M2M device */ + sub_framemgr = GET_DST_FRAMEMGR(vctx); + break; + case GROUP_ID_ISP: + fimc_is_group_isp_cancel(ldr_framemgr, ldr_frame, + vctx, group->instance); + break; + case GROUP_ID_DIS: + fimc_is_group_dis_cancel(ldr_framemgr, ldr_frame, + vctx, group->instance); + break; + default: + err("unresolved group id %d", group->id); + break; + } + + framemgr_x_barrier_irqr(ldr_framemgr, 0, flags); + + if (sub_framemgr) { + framemgr_e_barrier_irqs(sub_framemgr, 0, flags); + + switch (group->id) { + case GROUP_ID_3A0: + sub_queue = GET_DST_QUEUE(vctx); + fimc_is_frame_request_head(sub_framemgr, &sub_frame); + if (sub_frame) + fimc_is_group_3a0_cancel(sub_framemgr, sub_frame, + sub_queue, vctx, group->instance); + break; + case GROUP_ID_3A1: + sub_queue = GET_DST_QUEUE(vctx); + fimc_is_frame_request_head(sub_framemgr, &sub_frame); + if (sub_frame) + fimc_is_group_3a1_cancel(sub_framemgr, sub_frame, + sub_queue, vctx, group->instance); + break; + default: + err("unresolved group id %d", group->id); + break; + } + + framemgr_x_barrier_irqr(sub_framemgr, 0, flags); + } +} +#ifdef CONFIG_USE_VENDER_FEATURE +/* Flash Mode Control */ +#ifdef CONFIG_LEDS_LM3560 +extern int lm3560_reg_update_export(u8 reg, u8 mask, u8 data); +#endif +#ifdef CONFIG_LEDS_SKY81296 +extern int sky81296_torch_ctrl(int state); +#endif + +static void fimc_is_group_set_torch(struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame) +{ + if (group->prev) + return; + + if (group->aeflashMode != ldr_frame->shot->ctl.aa.aeflashMode) { + group->aeflashMode = ldr_frame->shot->ctl.aa.aeflashMode; + switch (group->aeflashMode) { + case AA_FLASHMODE_ON_ALWAYS: /*TORCH mode*/ +#ifdef CONFIG_LEDS_LM3560 + lm3560_reg_update_export(0xE0, 0xFF, 0xEF); +#elif defined(CONFIG_LEDS_SKY81296) + sky81296_torch_ctrl(1); +#endif + break; + case AA_FLASHMODE_START: /*Pre flash mode*/ +#ifdef CONFIG_LEDS_LM3560 + lm3560_reg_update_export(0xE0, 0xFF, 0xEF); +#elif defined(CONFIG_LEDS_SKY81296) + sky81296_torch_ctrl(1); +#endif + break; + case AA_FLASHMODE_CAPTURE: /*Main flash mode*/ + break; + case AA_FLASHMODE_OFF: /*OFF mode*/ +#ifdef CONFIG_LEDS_SKY81296 + sky81296_torch_ctrl(0); +#endif + break; + default: + break; + } + } + return; +} +#endif + +#ifdef DEBUG_AA +static void fimc_is_group_debug_aa_shot(struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame) +{ + if (group->prev) + return; + +#ifdef DEBUG_FLASH + if (ldr_frame->shot->ctl.aa.aeflashMode != group->flashmode) { + group->flashmode = ldr_frame->shot->ctl.aa.aeflashMode; + pr_info("flash ctl : %d(%d)\n", group->flashmode, ldr_frame->fcount); + } +#endif +} + +static void fimc_is_group_debug_aa_done(struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame) +{ + if (group->prev) + return; + +#ifdef DEBUG_FLASH + if (ldr_frame->shot->dm.flash.firingStable != group->flash.firingStable) { + group->flash.firingStable = ldr_frame->shot->dm.flash.firingStable; + pr_info("flash stable : %d(%d)\n", group->flash.firingStable, ldr_frame->fcount); + } + + if (ldr_frame->shot->dm.flash.flashReady!= group->flash.flashReady) { + group->flash.flashReady = ldr_frame->shot->dm.flash.flashReady; + pr_info("flash ready : %d(%d)\n", group->flash.flashReady, ldr_frame->fcount); + } + + if (ldr_frame->shot->dm.flash.flashOffReady!= group->flash.flashOffReady) { + group->flash.flashOffReady = ldr_frame->shot->dm.flash.flashOffReady; + pr_info("flash off : %d(%d)\n", group->flash.flashOffReady, ldr_frame->fcount); + } +#endif +} +#endif + +static void fimc_is_group_start_trigger(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_frame *frame) +{ + BUG_ON(!group); + BUG_ON(!frame); + + atomic_inc(&group->rcount); + queue_kthread_work(group->worker, &frame->work); +} + +static void fimc_is_group_pump(struct kthread_work *work) +{ + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_frame *frame; + + frame = container_of(work, struct fimc_is_frame, work); + groupmgr = frame->work_data1; + group = frame->work_data2; + + fimc_is_group_start(groupmgr, group, frame); +} + +int fimc_is_groupmgr_probe(struct fimc_is_groupmgr *groupmgr) +{ + int ret = 0; + u32 i, j; + + for (i = 0; i < FIMC_IS_MAX_NODES; ++i) { + spin_lock_init(&groupmgr->framemgr[i].frame_slock); + INIT_LIST_HEAD(&groupmgr->framemgr[i].frame_free_head); + groupmgr->framemgr[i].frame_free_cnt = 0; + + for (j = 0; j < FIMC_IS_MAX_GFRAME; ++j) { + groupmgr->framemgr[i].frame[j].fcount = 0; + fimc_is_gframe_s_free(&groupmgr->framemgr[i], + &groupmgr->framemgr[i].frame[j]); + } + + for (j = 0; j < GROUP_ID_MAX; ++j) + groupmgr->group[i][j] = NULL; + } + + for (i = 0; i < GROUP_ID_MAX; ++i) { + atomic_set(&groupmgr->group_refcount[i], 0); + clear_bit(FIMC_IS_GGROUP_START, &groupmgr->group_state[i]); + clear_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[i]); + clear_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[i]); + } + + return ret; +} + +int fimc_is_group_probe(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + u32 entry) +{ + int ret = 0; + + BUG_ON(!groupmgr); + BUG_ON(!group); + + group->id = GROUP_ID_INVALID; + group->leader.entry = entry; + + clear_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &group->state); + clear_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state); + clear_bit(FIMC_IS_GROUP_SHOT, &group->state); + clear_bit(FIMC_IS_GROUP_READY, &group->state); + clear_bit(FIMC_IS_GROUP_RUN, &group->state); + clear_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state); + clear_bit(FIMC_IS_GROUP_INIT, &group->state); + + return ret; +} + +int fimc_is_group_open(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 id, u32 instance, + struct fimc_is_video_ctx *vctx, + struct fimc_is_device_ischain *device, + fimc_is_start_callback start_callback) +{ + int ret = 0, i; + char name[30]; + struct fimc_is_core *core; + struct fimc_is_subdev *leader; + struct fimc_is_framemgr *framemgr; + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!device); + BUG_ON(!vctx); + BUG_ON(instance >= FIMC_IS_MAX_NODES); + BUG_ON(id >= GROUP_ID_MAX); + + leader = &group->leader; + framemgr = GET_SRC_FRAMEMGR(vctx); + core = (struct fimc_is_core *)device->interface->core; + + mdbgd_ischain("%s(id %d)\n", device, __func__, id); + + if (test_bit(FIMC_IS_GROUP_OPEN, &group->state)) { + merr("group%d already open", device, id); + ret = -EMFILE; + goto p_err; + } + + /* 1. start kthread */ + if (!test_bit(FIMC_IS_GGROUP_START, &groupmgr->group_state[id])) { + init_kthread_worker(&groupmgr->group_worker[id]); + snprintf(name, sizeof(name), "fimc_is_gworker%d", id); + groupmgr->group_task[id] = kthread_run(kthread_worker_fn, + &groupmgr->group_worker[id], name); + if (IS_ERR(groupmgr->group_task[id])) { + err("failed to create group_task%d\n", id); + ret = -ENOMEM; + goto p_err; + } + + ret = sched_setscheduler_nocheck(groupmgr->group_task[id], SCHED_FIFO, ¶m); + if (ret) { + merr("sched_setscheduler_nocheck is fail(%d)", group, ret); + goto p_err; + } + + set_bit(FIMC_IS_GGROUP_START, &groupmgr->group_state[id]); + } + + group->worker = &groupmgr->group_worker[id]; + for (i = 0; i < FRAMEMGR_MAX_REQUEST; ++i) + init_kthread_work(&framemgr->frame[i].work, fimc_is_group_pump); + + /* 2. Init Group */ + clear_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &group->state); + clear_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state); + clear_bit(FIMC_IS_GROUP_SHOT, &group->state); + clear_bit(FIMC_IS_GROUP_READY, &group->state); + clear_bit(FIMC_IS_GROUP_RUN, &group->state); + clear_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state); + clear_bit(FIMC_IS_GROUP_INIT, &group->state); + + group->start_callback = start_callback; + group->device = device; + group->id = id; + group->instance = instance; + group->source_vid = 0; + group->fcount = 0; + group->pcount = 0; + group->aeflashMode = 0; /* Flash Mode Control */ + atomic_set(&group->scount, 0); + atomic_set(&group->rcount, 0); + atomic_set(&group->backup_fcount, 0); + atomic_set(&group->sensor_fcount, 1); + sema_init(&group->smp_trigger, 0); + + INIT_LIST_HEAD(&group->frame_group_head); + group->frame_group_cnt = 0; + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + measure_init(&group->time, group->instance, group->id, 66); +#endif +#endif + + /* 3. Subdev Init */ + mutex_init(&leader->mutex_state); + leader->vctx = vctx; + leader->group = group; + leader->leader = NULL; + leader->input.width = 0; + leader->input.height = 0; + leader->output.width = 0; + leader->output.height = 0; + clear_bit(FIMC_IS_SUBDEV_START, &leader->state); + set_bit(FIMC_IS_SUBDEV_OPEN, &leader->state); + + /* 4. Configure Group & Subdev List */ + switch (id) { + case GROUP_ID_3A0: + case GROUP_ID_3A1: + /* path configuration */ + group->prev = NULL; + if (GET_FIMC_IS_NUM_OF_SUBIP(core, isp)) { + group->next = &device->group_isp; + /* HACK */ + group->next->prev = group; + } else { + group->next = NULL; + } + group->subdev[ENTRY_SCALERC] = NULL; + group->subdev[ENTRY_DIS] = NULL; + group->subdev[ENTRY_TDNR] = NULL; + group->subdev[ENTRY_SCALERP] = NULL; + group->subdev[ENTRY_LHFD] = NULL; + group->subdev[ENTRY_3AAC] = &device->taac; + group->subdev[ENTRY_3AAP] = &device->taap; + + device->taac.leader = leader; + device->taap.leader = leader; + + device->taac.group = group; + device->taap.group = group; + + set_bit(FIMC_IS_GROUP_ACTIVE, &group->state); + break; + case GROUP_ID_ISP: + /* path configuration */ + group->prev = NULL; + group->next = NULL; + if (GET_FIMC_IS_NUM_OF_SUBIP(core, scc)) + group->subdev[ENTRY_SCALERC] = &device->scc; + else + group->subdev[ENTRY_SCALERC] = NULL; + /* dis is not included to any group initially */ + group->subdev[ENTRY_DIS] = NULL; + if (GET_FIMC_IS_NUM_OF_SUBIP(core, dnr)) + group->subdev[ENTRY_TDNR] = &device->dnr; + else + group->subdev[ENTRY_TDNR] = NULL; + + if (GET_FIMC_IS_NUM_OF_SUBIP(core, scp)) + group->subdev[ENTRY_SCALERP] = &device->scp; + else + group->subdev[ENTRY_SCALERP] = NULL; + + if (GET_FIMC_IS_NUM_OF_SUBIP(core, fd)) + group->subdev[ENTRY_LHFD] = &device->fd; + else + group->subdev[ENTRY_LHFD] = NULL; + group->subdev[ENTRY_3AAC] = NULL; + group->subdev[ENTRY_3AAP] = NULL; + + device->scc.leader = leader; + device->dis.leader = leader; + device->dnr.leader = leader; + device->scp.leader = leader; + device->fd.leader = leader; + + device->scc.group = group; + device->dis.group = group; + device->dnr.group = group; + device->scp.group = group; + device->fd.group = group; + + set_bit(FIMC_IS_GROUP_ACTIVE, &group->state); + break; + case GROUP_ID_DIS: + /* path configuration */ + group->prev = NULL; + group->next = NULL; + group->subdev[ENTRY_SCALERC] = NULL; + group->subdev[ENTRY_DIS] = NULL; + group->subdev[ENTRY_TDNR] = NULL; + group->subdev[ENTRY_SCALERP] = NULL; + group->subdev[ENTRY_LHFD] = NULL; + group->subdev[ENTRY_3AAC] = NULL; + group->subdev[ENTRY_3AAP] = NULL; + + clear_bit(FIMC_IS_GROUP_ACTIVE, &group->state); + break; + default: + merr("group id(%d) is invalid", vctx, id); + ret = -EINVAL; + goto p_err; + } + + /* 5. Update Group Manager */ + groupmgr->group[instance][id] = group; + atomic_inc(&groupmgr->group_refcount[id]); + set_bit(FIMC_IS_GROUP_OPEN, &group->state); + +p_err: + mdbgd_group("%s(%d)\n", group, __func__, ret); + return ret; +} + +int fimc_is_group_close(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group) +{ + int ret = 0; + u32 refcount, i; + struct fimc_is_group_framemgr *gframemgr; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + + refcount = atomic_read(&groupmgr->group_refcount[group->id]); + + if (!test_bit(FIMC_IS_GROUP_OPEN, &group->state)) { + merr("group%d already close", group, group->id); + ret = -EMFILE; + goto p_err; + } + + /* + * Maybe there are some waiting smp_shot semaphores when finishing kthread + * in group close. This situation caused waiting kthread_stop to finish it + * We should check if there are smp_shot in waiting list. + */ + if (test_bit(FIMC_IS_GROUP_INIT, &group->state)) { + while (!list_empty(&group->smp_shot.wait_list)) { + warn("group%d frame reqs are waiting in semaphore[%d] when closing", + group->id, group->smp_shot.count); + up(&group->smp_shot); + } + } + + if ((refcount == 1) && + test_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[group->id]) && + groupmgr->group_task[group->id]) { + + set_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id]); + + /* flush semaphore shot */ + atomic_inc(&group->smp_shot_count); + + /* flush semaphore trigger */ + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) + up(&group->smp_trigger); + + /* + * flush kthread wait until all work is complete + * it's dangerous if all is not finished + * so it's commented currently + * flush_kthread_worker(&groupmgr->group_worker[group->id]); + */ + kthread_stop(groupmgr->group_task[group->id]); + + clear_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id]); + clear_bit(FIMC_IS_GGROUP_START, &groupmgr->group_state[group->id]); + clear_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[group->id]); + } + + groupmgr->group[group->instance][group->id] = NULL; + atomic_dec(&groupmgr->group_refcount[group->id]); + + /* all group is closed */ + if (!groupmgr->group[group->instance][GROUP_ID_3A0] && + !groupmgr->group[group->instance][GROUP_ID_3A1] && + !groupmgr->group[group->instance][GROUP_ID_ISP] && + !groupmgr->group[group->instance][GROUP_ID_DIS]) { + gframemgr = &groupmgr->framemgr[group->instance]; + if (gframemgr->frame_free_cnt != FIMC_IS_MAX_GFRAME) { + mwarn("gframemgr free count is invalid(%d)", group, + gframemgr->frame_free_cnt); + INIT_LIST_HEAD(&gframemgr->frame_free_head); + gframemgr->frame_free_cnt = 0; + for (i = 0; i < FIMC_IS_MAX_GFRAME; ++i) { + gframemgr->frame[i].fcount = 0; + fimc_is_gframe_s_free(gframemgr, + &gframemgr->frame[i]); + } + } + } + + group->id = GROUP_ID_INVALID; + clear_bit(FIMC_IS_GROUP_INIT, &group->state); + clear_bit(FIMC_IS_GROUP_OPEN, &group->state); + clear_bit(FIMC_IS_SUBDEV_OPEN, &group->leader.state); + +p_err: + mdbgd_group("%s(ref %d, %d)", group, __func__, (refcount - 1), ret); + return ret; +} + +int fimc_is_group_init(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + bool otf_input, + u32 video_id) +{ + int ret = 0; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + + if (test_bit(FIMC_IS_GROUP_INIT, &group->state)) { + merr("already initialized", group); + /* HACK */ + /* ret = -EINVAL; */ + goto p_err; + } + + group->source_vid = video_id; + + if (!test_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[group->id])) { + if (otf_input) + sema_init(&groupmgr->group_smp_res[group->id], MIN_OF_SHOT_RSC); + else + sema_init(&groupmgr->group_smp_res[group->id], 1); + + set_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[group->id]); + } + + if (otf_input) { + sema_init(&group->smp_shot, MIN_OF_SHOT_RSC); + atomic_set(&group->smp_shot_count, MIN_OF_SHOT_RSC); + set_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state); + group->async_shots = MIN_OF_ASYNC_SHOTS; + group->sync_shots = MIN_OF_SYNC_SHOTS; + } else { + sema_init(&group->smp_shot, 1); + atomic_set(&group->smp_shot_count, 1); + clear_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state); + group->async_shots = 0; + group->sync_shots = 1; + } + + set_bit(FIMC_IS_GROUP_INIT, &group->state); + +p_err: + mdbgd_group("%s(otf : %d, %d)\n", group, __func__, otf_input, ret); + return ret; +} + +int fimc_is_group_change(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group_isp, + struct fimc_is_frame *frame) +{ + int ret = 0; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader_isp, *leader_dis; + struct fimc_is_group *group_dis; + + BUG_ON(!groupmgr); + BUG_ON(!group_isp); + BUG_ON(!group_isp->device); + BUG_ON(!frame); + + device = group_isp->device; + group_dis = &device->group_dis; + leader_dis = &group_dis->leader; + leader_isp = &group_isp->leader; + + if (frame->shot->ctl.aa.videoStabilizationMode) { + if (!test_bit(FIMC_IS_GROUP_READY, &group_dis->state)) { + merr("DIS group is not ready", group_dis); + frame->shot->dm.aa.videoStabilizationMode = 0; + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_GROUP_ACTIVE, &group_dis->state)) { + pr_info("[GRP%d] Change On\n", group_isp->id); + + /* HACK */ + sema_init(&group_dis->smp_trigger, 0); + + group_isp->prev = &device->group_3aa; + group_isp->next = group_dis; + group_isp->subdev[ENTRY_SCALERC] = &device->scc; + group_isp->subdev[ENTRY_DIS] = &device->dis; + group_isp->subdev[ENTRY_TDNR] = NULL; + group_isp->subdev[ENTRY_SCALERP] = NULL; + group_isp->subdev[ENTRY_LHFD] = NULL; + + group_dis->next = NULL; + group_dis->prev = group_isp; + group_dis->subdev[ENTRY_SCALERC] = NULL; + group_dis->subdev[ENTRY_DIS] = NULL; + group_dis->subdev[ENTRY_TDNR] = &device->dnr; + group_dis->subdev[ENTRY_SCALERP] = &device->scp; + group_dis->subdev[ENTRY_LHFD] = &device->fd; + + device->scc.leader = leader_isp; + device->dis.leader = leader_isp; + device->dnr.leader = leader_dis; + device->scp.leader = leader_dis; + device->fd.leader = leader_dis; + + device->scc.group = group_isp; + device->dis.group = group_isp; + device->dnr.group = group_dis; + device->scp.group = group_dis; + device->fd.group = group_dis; + + set_bit(FIMC_IS_GROUP_ACTIVE, &group_dis->state); + } + + frame->shot->dm.aa.videoStabilizationMode = 1; + } else { + if (test_bit(FIMC_IS_GROUP_ACTIVE, &group_dis->state)) { + pr_info("[GRP%d] Change Off\n", group_isp->id); + group_isp->prev = &device->group_3aa; + group_isp->next = NULL; + group_isp->subdev[ENTRY_SCALERC] = &device->scc; + group_isp->subdev[ENTRY_DIS] = &device->dis; + group_isp->subdev[ENTRY_TDNR] = &device->dnr; + group_isp->subdev[ENTRY_SCALERP] = &device->scp; + group_isp->subdev[ENTRY_LHFD] = &device->fd; + + group_dis->next = NULL; + group_dis->prev = NULL; + group_dis->subdev[ENTRY_SCALERC] = NULL; + group_dis->subdev[ENTRY_DIS] = NULL; + group_dis->subdev[ENTRY_TDNR] = NULL; + group_dis->subdev[ENTRY_SCALERP] = NULL; + group_dis->subdev[ENTRY_LHFD] = NULL; + + device->scc.leader = leader_isp; + device->dis.leader = leader_isp; + device->dnr.leader = leader_isp; + device->scp.leader = leader_isp; + device->fd.leader = leader_isp; + + device->scc.group = group_isp; + device->dis.group = group_isp; + device->dnr.group = group_isp; + device->scp.group = group_isp; + device->fd.group = group_isp; + + clear_bit(FIMC_IS_GROUP_ACTIVE, &group_dis->state); + } + + frame->shot->dm.aa.videoStabilizationMode = 0; + } + +p_err: + return ret; +} + +int fimc_is_group_process_start(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue) +{ + int ret = 0; + struct fimc_is_device_sensor *sensor = NULL; + struct fimc_is_framemgr *framemgr = NULL; + u32 shot_resource = 1; + u32 sensor_fcount; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!group->device); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + BUG_ON(!queue); + BUG_ON(!test_bit(FIMC_IS_GGROUP_INIT, &groupmgr->group_state[group->id])); + BUG_ON(!test_bit(FIMC_IS_GROUP_INIT, &group->state)); + + if (test_bit(FIMC_IS_GROUP_READY, &group->state)) { + warn("already group start"); + goto p_err; + } + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + framemgr = &queue->framemgr; + if (!framemgr) { + err("framemgr is null\n"); + ret = -EINVAL; + goto p_err; + } + + sensor = group->device->sensor; + if (!sensor) { + err("sensor is null\n"); + ret = -EINVAL; + goto p_err; + } + + /* async & sync shots */ + if (fimc_is_sensor_g_framerate(sensor) > 30) + group->async_shots = max_t(int, MIN_OF_ASYNC_SHOTS, + DIV_ROUND_UP(framemgr->frame_cnt, 3)); + else + group->async_shots = MIN_OF_ASYNC_SHOTS; + group->sync_shots = max_t(int, MIN_OF_SYNC_SHOTS, + framemgr->frame_cnt - group->async_shots); + + /* shot resource */ + shot_resource = group->async_shots + MIN_OF_SYNC_SHOTS; + sema_init(&groupmgr->group_smp_res[group->id], shot_resource); + + /* frame count */ + sensor_fcount = fimc_is_sensor_g_fcount(sensor) + 1; + atomic_set(&group->sensor_fcount, sensor_fcount); + atomic_set(&group->backup_fcount, sensor_fcount - 1); + group->fcount = sensor_fcount - 1; + + info("[OTF] framerate: %d, async shots: %d, shot resource: %d\n", + fimc_is_sensor_g_framerate(sensor), + group->async_shots, + shot_resource); + + memset(&group->intent_ctl, 0, sizeof(struct camera2_ctl)); + } + + sema_init(&group->smp_shot, shot_resource); + atomic_set(&group->smp_shot_count, shot_resource); + + atomic_set(&group->scount, 0); + atomic_set(&group->rcount, 0); + sema_init(&group->smp_trigger, 0); + + set_bit(FIMC_IS_GROUP_READY, &group->state); + +p_err: + return ret; +} + +int fimc_is_group_process_stop(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue) +{ + int ret = 0; + int retry; + u32 rcount; + struct fimc_is_framemgr *framemgr; + struct fimc_is_device_ischain *device; + struct fimc_is_device_sensor *sensor; + u32 group_id; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + BUG_ON(!queue); + + if (!test_bit(FIMC_IS_GROUP_READY, &group->state)) { + warn("already group stop"); + goto p_err; + } + + device = group->device; + if (!device) { + merr("device is NULL", group); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state)) { + warn("alredy close sensor was called"); + goto p_err; + } + + sensor = device->sensor; + framemgr = &queue->framemgr; + + if (test_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &group->state)) { + set_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state); + clear_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &group->state); + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state) && + !list_empty(&group->smp_trigger.wait_list)) { + if (!sensor) { + warn("sensor is NULL, forcely trigger"); + up(&group->smp_trigger); + goto check_completion; + } + + if (!test_bit(FIMC_IS_SENSOR_OPEN, &sensor->state)) { + warn("sensor is closed, forcely trigger"); + up(&group->smp_trigger); + goto check_completion; + } + + if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state)) { + warn("front sensor is stopped, forcely trigger"); + up(&group->smp_trigger); + goto check_completion; + } + + if (!test_bit(FIMC_IS_SENSOR_BACK_START, &sensor->state)) { + warn("back sensor is stopped, forcely trigger"); + up(&group->smp_trigger); + goto check_completion; + } + } + } + +check_completion: + retry = 150; + while (--retry && framemgr->frame_req_cnt) { + warn("%d frame reqs waiting...\n", framemgr->frame_req_cnt); + msleep(20); + } + + if (!retry) { + merr("waiting(until request empty) is fail", group); + ret = -EINVAL; + } + + if (!test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + retry = 10; + while (--retry && framemgr->frame_pro_cnt) { + warn("%d frame pros waiting...\n", framemgr->frame_pro_cnt); + msleep(20); + } + + if (!retry) { + merr("waiting(until process empty) is fail", group); + ret = -EINVAL; + } + } + + if (test_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state)) { + ret = fimc_is_itf_force_stop(device, GROUP_ID(group->id)); + if (ret) { + merr("fimc_is_itf_force_stop is fail", group); + ret = -EINVAL; + } + } else { + /* if there's only one group of isp, send group id by 3a0 */ + if ((group->id == GROUP_ID_ISP) && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) == 0 && + GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1) == 0) + group_id = GROUP_ID(GROUP_ID_3A0); + else + group_id = GROUP_ID(group->id); + + ret = fimc_is_itf_process_stop(device, group_id); + if (ret) { + merr("fimc_is_itf_process_stop is fail", group); + ret = -EINVAL; + } + } + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + retry = 10; + while (--retry && framemgr->frame_pro_cnt) { + warn("%d frame pros waiting...\n", framemgr->frame_pro_cnt); + msleep(20); + } + + if (!retry) { + merr("waiting(until process empty) is fail", group); + ret = -EINVAL; + } + } + + rcount = atomic_read(&group->rcount); + if (rcount) { + merr("rcount is not empty(%d)", group, rcount); + ret = -EINVAL; + } + + retry = 100; + while (--retry && test_bit(FIMC_IS_GROUP_SHOT, &group->state)) { + mgwarn(" thread stop waiting...", device, group); + msleep(10); + } + + if (!retry) { + mgerr(" waiting(until thread stop) is fail", device, group); + ret = -EINVAL; + } + + fimc_is_gframe_flush(groupmgr, group); + + clear_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state); + clear_bit(FIMC_IS_GROUP_READY, &group->state); + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) + pr_info("[OTF] sensor fcount: %d, fcount: %d\n", + atomic_read(&group->sensor_fcount), group->fcount); + +p_err: + return ret; +} + +int fimc_is_group_buffer_queue(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue, + u32 index) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_device_sensor *sensor; + struct fimc_is_device_ischain *device; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!group->device); + BUG_ON(!group->device->sensor); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + BUG_ON(!queue); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + + device = group->device; + sensor = device->sensor; + framemgr = &queue->framemgr; + + /* 1. check frame validation */ + frame = &framemgr->frame[index]; + if (!frame) { + err("frame is null\n"); + ret = -EINVAL; + goto p_err; + } + + if (unlikely(!test_bit(FRAME_INI_MEM, &frame->memory))) { + err("frame %d is NOT init", index); + ret = EINVAL; + goto p_err; + } + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + do_gettimeofday(&frame->time_queued); +#endif +#endif + + /* 2. update frame manager */ + framemgr_e_barrier_irqs(framemgr, index, flags); + + if (frame->state == FIMC_IS_FRAME_STATE_FREE) { + if (frame->req_flag) { + merr("req_flag of buffer%d is not clear(%08X)", + group, frame->index, (u32)frame->req_flag); + frame->req_flag = 0; + } + + if (test_bit(OUT_SCC_FRAME, &frame->out_flag)) { + merr("scc output is not generated", group); + clear_bit(OUT_SCC_FRAME, &frame->out_flag); + } + + if (test_bit(OUT_DIS_FRAME, &frame->out_flag)) { + merr("dis output is not generated", group); + clear_bit(OUT_DIS_FRAME, &frame->out_flag); + } + + if (test_bit(OUT_SCP_FRAME, &frame->out_flag)) { + merr("scp output is not generated", group); + clear_bit(OUT_SCP_FRAME, &frame->out_flag); + } + + if (test_bit(OUT_3AAC_FRAME, &frame->out_flag)) { + merr("3aac output is not generated", group); + clear_bit(OUT_3AAC_FRAME, &frame->out_flag); + } + + if (!test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state) && + (framemgr->frame_req_cnt >= 3)) + mwarn("GROUP%d is working lately(%d)", + group, group->id, framemgr->frame_req_cnt); + + frame->fcount = frame->shot->dm.request.frameCount; + frame->rcount = frame->shot->ctl.request.frameCount; + frame->work_data1 = groupmgr; + frame->work_data2 = group; + +#ifdef FIXED_FPS_DEBUG + frame->shot_ext->shot.ctl.aa.aeTargetFpsRange[0] = FIXED_FPS_VALUE; + frame->shot_ext->shot.ctl.aa.aeTargetFpsRange[1] = FIXED_FPS_VALUE; + frame->shot_ext->shot.ctl.sensor.frameDuration = 1000000000/FIXED_FPS_VALUE; +#endif + +#ifdef ENABLE_FAST_SHOT + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + memcpy(&group->fast_ctl.aa, &frame->shot->ctl.aa, + sizeof(struct camera2_aa_ctl)); + memcpy(&group->fast_ctl.scaler, &frame->shot->ctl.scaler, + sizeof(struct camera2_scaler_ctl)); + } +#endif + + fimc_is_frame_trans_fre_to_req(framemgr, frame); + } else { + err("frame(%d) is invalid state(%d)\n", index, frame->state); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, index, flags); + + if (unlikely(!test_bit(FRAME_MAP_MEM, &frame->memory) && + !test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state))) { + fimc_is_itf_map(device, GROUP_ID(group->id), frame->dvaddr_shot, frame->shot_size); + set_bit(FRAME_MAP_MEM, &frame->memory); + } + + fimc_is_group_start_trigger(groupmgr, group, frame); + +p_err: + return ret; +} + +int fimc_is_group_buffer_finish(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 index) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!group->leader.vctx); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + + framemgr = GET_GROUP_FRAMEMGR(group); + + /* 2. update frame manager */ + framemgr_e_barrier_irqs(framemgr, index, flags); + + fimc_is_frame_complete_head(framemgr, &frame); + if (frame) { + if (frame->index == index) { + fimc_is_frame_trans_com_to_fre(framemgr, frame); + + frame->shot_ext->free_cnt = framemgr->frame_fre_cnt; + frame->shot_ext->request_cnt = framemgr->frame_req_cnt; + frame->shot_ext->process_cnt = framemgr->frame_pro_cnt; + frame->shot_ext->complete_cnt = framemgr->frame_com_cnt; + } else { + merr("buffer index is NOT matched(G%d, %d != %d)", + group, group->id, index, frame->index); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + } else { + merr("frame is empty from complete", group); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, index, flags); + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + do_gettimeofday(&frame->time_dequeued); + measure_time(&group->time, + &frame->time_queued, &frame->time_shot, + &frame->time_shotdone, &frame->time_dequeued); +#endif +#endif + + return ret; +} + +int fimc_is_group_start(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame) +{ + int ret = 0; + struct fimc_is_device_ischain *device; + struct fimc_is_resourcemgr *resourcemgr; + struct fimc_is_group *group_next, *group_prev; + struct fimc_is_group_framemgr *gframemgr; + struct fimc_is_group_frame *gframe; + int async_step = 0; + bool try_sdown = false; + bool try_rdown = false; +#ifdef ENABLE_DVFS + int scenario_id; +#endif + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!group->leader.vctx); + BUG_ON(!group->start_callback); + BUG_ON(!group->device); + BUG_ON(!ldr_frame); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + + set_bit(FIMC_IS_GROUP_SHOT, &group->state); + atomic_dec(&group->rcount); + + if (test_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state)) { + mwarn("g%dframe is cancelled(force stop)", group, group->id); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id])) { + merr("cancel by group stop #1", group); + ret = -EINVAL; + goto p_err; + } + + PROGRAM_COUNT(1); + ret = down_interruptible(&group->smp_shot); + if (ret) { + err("down is fail1(%d)", ret); + goto p_err; + } + atomic_dec(&group->smp_shot_count); + try_sdown = true; + + /* skip left operation, if group is closing */ + if (!test_bit(FIMC_IS_GROUP_READY, &group->state)) { + mwarn("this group%d was already process stoped", group, group->id); + ret = -EINVAL; + goto p_err; + } + + PROGRAM_COUNT(2); + ret = down_interruptible(&groupmgr->group_smp_res[group->id]); + if (ret) { + err("down is fail2(%d)", ret); + goto p_err; + } + try_rdown = true; + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + if (atomic_read(&group->smp_shot_count) < MIN_OF_SYNC_SHOTS) { + PROGRAM_COUNT(3); + ret = down_interruptible(&group->smp_trigger); + if (ret) { + err("down is fail3(%d)", ret); + goto p_err; + } + } else { + /* + * backup fcount can not be bigger than sensor fcount + * otherwise, duplicated shot can be generated. + * this is problem can be caused by hal qbuf timing + */ + if (atomic_read(&group->backup_fcount) >= + atomic_read(&group->sensor_fcount)) { + PROGRAM_COUNT(4); + ret = down_interruptible(&group->smp_trigger); + if (ret) { + err("down is fail4(%d)", ret); + goto p_err; + } + } else { + /* + * this statement is execued only at initial. + * automatic increase the frame count of sensor + * for next shot without real frame start + */ + + /* it's a async shot time */ + async_step = 1; + } + } + + if (test_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id])) { + err("cancel by group stop #2"); + ret = -EINVAL; + goto p_err; + } + + ldr_frame->fcount = atomic_read(&group->sensor_fcount); + atomic_set(&group->backup_fcount, ldr_frame->fcount); + ldr_frame->shot->dm.request.frameCount = ldr_frame->fcount; + ldr_frame->shot->dm.sensor.timeStamp = fimc_is_get_timestamp(); + + /* real automatic increase */ + if (async_step && (atomic_read(&group->smp_shot_count) > MIN_OF_SYNC_SHOTS)) + atomic_inc(&group->sensor_fcount); + } + + if (test_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id])) { + err("cancel by group stop #3"); + ret = -EINVAL; + goto p_err; + } + +#ifdef ENABLE_VDIS + if (group->id == GROUP_ID_ISP) + fimc_is_group_change(groupmgr, group, ldr_frame); + + /* HACK */ + if ((group->id == GROUP_ID_DIS) && + test_bit(FIMC_IS_GROUP_ACTIVE, &group->state)) + down(&group->smp_trigger); +#endif + + PROGRAM_COUNT(5); + device = group->device; + resourcemgr = device->resourcemgr; + group_next = group->next; + group_prev = group->prev; + gframemgr = &groupmgr->framemgr[group->instance]; + if (!gframemgr) { + err("gframemgr is NULL(instance %d)", group->instance); + ret = -EINVAL; + goto p_err; + } + + if (group_prev && !group_next) { + /* tailer */ + spin_lock_irq(&gframemgr->frame_slock); + fimc_is_gframe_group_head(group, &gframe); + if (!gframe) { + merr("g%dframe is NULL1", group, group->id); + warn("GRP%d(res %d, rcnt %d), GRP2(res %d, rcnt %d)", + device->group_3aa.id, + groupmgr->group_smp_res[device->group_3aa.id].count, + atomic_read(&device->group_3aa.rcount), + groupmgr->group_smp_res[device->group_isp.id].count, + atomic_read(&device->group_isp.rcount)); + fimc_is_gframe_print_group(group_prev); + fimc_is_gframe_print_free(gframemgr); + spin_unlock_irq(&gframemgr->frame_slock); + ret = -EINVAL; + goto p_err; + } + + if (ldr_frame->fcount != gframe->fcount) { + mwarn("grp%d shot mismatch(%d != %d)", group, group->id, + ldr_frame->fcount, gframe->fcount); + gframe = fimc_is_gframe_rewind(groupmgr, group, + ldr_frame->fcount); + if (!gframe) { + spin_unlock_irq(&gframemgr->frame_slock); + merr("rewinding is fail,can't recovery", group); + goto p_err; + } + } + + fimc_is_gframe_s_info(gframe, group->id, ldr_frame); + fimc_is_gframe_trans_grp_to_fre(group, gframemgr, gframe); + spin_unlock_irq(&gframemgr->frame_slock); + } else if (!group_prev && group_next) { + /* leader */ + group->fcount++; + spin_lock_irq(&gframemgr->frame_slock); + fimc_is_gframe_free_head(gframemgr, &gframe); + if (!gframe) { + merr("g%dframe is NULL2", group, group->id); + warn("GRP%d(res %d, rcnt %d), GRP2(res %d, rcnt %d)", + device->group_3aa.id, + groupmgr->group_smp_res[device->group_3aa.id].count, + atomic_read(&device->group_3aa.rcount), + groupmgr->group_smp_res[device->group_isp.id].count, + atomic_read(&device->group_isp.rcount)); + fimc_is_gframe_print_free(gframemgr); + fimc_is_gframe_print_group(group_next); + spin_unlock_irq(&gframemgr->frame_slock); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state) && + (ldr_frame->fcount != group->fcount)) { + if (ldr_frame->fcount > group->fcount) { + warn("grp%d shot mismatch(%d != %d)", group->id, + ldr_frame->fcount, group->fcount); + group->fcount = ldr_frame->fcount; + } else { + spin_unlock_irq(&gframemgr->frame_slock); + err("grp%d shot mismatch(%d, %d)", group->id, + ldr_frame->fcount, group->fcount); + group->fcount--; + ret = -EINVAL; + goto p_err; + } + } + + gframe->fcount = ldr_frame->fcount; + fimc_is_gframe_s_info(gframe, group->id, ldr_frame); + fimc_is_gframe_trans_fre_to_grp(gframemgr, group_next, gframe); + spin_unlock_irq(&gframemgr->frame_slock); + } else if (group_prev && group_next) { + spin_lock_irq(&gframemgr->frame_slock); + fimc_is_gframe_group_head(group, &gframe); + if (!gframe) { + merr("g%dframe is NULL3", group, group->id); + warn("GRP%d(res %d, rcnt %d), GRP2(res %d, rcnt %d)", + device->group_3aa.id, + groupmgr->group_smp_res[device->group_3aa.id].count, + atomic_read(&device->group_3aa.rcount), + groupmgr->group_smp_res[device->group_isp.id].count, + atomic_read(&device->group_isp.rcount)); + fimc_is_gframe_print_group(group_prev); + fimc_is_gframe_print_group(group_next); + spin_unlock_irq(&gframemgr->frame_slock); + ret = -EINVAL; + goto p_err; + } + + if (ldr_frame->fcount != gframe->fcount) { + mwarn("grp%d shot mismatch(%d != %d)", group, group->id, + ldr_frame->fcount, gframe->fcount); + gframe = fimc_is_gframe_rewind(groupmgr, group, + ldr_frame->fcount); + if (!gframe) { + spin_unlock_irq(&gframemgr->frame_slock); + merr("rewinding is fail,can't recovery", group); + goto p_err; + } + } + + fimc_is_gframe_s_info(gframe, group->id, ldr_frame); + fimc_is_gframe_trans_grp_to_grp(group, group_next, gframe); + spin_unlock_irq(&gframemgr->frame_slock); + } else { + /* single */ + group->fcount++; + spin_lock_irq(&gframemgr->frame_slock); + fimc_is_gframe_free_head(gframemgr, &gframe); + if (!gframe) { + merr("g%dframe is NULL4", group, group->id); + warn("GRP%d(res %d, rcnt %d), GRP2(res %d, rcnt %d)", + device->group_3aa.id, + groupmgr->group_smp_res[device->group_3aa.id].count, + atomic_read(&device->group_3aa.rcount), + groupmgr->group_smp_res[device->group_isp.id].count, + atomic_read(&device->group_isp.rcount)); + fimc_is_gframe_print_free(gframemgr); + spin_unlock_irq(&gframemgr->frame_slock); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state) && + (ldr_frame->fcount != group->fcount)) { + if (ldr_frame->fcount > group->fcount) { + warn("grp%d shot mismatch(%d != %d)", group->id, + ldr_frame->fcount, group->fcount); + group->fcount = ldr_frame->fcount; + } else { + spin_unlock_irq(&gframemgr->frame_slock); + err("grp%d shot mismatch(%d, %d)", group->id, + ldr_frame->fcount, group->fcount); + group->fcount--; + ret = -EINVAL; + goto p_err; + } + } + + gframe->fcount = ldr_frame->fcount; + spin_unlock_irq(&gframemgr->frame_slock); + } + +#ifdef DEBUG_AA + fimc_is_group_debug_aa_shot(group, ldr_frame); +#endif +#ifdef CONFIG_USE_VENDER_FEATURE + /* Flash Mode Control */ + fimc_is_group_set_torch(group, ldr_frame); +#endif + +#ifdef ENABLE_DVFS + mutex_lock(&resourcemgr->dvfs_ctrl.lock); + if ((!pm_qos_request_active(&device->user_qos)) && + (sysfs_debug.en_dvfs)) { + /* try to find dynamic scenario to apply */ + scenario_id = fimc_is_dvfs_sel_scenario(FIMC_IS_DYNAMIC_SN, device, ldr_frame); + + if (scenario_id > 0) { + struct fimc_is_dvfs_scenario_ctrl *dynamic_ctrl = resourcemgr->dvfs_ctrl.dynamic_ctrl; + info("GRP:%d dynamic scenario(%d)-[%s]\n", + group->id, scenario_id, + dynamic_ctrl->scenarios[dynamic_ctrl->cur_scenario_idx].scenario_nm); + fimc_is_set_dvfs(device, scenario_id); + } + + if ((scenario_id < 0) && (resourcemgr->dvfs_ctrl.dynamic_ctrl->cur_frame_tick == 0)) { + struct fimc_is_dvfs_scenario_ctrl *static_ctrl = resourcemgr->dvfs_ctrl.static_ctrl; + info("GRP:%d restore scenario(%d)-[%s]\n", + group->id, static_ctrl->cur_scenario_id, + static_ctrl->scenarios[static_ctrl->cur_scenario_idx].scenario_nm); + fimc_is_set_dvfs(device, static_ctrl->cur_scenario_id); + } + } + mutex_unlock(&resourcemgr->dvfs_ctrl.lock); +#endif + + PROGRAM_COUNT(6); + ret = group->start_callback(group->device, ldr_frame); + if (ret) { + merr("start_callback is fail", group); + fimc_is_group_cancel(group, ldr_frame); + fimc_is_group_done(groupmgr, group, ldr_frame, VB2_BUF_STATE_ERROR); + } else { + atomic_inc(&group->scount); + } + + clear_bit(FIMC_IS_GROUP_SHOT, &group->state); + PROGRAM_COUNT(7); + return ret; + +p_err: + if (!test_bit(FIMC_IS_GGROUP_REQUEST_STOP, &groupmgr->group_state[group->id])) + fimc_is_group_cancel(group, ldr_frame); + + if (try_sdown) { + atomic_inc(&group->smp_shot_count); + up(&group->smp_shot); + } + + if (try_rdown) + up(&groupmgr->group_smp_res[group->id]); + + clear_bit(FIMC_IS_GROUP_SHOT, &group->state); + PROGRAM_COUNT(8); + + return ret; +} + +int fimc_is_group_done(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame, + u32 done_state) +{ + int ret = 0; + u32 resources; + struct fimc_is_device_ischain *device; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!ldr_frame); + BUG_ON(group->instance >= FIMC_IS_MAX_NODES); + BUG_ON(group->id >= GROUP_ID_MAX); + BUG_ON(!group->device); + +#ifdef ENABLE_VDIS + /* current group notify to next group that shot done is arrvied */ + /* HACK */ + if (group->next && (group->id == GROUP_ID_ISP) && + test_bit(FIMC_IS_GROUP_ACTIVE, &group->next->state)) + up(&group->next->smp_trigger); +#endif + + /* check shot & resource count validation */ + resources = group->async_shots + group->sync_shots; + + if (group->smp_shot.count >= resources) { + merr("G%d, shot count is invalid(%d >= %d+%d)", group, + group->id, group->smp_shot.count, group->async_shots, + group->sync_shots); + atomic_set(&group->smp_shot_count, resources - 1); + sema_init(&group->smp_shot, resources - 1); + } + + if (groupmgr->group_smp_res[group->id].count >= resources) { + merr("G%d, resource count is invalid(%d >= %d+%d)", group, + group->id, groupmgr->group_smp_res[group->id].count, + group->async_shots, group->sync_shots); + sema_init(&groupmgr->group_smp_res[group->id], resources - 1); + } + + device = group->device; + if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state) && + (done_state != VB2_BUF_STATE_DONE)) { + merr("GRP%d NOT DONE(reprocessing)\n", group, group->id); + fimc_is_hw_logdump(device->interface); + } + +#ifdef DEBUG_AA + fimc_is_group_debug_aa_done(group, ldr_frame); +#endif + + clear_bit(FIMC_IS_GROUP_RUN, &group->state); + atomic_inc(&group->smp_shot_count); + up(&group->smp_shot); + up(&groupmgr->group_smp_res[group->id]); + + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.h b/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.h new file mode 100644 index 000000000000..edf41d3aa673 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-groupmgr.h @@ -0,0 +1,178 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_GROUP_MGR_H +#define FIMC_IS_GROUP_MGR_H + +#include "fimc-is-config.h" +#include "fimc-is-time.h" +#include "fimc-is-subdev-ctrl.h" +#include "fimc-is-video.h" + +/* #define DEBUG_AA */ +/* #define DEBUG_FLASH */ + +#define TRACE_GROUP +#define GROUP_ID_3A0 0 /* hardware : CH0 */ +#define GROUP_ID_3A1 1 /* hardware : 3AA */ +#define GROUP_ID_ISP 2 /* hardware : CH1 */ +#define GROUP_ID_DIS 3 +#define GROUP_ID_MAX 4 +#define GROUP_ID_INVALID 5 +#define GROUP_ID_PARM_MASK (0xF) +#define GROUP_ID_SHIFT (16) +#define GROUP_ID_MASK (0xFFFF) +#define GROUP_ID(id) (1 << id) + +#define FIMC_IS_MAX_GFRAME 15 /* max shot buffer of F/W */ +#define MIN_OF_ASYNC_SHOTS 1 +#define MIN_OF_SYNC_SHOTS 2 +#define MIN_OF_SHOT_RSC (MIN_OF_ASYNC_SHOTS + MIN_OF_SYNC_SHOTS) + +enum fimc_is_group_state { + FIMC_IS_GROUP_OPEN, + FIMC_IS_GROUP_INIT, + FIMC_IS_GROUP_READY, + FIMC_IS_GROUP_ACTIVE, + FIMC_IS_GROUP_RUN, + FIMC_IS_GROUP_SHOT, + FIMC_IS_GROUP_REQUEST_FSTOP, + FIMC_IS_GROUP_FORCE_STOP, + FIMC_IS_GROUP_OTF_INPUT +}; + +enum fimc_is_global_group_state { + FIMC_IS_GGROUP_INIT, + FIMC_IS_GGROUP_START, + FIMC_IS_GGROUP_REQUEST_STOP +}; + +struct fimc_is_frame; +struct fimc_is_device_ischain; +typedef int (*fimc_is_start_callback)(struct fimc_is_device_ischain *device, + struct fimc_is_frame *frame); + +struct fimc_is_group_frame { + struct list_head list; + u32 fcount; + struct camera2_node_group group_cfg[GROUP_ID_MAX]; +}; + +struct fimc_is_group_framemgr { + struct fimc_is_group_frame frame[FIMC_IS_MAX_GFRAME]; + spinlock_t frame_slock; + struct list_head frame_free_head; + u32 frame_free_cnt; +}; + +struct fimc_is_group { + struct fimc_is_group *next; + struct fimc_is_group *prev; + + struct fimc_is_subdev leader; + struct fimc_is_subdev *subdev[ENTRY_END]; + struct kthread_worker *worker; + + /* for otf interface */ + atomic_t sensor_fcount; + atomic_t backup_fcount; + struct semaphore smp_trigger; + struct semaphore smp_shot; + atomic_t smp_shot_count; + u32 async_shots; + u32 sync_shots; + struct camera2_ctl fast_ctl; + struct camera2_ctl intent_ctl; + + u32 id; /* group id */ + u32 instance; /* device instance */ + u32 source_vid; /* source video id */ + u32 pcount; /* program count */ + u32 fcount; /* frame count */ + atomic_t scount; /* shot count */ + atomic_t rcount; /* request count */ + unsigned long state; + + struct list_head frame_group_head; + u32 frame_group_cnt; + + fimc_is_start_callback start_callback; + struct fimc_is_device_ischain *device; + +#ifdef DEBUG_AA +#ifdef DEBUG_FLASH + enum aa_ae_flashmode flashmode; + struct camera2_flash_dm flash; +#endif +#endif + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + struct fimc_is_time time; +#endif +#endif + u32 aeflashMode; /* Flash Mode Control */ +}; + +struct fimc_is_groupmgr { + struct fimc_is_group_framemgr framemgr[FIMC_IS_MAX_NODES]; + struct fimc_is_group *group[FIMC_IS_MAX_NODES][GROUP_ID_MAX]; + struct kthread_worker group_worker[GROUP_ID_MAX]; + struct task_struct *group_task[GROUP_ID_MAX]; + struct semaphore group_smp_res[GROUP_ID_MAX]; + unsigned long group_state[GROUP_ID_MAX]; + atomic_t group_refcount[GROUP_ID_MAX]; +}; + +int fimc_is_groupmgr_probe(struct fimc_is_groupmgr *groupmgr); +int fimc_is_group_probe(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + u32 entry); +int fimc_is_group_open(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 id, u32 instance, + struct fimc_is_video_ctx *vctx, + struct fimc_is_device_ischain *device, + fimc_is_start_callback start_callback); +int fimc_is_group_close(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group); +int fimc_is_group_init(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + bool otf_input, + u32 video_id); +int fimc_is_group_process_start(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue); +int fimc_is_group_process_stop(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue); +int fimc_is_group_buffer_queue(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_queue *queue, + u32 index); +int fimc_is_group_buffer_finish(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 index); +int fimc_is_group_start(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_frame *frame); +int fimc_is_group_done(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_frame *ldr_frame, + u32 done_state); + +int fimc_is_gframe_cancel(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, u32 target_fcount); + +#define PROGRAM_COUNT(count) (group->pcount = count) + +#define GET_GROUP_FRAMEMGR(group) \ + (((group) && (group)->leader.vctx) ? (&(group)->leader.vctx->q_src->framemgr) : NULL) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-hw-csi.c b/drivers/media/platform/exynos/fimc-is/fimc-is-hw-csi.c new file mode 100644 index 000000000000..18e5ed8a0d16 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-hw-csi.c @@ -0,0 +1,559 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-config.h" +#include "fimc-is-type.h" +#include "fimc-is-regs.h" +#include "fimc-is-hw.h" + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0310_0100) +#define CSI_REG_CTRL (0x00) +#define CSI_REG_DPHYCTRL (0x04) +#define CSI_REG_INTMSK (0x10) +#define CSI_REG_INTSRC (0x14) +#define CSI_REG_CONFIG0 (0x08) +#define CSI_REG_CONFIG1 (0x40) +#define CSI_REG_CONFIG2 (0x50) +#define CSI_REG_CONFIG3 (0x60) +#define CSI_REG_RESOL0 (0x2c) +#define CSI_REG_RESOL1 (0x44) +#define CSI_REG_RESOL2 (0x54) +#define CSI_REG_RESOL3 (0x64) +#define CSI_REG_DPHYCTRL0 (0x20) +#define CSI_REG_DPHYCTRL1 (0x24) +#else +/* CSIS global control */ +#define S5PCSIS_CTRL (0x00) +#define S5PCSIS_CTRL_DPDN_SWAP_CLOCK_DEFAULT (0 << 31) +#define S5PCSIS_CTRL_DPDN_SWAP_CLOCK (1 << 31) +#define S5PCSIS_CTRL_DPDN_SWAP_DATA_DEFAULT (0 << 30) +#define S5PCSIS_CTRL_DPDN_SWAP_DATA (1 << 30) +#define S5PCSIS_CTRL_INTERLEAVE_MODE(x) ((x & 0x3) << 22) +#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20) +#define S5PCSIS_CTRL_UPDATE_SHADOW(x) ((1 << (x)) << 16) +#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8) +#define S5PCSIS_CTRL_RESET (1 << 4) +#define S5PCSIS_CTRL_NUMOFDATALANE(x) ((x) << 2) +#define S5PCSIS_CTRL_ENABLE (1 << 0) + +/* D-PHY control */ +#define S5PCSIS_DPHYCTRL (0x04) +#define S5PCSIS_DPHYCTRL_DPHY_ON(lanes) ((~(0x1f << (lanes + 1))) & 0x1f) +#if defined(CONFIG_SOC_EXYNOS5260) +#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27) +#else +#define S5PCSIS_DPHYCTRL_HSS_MASK (0xff << 24) +#define S5PCSIS_DPHYCTRL_CLKSETTLEMASK (0x3 << 22) +#endif + +/* Configuration */ +#define S5PCSIS_CONFIG (0x08) +#define S5PCSIS_CONFIG_CH1 (0x40) +#define S5PCSIS_CONFIG_CH2 (0x50) +#define S5PCSIS_CONFIG_CH3 (0x60) +#define S5PCSIS_CFG_LINE_INTERVAL(x) (x << 26) +#define S5PCSIS_CFG_START_INTERVAL(x) (x << 20) +#define S5PCSIS_CFG_END_INTERVAL(x) (x << 8) +#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2) +#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2) +#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2) +#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2) +/* User defined formats, x = 1...4 */ +#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2) +#define S5PCSIS_CFG_FMT_MASK (0x3f << 2) +#define S5PCSIS_CFG_VIRTUAL_CH(x) (x << 0) +#define S5PCSIS_CFG_NR_LANE_MASK (3) + +/* Interrupt mask. */ +#define S5PCSIS_INTMSK (0x10) +#if defined(CONFIG_SOC_EXYNOS5260) +#define S5PCSIS_INTMSK_EN_ALL (0xfc00103f) +#else +#define S5PCSIS_INTMSK_EN_ALL (0xf1101117) +#endif +#define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31) +#define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30) +#define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29) +#define S5PCSIS_INTMSK_ODD_AFTER (1 << 28) +#define S5PCSIS_INTMSK_FRAME_START_CH3 (1 << 27) +#define S5PCSIS_INTMSK_FRAME_START_CH2 (1 << 26) +#define S5PCSIS_INTMSK_FRAME_START_CH1 (1 << 25) +#define S5PCSIS_INTMSK_FRAME_START_CH0 (1 << 24) +#define S5PCSIS_INTMSK_FRAME_END_CH3 (1 << 23) +#define S5PCSIS_INTMSK_FRAME_END_CH2 (1 << 22) +#define S5PCSIS_INTMSK_FRAME_END_CH1 (1 << 21) +#define S5PCSIS_INTMSK_FRAME_END_CH0 (1 << 20) +#define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 16) +#define S5PCSIS_INTMSK_ERR_LOST_FS_CH3 (1 << 15) +#define S5PCSIS_INTMSK_ERR_LOST_FS_CH2 (1 << 14) +#define S5PCSIS_INTMSK_ERR_LOST_FS_CH1 (1 << 13) +#define S5PCSIS_INTMSK_ERR_LOST_FS_CH0 (1 << 12) +#define S5PCSIS_INTMSK_ERR_LOST_FE_CH3 (1 << 11) +#define S5PCSIS_INTMSK_ERR_LOST_FE_CH2 (1 << 10) +#define S5PCSIS_INTMSK_ERR_LOST_FE_CH1 (1 << 9) +#define S5PCSIS_INTMSK_ERR_LOST_FE_CH0 (1 << 8) +#define S5PCSIS_INTMSK_ERR_OVER_CH3 (1 << 7) +#define S5PCSIS_INTMSK_ERR_OVER_CH2 (1 << 6) +#define S5PCSIS_INTMSK_ERR_OVER_CH1 (1 << 5) +#define S5PCSIS_INTMSK_ERR_OVER_CH0 (1 << 4) +#define S5PCSIS_INTMSK_ERR_ECC (1 << 2) +#define S5PCSIS_INTMSK_ERR_CRC (1 << 1) +#define S5PCSIS_INTMSK_ERR_ID (1 << 0) + +/* Interrupt source */ +#define S5PCSIS_INTSRC (0x14) +#define S5PCSIS_INTSRC_EVEN_BEFORE (1 << 31) +#define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30) +#define S5PCSIS_INTSRC_EVEN (0x3 << 30) +#define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29) +#define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) +#define S5PCSIS_INTSRC_ODD (0x3 << 28) +#define S5PCSIS_INTSRC_FRAME_START (0xf << 24) +#define S5PCSIS_INTSRC_FRAME_END (0xf << 20) +#define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 16) +#define S5PCSIS_INTSRC_ERR_LOST_FS (0xf << 12) +#define S5PCSIS_INTSRC_ERR_LOST_FE (0xf << 8) +#define S5PCSIS_INTSRC_ERR_OVER (0xf << 4) +#define S5PCSIS_INTSRC_ERR_ECC (1 << 2) +#define S5PCSIS_INTSRC_ERR_CRC (1 << 1) +#define S5PCSIS_INTSRC_ERR_ID (1 << 0) +#define S5PCSIS_INTSRC_ERRORS (0xf1111117) + +/* Pixel resolution */ +#define S5PCSIS_RESOL (0x2c) +#define CSIS_MAX_PIX_WIDTH (0xffff) +#define CSIS_MAX_PIX_HEIGHT (0xffff) +#endif + +#if (FIMC_IS_CSI_VERSION == CSI_VERSION_0310_0100) + +int csi_hw_reset(unsigned long __iomem *base_reg) +{ + int ret = 0; + u32 retry = 10; + u32 val; + + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + writel(val | (1 << 4), base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + + while (--retry) { + udelay(10); + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + if (!(val & (1 << 4))) + break; + } + + if (!retry) { + err("reset is fail(%d)", retry); + ret = -EINVAL; + goto p_err; + } + +p_err: + return ret; +} + +int csi_hw_s_settle(unsigned long __iomem *base_reg, + u32 settle) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + val = (val & ~(0xFF << 24)) | (settle << 24); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + + return 0; +} + +int csi_hw_s_dphyctrl0(unsigned long __iomem *base_reg, + u32 ctrl) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL0)); + val = (val & ~(0xFFFFFFFF << 0)) | (ctrl << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL0)); + + return 0; +} + +int csi_hw_s_dphyctrl1(unsigned long __iomem *base_reg, + u32 ctrl) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL1)); + val = (val & ~(0xFFFFFFFF << 0)) | (ctrl << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL1)); + + return 0; +} + +int csi_hw_s_control(unsigned long __iomem *base_reg, + u32 pixelformat, u32 mode, u32 lanes) +{ + int ret = 0; + u32 val; + + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + val = (val & ~(0x3 << 2)) | (lanes << 2); + val = (val & ~(0x3 << 22)) | (mode << 22); + + /* all channel use extclk for wrapper clock source */ + val |= (0xF << 8); + + switch (pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_JPEG: + /* output width of CH0 is not 32 bits(normal output) */ + val &= ~(1 << 20); + break; + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUYV: + /* output width of CH0 is 32 bits(32bit align) */ + val |= (1 << 20); + break; + default: + err("unsupported format(%X)", pixelformat); + ret = -EINVAL; + goto p_err; + break; + } + + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + +p_err: + return ret; +} + +int csi_hw_s_config(unsigned long __iomem *base_reg, + u32 vc_src, u32 vc_dst, u32 pixelformat, u32 width, u32 height) +{ + int ret = 0; + u32 val, format; + + switch (pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + format = HW_FORMAT_RAW8; + break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + format = HW_FORMAT_RAW10; + break; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + format = HW_FORMAT_RAW10; + /* HACK : format = HW_FORMAT_RAW12; */ + break; + case V4L2_PIX_FMT_SBGGR16: + format = HW_FORMAT_RAW10; + /* HACK : format = HW_FORMAT_RAW12; */ + break; + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_NV21: + format = HW_FORMAT_YUV420_8BIT; + break; + case V4L2_PIX_FMT_YUYV: + format = HW_FORMAT_YUV422_8BIT; + break; + case V4L2_PIX_FMT_JPEG: + format = HW_FORMAT_USER; + break; + default: + err("unsupported format(%X)", pixelformat); + ret = -EINVAL; + goto p_err; + break; + } + + switch (vc_src) { + case CSI_VIRTUAL_CH_0: + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG0)); + val = (val & ~(0x3 << 0)) | (vc_dst << 0); + val = (val & ~(0x3f << 2)) | (format << 2); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG0)); + + val = (width << 16) | (height << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_RESOL0)); + break; + case CSI_VIRTUAL_CH_1: + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG1)); + val = (val & ~(0x3 << 0)) | (vc_dst << 0); + val = (val & ~(0x3f << 2)) | (format << 2); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG1)); + + val = (width << 16) | (height << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_RESOL1)); + break; + case CSI_VIRTUAL_CH_2: + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG2)); + val = (val & ~(0x3 << 0)) | (vc_dst << 0); + val = (val & ~(0x3f << 2)) | (format << 2); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG2)); + + val = (width << 16) | (height << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_RESOL2)); + break; + case CSI_VIRTUAL_CH_3: + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG3)); + val = (val & ~(0x3 << 0)) | (vc_dst << 0); + val = (val & ~(0x3f << 2)) | (format << 2); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CONFIG3)); + + val = (width << 16) | (height << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_RESOL3)); + break; + default: + err("invalid channel(%d)", vc_src); + ret = -EINVAL; + goto p_err; + break; + } + +p_err: + return ret; +} + +int csi_hw_s_interrupt(unsigned long __iomem *base_reg, bool on) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_INTMSK)); + val = on ? (val | 0xFFF1FFF7) : (val & ~0xFFF1FFF7); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_INTMSK)); + + return 0; +} + +int csi_hw_g_interrupt(unsigned long __iomem *base_reg) +{ + u32 val; + + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_INTSRC)); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_INTSRC)); + + return val; +} + +int csi_hw_enable(unsigned long __iomem *base_reg) +{ + u32 val; + + /* update shadow */ + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + val |= (0xF << 16); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + + /* DPHY on */ + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + val |= (0x1f << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + + /* csi enable */ + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + val |= (0x1 << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + + return 0; +} + +int csi_hw_disable(unsigned long __iomem *base_reg) +{ + u32 val; + + /* DPHY on */ + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + val &= ~(0x1f << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_DPHYCTRL)); + + /* csi enable */ + val = readl(base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + val &= ~(0x1 << 0); + writel(val, base_reg + TO_WORD_OFFSET(CSI_REG_CTRL)); + + return 0; +} + +#else + +void s5pcsis_enable_interrupts(unsigned long __iomem *base_reg, + struct fimc_is_image *image, bool on) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_INTMSK)); + + val = on ? val | S5PCSIS_INTMSK_EN_ALL : + val & ~S5PCSIS_INTMSK_EN_ALL; + + if (image->format.field == V4L2_FIELD_INTERLACED) { + if (on) { + val |= S5PCSIS_INTMSK_FRAME_START_CH2; + val |= S5PCSIS_INTMSK_FRAME_END_CH2; + } else { + val &= ~S5PCSIS_INTMSK_FRAME_START_CH2; + val &= ~S5PCSIS_INTMSK_FRAME_END_CH2; + } + } + +#if defined(CONFIG_SOC_EXYNOS5260) + /* FIXME: hard coded, only for rhea */ + writel(0xFFF01037, base_reg + TO_WORD_OFFSET(S5PCSIS_INTMSK)); +#else + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_INTMSK)); +#endif +} + +void s5pcsis_reset(unsigned long __iomem *base_reg) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + + writel(val | S5PCSIS_CTRL_RESET, base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + udelay(10); +} + +void s5pcsis_system_enable(unsigned long __iomem *base_reg, int on, u32 lanes) +{ + u32 val; + + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) || defined(CONFIG_SOC_EXYNOS5422) + val |= S5PCSIS_CTRL_WCLK_EXTCLK; +#endif + + if (on) { + val |= S5PCSIS_CTRL_ENABLE; + val |= S5PCSIS_CTRL_WCLK_EXTCLK; + } else + val &= ~S5PCSIS_CTRL_ENABLE; +#if defined(CONFIG_SOC_EXYNOS5260) + /* FIXME: hard coded, only for rhea */ + writel(0x0000010D, base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); +#else + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); +#endif + + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); + if (on) + val |= S5PCSIS_DPHYCTRL_DPHY_ON(lanes); + else + val &= ~S5PCSIS_DPHYCTRL_DPHY_ON(lanes); +#if defined(CONFIG_SOC_EXYNOS5260) + /* FIXME: hard coded, only for rhea */ + writel(0x0E00001F, base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); +#else + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); +#endif +} + +/* Called with the state.lock mutex held */ +static void __s5pcsis_set_format(unsigned long __iomem *base_reg, + struct fimc_is_image *image) +{ + u32 val; + + BUG_ON(!image); + + /* Color format */ + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CONFIG)); + + if (image->format.pixelformat == V4L2_PIX_FMT_SGRBG8) + val = (val & ~S5PCSIS_CFG_FMT_MASK) | S5PCSIS_CFG_FMT_RAW8; + else + val = (val & ~S5PCSIS_CFG_FMT_MASK) | S5PCSIS_CFG_FMT_RAW10; + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) || defined(CONFIG_SOC_EXYNOS5422) + val |= S5PCSIS_CFG_END_INTERVAL(1); +#endif + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_CONFIG)); + + /* Pixel resolution */ + val = (image->window.o_width << 16) | image->window.o_height; + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_RESOL)); + + /* Output channel2 for DT */ + if (image->format.field == V4L2_FIELD_INTERLACED) { + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CONFIG_CH2)); + val |= S5PCSIS_CFG_VIRTUAL_CH(2); + val |= S5PCSIS_CFG_END_INTERVAL(1); + val = (val & ~S5PCSIS_CFG_FMT_MASK) | S5PCSIS_CFG_FMT_USER(1); + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_CONFIG_CH2)); + } +} + +void s5pcsis_set_hsync_settle(unsigned long __iomem *base_reg, u32 settle) +{ + u32 val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); + + val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 24); + +#if defined(CONFIG_SOC_EXYNOS5260) + /* FIXME: hard coded, only for rhea */ + writel(0x0E00001F, base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); +#else + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_DPHYCTRL)); +#endif +} + +void s5pcsis_set_params(unsigned long __iomem *base_reg, + struct fimc_is_image *image, u32 lanes) +{ + u32 val; + +#if defined(CONFIG_SOC_EXYNOS3470) + writel(0x000000AC, base_reg + TO_WORD_OFFSET(S5PCSIS_CONFIG)); /* only for carmen */ +#endif + __s5pcsis_set_format(base_reg, image); + + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + val &= ~S5PCSIS_CTRL_ALIGN_32BIT; + + val |= S5PCSIS_CTRL_NUMOFDATALANE(lanes); + + /* Interleaved data */ + if (image->format.field == V4L2_FIELD_INTERLACED) { + pr_info("set DT only\n"); + val |= S5PCSIS_CTRL_INTERLEAVE_MODE(1); /* DT only */ + val |= S5PCSIS_CTRL_UPDATE_SHADOW(2); /* ch2 shadow reg */ + } + + /* Not using external clock. */ + val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; + + writel(val, base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + + /* Update the shadow register. */ + val = readl(base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); + writel(val | S5PCSIS_CTRL_UPDATE_SHADOW(0), base_reg + TO_WORD_OFFSET(S5PCSIS_CTRL)); +} +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-hw-ischain.c b/drivers/media/platform/exynos/fimc-is/fimc-is-hw-ischain.c new file mode 100644 index 000000000000..b43853725a25 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-hw-ischain.c @@ -0,0 +1,486 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fimc-is-config.h" +#include "fimc-is-type.h" +#include "fimc-is-regs.h" +#include "fimc-is-core.h" +#include "fimc-is-dvfs.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) +#define PM_QOS_CAM_THROUGHPUT PM_QOS_RESERVED +#endif +extern struct pm_qos_request exynos_isp_qos_cpu_min; +extern struct pm_qos_request exynos_isp_qos_cpu_max; +extern struct pm_qos_request exynos_isp_qos_int; +extern struct pm_qos_request exynos_isp_qos_mem; +extern struct pm_qos_request exynos_isp_qos_cam; +extern struct pm_qos_request exynos_isp_qos_disp; +#if defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +extern struct pm_qos_request max_cpu_qos; +#endif + +#if defined(CONFIG_ARGOS) +extern void argos_block_enable(char *req_name, bool set); +#endif + +#if defined(CONFIG_PM_DEVFREQ) +inline static void fimc_is_set_qos_init(struct fimc_is_core *core, bool on) +{ + int cpu_min_qos, cpu_max_qos, int_qos, mif_qos, cam_qos, disp_qos; + + cpu_min_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CPU_MIN, START_DVFS_LEVEL); + cpu_max_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CPU_MAX, START_DVFS_LEVEL); + int_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_INT, START_DVFS_LEVEL); + mif_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_MIF, START_DVFS_LEVEL); + cam_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CAM, START_DVFS_LEVEL); + disp_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_DISP, START_DVFS_LEVEL); + + core->resourcemgr.dvfs_ctrl.cur_cpu_min_qos = cpu_min_qos; + core->resourcemgr.dvfs_ctrl.cur_cpu_max_qos = cpu_max_qos; + core->resourcemgr.dvfs_ctrl.cur_int_qos = int_qos; + core->resourcemgr.dvfs_ctrl.cur_mif_qos = mif_qos; + core->resourcemgr.dvfs_ctrl.cur_cam_qos = cam_qos; + core->resourcemgr.dvfs_ctrl.cur_disp_qos = disp_qos; + + if (on) { + /* DEVFREQ lock */ + if (cpu_min_qos > 0) + pm_qos_add_request(&exynos_isp_qos_cpu_min, PM_QOS_CPU_FREQ_MIN, cpu_min_qos); + if (cpu_max_qos > 0) + pm_qos_add_request(&exynos_isp_qos_cpu_max, PM_QOS_CPU_FREQ_MAX, cpu_max_qos); + if (int_qos > 0) + pm_qos_add_request(&exynos_isp_qos_int, PM_QOS_DEVICE_THROUGHPUT, int_qos); + if (mif_qos > 0) + pm_qos_add_request(&exynos_isp_qos_mem, PM_QOS_BUS_THROUGHPUT, mif_qos); + if (cam_qos > 0) + pm_qos_add_request(&exynos_isp_qos_cam, PM_QOS_CAM_THROUGHPUT, cam_qos); + if (disp_qos > 0) + pm_qos_add_request(&exynos_isp_qos_disp, PM_QOS_DISPLAY_THROUGHPUT, disp_qos); + + pr_info("[RSC] %s: QoS LOCK [INT(%d), MIF(%d), CAM(%d), DISP(%d) CPU(%d/%d)]\n", + __func__, int_qos, mif_qos, cam_qos, disp_qos, cpu_min_qos, cpu_max_qos); + } else { + /* DEVFREQ unlock */ + if (cpu_min_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_cpu_min); + if (cpu_max_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_cpu_max); + if (int_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_int); + if (mif_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_mem); + if (cam_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_cam); + if (disp_qos > 0) + pm_qos_remove_request(&exynos_isp_qos_disp); + + pr_info("[RSC] %s: QoS UNLOCK\n", __func__); + } +} +#endif + + +#if (FIMC_IS_VERSION == FIMC_IS_VERSION_250) +int fimc_is_runtime_suspend_post(struct device *dev) +{ + int ret = 0; + u32 timeout; + + timeout = 1000; + while ((readl(PMUREG_ISP0_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + } + if (timeout == 0) + err("ISP0 power down failed(0x%08x)\n", readl(PMUREG_ISP0_STATUS)); + + timeout = 1000; + while ((readl(PMUREG_ISP1_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + } + if (timeout == 0) + err("ISP0 power down failed(0x%08x)\n", readl(PMUREG_ISP1_STATUS)); + + return ret; +} + +int fimc_is_runtime_suspend(struct device *dev) +{ +#ifndef CONFIG_PM_RUNTIME + int ret = 0; + u32 val; +#endif + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(pdev); + + BUG_ON(!core); + BUG_ON(!core->pdata); + BUG_ON(!core->pdata->clk_off); + + info("FIMC_IS runtime suspend in\n"); + +#if defined(CONFIG_VIDEOBUF2_ION) + if (core->mem.alloc_ctx) + vb2_ion_detach_iommu(core->mem.alloc_ctx); +#endif + +#if defined(CONFIG_FIMC_IS_BUS_DEVFREQ) + exynos5_update_media_layers(TYPE_FIMC_LITE, false); +#endif + +#ifndef CONFIG_PM_RUNTIME + /* ISP1 */ + /* 1. set internal clock reset */ + val = __raw_readl(PMUREG_CMU_RESET_ISP1_SYS_PWR); + val = (val & ~(0x1 << 0)) | (0x0 << 0); + __raw_writel(val, PMUREG_CMU_RESET_ISP1_SYS_PWR); + + /* 2. change to OSCCLK */ + ret = core->pdata->clk_off(pdev); + if (ret) + warn("clk_off is fail(%d)", ret); + + /* 3. set feedback mode */ + val = __raw_readl(PMUREG_ISP1_OPTION); + val = (val & ~(0x3 << 0)) | (0x2 << 0); + __raw_writel(val, PMUREG_ISP1_OPTION); + + /* 4. power off */ + val = __raw_readl(PMUREG_ISP1_CONFIGURATION); + val = (val & ~(0x7 << 0)) | (0x0 << 0); + __raw_writel(val, PMUREG_ISP1_CONFIGURATION); + + /* ISP0 */ + /* 1. set internal clock reset */ + val = __raw_readl(PMUREG_CMU_RESET_ISP0_SYS_PWR); + val = (val & ~(0x1 << 0)) | (0x0 << 0); + __raw_writel(val, PMUREG_CMU_RESET_ISP0_SYS_PWR); + + /* 2. set standbywfi a5 */ + val = __raw_readl(PMUREG_CENTRAL_SEQ_OPTION); + val = (val & ~(0x1 << 18)) | (0x1 << 18); + __raw_writel(val, PMUREG_CENTRAL_SEQ_OPTION); + + /* 3. stop a5 */ + __raw_writel(0x00010000, PMUREG_ISP_ARM_OPTION); + + /* 4. reset a5 */ + val = __raw_readl(PMUREG_ISP_ARM_SYS_PWR_REG); + val = (val & ~(0x1 << 0)) | (0x1 << 0); + __raw_writel(val, PMUREG_ISP_ARM_SYS_PWR_REG); + + /* 5. change to OSCCLK */ + + /* 6. set feedback mode */ + val = __raw_readl(PMUREG_ISP0_OPTION); + val = (val & ~(0x3 << 0)) | (0x2 << 0); + __raw_writel(val, PMUREG_ISP0_OPTION); + + /* 7. power off */ + val = __raw_readl(PMUREG_ISP0_CONFIGURATION); + val = (val & ~(0x7 << 0)) | (0x0 << 0); + __raw_writel(val, PMUREG_ISP0_CONFIGURATION); + + /* 8. a5 power off */ + val = __raw_readl(PMUREG_ISP_ARM_CONFIGURATION); + val = (val & ~(0x1 << 0)) | (0x0 << 0); + __raw_writel(val, PMUREG_ISP_ARM_CONFIGURATION); +#endif + +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ release */ + fimc_is_set_qos_init(core, false); +#endif + +#ifdef CONFIG_PM_RUNTIME + if (CALL_POPS(core, clk_off, pdev) < 0) + warn("clk_off is fail\n"); +#endif + + info("FIMC_IS runtime suspend out\n"); + pm_relax(dev); + return 0; +} + +int fimc_is_runtime_resume(struct device *dev) +{ + int ret = 0; + u32 val; + + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(pdev); + + BUG_ON(!core); + BUG_ON(!core->pdata); + BUG_ON(!core->pdata->clk_cfg); + BUG_ON(!core->pdata->clk_on); + + info("FIMC_IS runtime resume in\n"); + + val = __raw_readl(PMUREG_ISP0_STATUS); + if((val & 0x7) != 0x7){ + err("FIMC_IS runtime resume ISP0 : %d Power down\n",val); + BUG(); + } + + val = __raw_readl(PMUREG_ISP1_STATUS); + if((val & 0x7) != 0x7){ + err("FIMC_IS runtime resume ISP1 : %d Power down\n",val); + BUG(); + } + +#ifndef CONFIG_PM_RUNTIME + /* ISP0 */ + /* 1. set feedback mode */ + val = __raw_readl(PMUREG_ISP0_OPTION); + val = (val & ~(0x3<< 0)) | (0x2 << 0); + __raw_writel(val, PMUREG_ISP0_OPTION); + + /* 2. power on isp0 */ + val = __raw_readl(PMUREG_ISP0_CONFIGURATION); + val = (val & ~(0x7 << 0)) | (0x7 << 0); + __raw_writel(val, PMUREG_ISP0_CONFIGURATION); + + /* ISP1 */ + /* 3. set feedback mode */ + val = __raw_readl(PMUREG_ISP1_OPTION); + val = (val & ~(0x3<< 0)) | (0x2 << 0); + __raw_writel(val, PMUREG_ISP1_OPTION); + + /* 4. power on isp1 */ + val = __raw_readl(PMUREG_ISP1_CONFIGURATION); + val = (val & ~(0x7 << 0)) | (0x7 << 0); + __raw_writel(val, PMUREG_ISP1_CONFIGURATION); +#endif + + ret = core->pdata->clk_cfg(pdev); + if (ret) { + err("clk_cfg is fail(%d)", ret); + goto p_err; + } + + /* HACK: DVFS lock sequence is change. + * DVFS level should be locked after power on. + */ +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ set */ + fimc_is_set_qos_init(core, true); +#endif + + /* Clock on */ + ret = core->pdata->clk_on(pdev); + if (ret) { + err("clk_on is fail(%d)", ret); + goto p_err; + } + +#if defined(CONFIG_VIDEOBUF2_ION) + if (core->mem.alloc_ctx) + vb2_ion_attach_iommu(core->mem.alloc_ctx); +#endif + +#if defined(CONFIG_FIMC_IS_BUS_DEVFREQ) + exynos5_update_media_layers(TYPE_FIMC_LITE, true); +#endif + + pm_stay_awake(dev); + +p_err: + info("FIMC-IS runtime resume out\n"); + return ret; +} + +#else +int fimc_is_runtime_suspend_post(struct device *dev) +{ + int ret = 0; + u32 timeout; + + timeout = 2000; + while ((readl(PMUREG_ISP_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + } + if (timeout == 0) + err("ISP power down failed(0x%08x)\n", + readl(PMUREG_ISP_STATUS)); + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + timeout = 1000; + while ((readl(PMUREG_CAM0_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + } + if (timeout == 0) + err("CAM0 power down failed(0x%08x)\n", + readl(PMUREG_CAM0_STATUS)); + + timeout = 2000; + while ((readl(PMUREG_CAM1_STATUS) & 0x1) && timeout) { + timeout--; + usleep_range(1000, 1000); + } + if (timeout == 0) + err("CAM1 power down failed(CAM1:0x%08x, A5:0x%08x)\n", + readl(PMUREG_CAM1_STATUS), readl(PMUREG_ISP_ARM_STATUS)); +#endif /* defined(CONFIG_SOC_EXYNOS5430) */ + +#if defined(CONFIG_SOC_EXYNOS5422) +#endif /* defined(CONFIG_SOC_EXYNOS5422) */ + + return ret; +} + +int fimc_is_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_core *core + = (struct fimc_is_core *)platform_get_drvdata(pdev); + + BUG_ON(!core); + BUG_ON(!core->pdata); + BUG_ON(!core->pdata->clk_off); + + pr_info("FIMC_IS runtime suspend in\n"); + +#if !(defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)) +#if defined(CONFIG_VIDEOBUF2_ION) + if (core->mem.alloc_ctx) + vb2_ion_detach_iommu(core->mem.alloc_ctx); +#endif +#endif + +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ set */ + fimc_is_set_qos_init(core, false); +#endif + +#if defined(CONFIG_ARGOS) + argos_block_enable("EMMC", false); +#endif + +#if defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) + /* EGL Release */ + pm_qos_update_request(&max_cpu_qos, PM_QOS_CPU_FREQ_MAX_DEFAULT_VALUE); + pm_qos_remove_request(&max_cpu_qos); +#endif /* CONFIG_SOC_EXYNOS5422 */ + +#if defined(CONFIG_FIMC_IS_BUS_DEVFREQ) + /* BTS */ +#if defined(CONFIG_SOC_EXYNOS5260) + bts_initialize("spd-flite-a", false); + bts_initialize("spd-flite-b", false); +#elif defined(CONFIG_SOC_EXYNOS3470) + bts_initialize("pd-cam", false); +#else + bts_initialize("pd-fimclite", false); +#endif + /* media layer */ + exynos5_update_media_layers(TYPE_FIMC_LITE, false); +#endif /* CONFIG_FIMC_IS_BUS_DEVFREQ */ + + if (CALL_POPS(core, clk_off, pdev) < 0) + warn("clk_off is fail\n"); + + pr_info("FIMC_IS runtime suspend out\n"); + + pm_relax(dev); + return 0; +} + +int fimc_is_runtime_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_core *core + = (struct fimc_is_core *)platform_get_drvdata(pdev); + + pm_stay_awake(dev); + pr_info("FIMC_IS runtime resume in\n"); + +#if defined(CONFIG_ARGOS) + argos_block_enable("EMMC", true); +#endif + +#if defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS5430) + /* EGL Lock */ + pm_qos_add_request(&max_cpu_qos, PM_QOS_CPU_FREQ_MAX, 1600000); +#elif defined(CONFIG_SOC_EXYNOS5433) + /* EGL Lock */ + pm_qos_add_request(&max_cpu_qos, PM_QOS_CPU_FREQ_MAX, 1700000); +#endif /* CONFIG_SOC_EXYNOS5422 */ + + /* HACK: DVFS lock sequence is change. + * DVFS level should be locked after power on. + */ +#if defined(CONFIG_PM_DEVFREQ) + /* DEVFREQ set */ + fimc_is_set_qos_init(core, true); +#endif + + /* Low clock setting */ + if (CALL_POPS(core, clk_cfg, core->pdev) < 0) { + err("clk_cfg is fail\n"); + ret = -EINVAL; + goto p_err; + } + + /* Clock on */ + if (CALL_POPS(core, clk_on, core->pdev) < 0) { + err("clk_on is fail\n"); + ret = -EINVAL; + goto p_err; + } +#if !(defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)) +#if defined(CONFIG_VIDEOBUF2_ION) + if (core->mem.alloc_ctx) + vb2_ion_attach_iommu(core->mem.alloc_ctx); +#endif +#endif + +#if defined(CONFIG_FIMC_IS_BUS_DEVFREQ) + /* BTS */ +#if defined(CONFIG_SOC_EXYNOS5260) + bts_initialize("spd-flite-a", true); + bts_initialize("spd-flite-b", true); +#elif defined(CONFIG_SOC_EXYNOS3470) + bts_initialize("pd-cam", true); +#else + bts_initialize("pd-fimclite", true); +#endif + /* media layer */ + exynos5_update_media_layers(TYPE_FIMC_LITE, true); +#endif /* CONFIG_FIMC_IS_BUS_DEVFREQ */ + + pr_info("FIMC-IS runtime resume out\n"); + + return 0; + +p_err: + pm_relax(dev); + return ret; +} +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-hw.h b/drivers/media/platform/exynos/fimc-is/fimc-is-hw.h new file mode 100644 index 000000000000..8a756719a429 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-hw.h @@ -0,0 +1,63 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_HW_H +#define FIMC_IS_HW_H + +#define CSI_VIRTUAL_CH_0 0 +#define CSI_VIRTUAL_CH_1 1 +#define CSI_VIRTUAL_CH_2 2 +#define CSI_VIRTUAL_CH_3 3 +#define CSI_VIRTUAL_CH_MAX 4 + +#define CSI_DATA_LANES_1 0 +#define CSI_DATA_LANES_2 1 +#define CSI_DATA_LANES_3 2 +#define CSI_DATA_LANES_4 3 + +#define CSI_MODE_CH0_ONLY 0 +#define CSI_MODE_DT_ONLY 1 +#define CSI_MODE_VC_ONLY 2 +#define CSI_MODE_VC_DT 3 + +#define HW_FORMAT_YUV420_8BIT 0x18 +#define HW_FORMAT_YUV420_10BIT 0x19 +#define HW_FORMAT_YUV422_8BIT 0x1E +#define HW_FORMAT_YUV422_10BIT 0x1F +#define HW_FORMAT_RGB565 0x22 +#define HW_FORMAT_RGB666 0x23 +#define HW_FORMAT_RGB888 0x24 +#define HW_FORMAT_RAW6 0x28 +#define HW_FORMAT_RAW7 0x29 +#define HW_FORMAT_RAW8 0x2A +#define HW_FORMAT_RAW10 0x2B +#define HW_FORMAT_RAW12 0x2C +#define HW_FORMAT_RAW14 0x2D +#define HW_FORMAT_USER 0x30 + +struct fimc_is_vci { + u32 pixelformat; + u32 vc_map[CSI_VIRTUAL_CH_MAX]; +}; + +int csi_hw_reset(unsigned long __iomem *base_reg); +int csi_hw_s_settle(unsigned long __iomem *base_reg, u32 settle); +int csi_hw_s_control(unsigned long __iomem *base_reg, u32 pixelformat, u32 mode, u32 lanes); +int csi_hw_s_config(unsigned long __iomem *base_reg, u32 vc_src, u32 vc_dst, u32 pixelformat, u32 width, u32 height); +int csi_hw_s_interrupt(unsigned long __iomem *base_reg, bool on); +int csi_hw_g_interrupt(unsigned long __iomem *base_reg); +int csi_hw_enable(unsigned long __iomem *base_reg); +int csi_hw_disable(unsigned long __iomem *base_reg); + +int fimc_is_runtime_suspend_post(struct device *dev); +int fimc_is_runtime_suspend(struct device *dev); +int fimc_is_runtime_resume(struct device *dev); +#endif \ No newline at end of file diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-interface.c b/drivers/media/platform/exynos/fimc-is/fimc-is-interface.c new file mode 100644 index 000000000000..e7ad6a899622 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-interface.c @@ -0,0 +1,3213 @@ +/* + * drivers/media/video/exynos/fimc-is-mc2/fimc-is-interface.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * The header file related to camera + * + * 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 +#include + +#include "fimc-is-core.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-groupmgr.h" + +#include "fimc-is-interface.h" +#include "fimc-is-clk-gate.h" + +u32 __iomem *notify_fcount_sen0; +u32 __iomem *notify_fcount_sen1; +u32 __iomem *notify_fcount_sen2; +u32 __iomem *last_fcount0; +u32 __iomem *last_fcount1; + +#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); +#define init_process_barrier(itf) spin_lock_init(&itf->process_barrier); +#define enter_process_barrier(itf) spin_lock_irq(&itf->process_barrier); +#define exit_process_barrier(itf) spin_unlock_irq(&itf->process_barrier); + +extern struct fimc_is_sysfs_debug sysfs_debug; + +/* func to register error report callback */ +int fimc_is_set_err_report_vendor(struct fimc_is_interface *itf, + void *err_report_data, + int (*err_report_vendor)(void *data, u32 err_report_type)) +{ + if (itf) { + itf->err_report_data = err_report_data; + itf->err_report_vendor = err_report_vendor; + } + + return 0; +} + +/* main func to handle error report */ +static int fimc_is_err_report_handler(struct fimc_is_interface *itf, struct fimc_is_msg *msg) +{ + struct fimc_is_device_ischain *device; + struct fimc_is_core *core; + unsigned long dtp_wait_time; + + core = itf->core; + device = &core->ischain[msg->instance]; + dtp_wait_time = device->sensor->dtp_timer.expires; + + err("IHC_REPORT_ERR(%d,%d,%d,%d,%d) is occured", + msg->instance, + msg->group, + msg->parameter1, + msg->parameter2, + msg->parameter3); + + /* + * if vendor error report func was registered, + * call the func. + */ + if (itf->err_report_vendor) + itf->err_report_vendor(itf->err_report_data, msg->parameter2); + + switch (msg->parameter2) { + case REPORT_ERR_CIS_ID: + warn("Occured the ERR_ID"); + break; + case REPORT_ERR_CIS_ECC: + warn("Occured the ERR_ECC"); + case REPORT_ERR_CIS_CRC: + warn("Occured the ERR_CRC"); +#ifdef ENABLE_DTP + if (dtp_wait_time >= jiffies) + set_bit(FIMC_IS_BAD_FRAME_STOP, &device->sensor->force_stop); +#endif + break; + case REPORT_ERR_CIS_OVERFLOW_VC0: + warn("Occured the OVERFLOW_VC0"); + break; + case REPORT_ERR_CIS_LOST_FE_VC0: + warn("Occured the LOST_FE_VC0"); + break; + case REPORT_ERR_CIS_LOST_FS_VC0: + warn("Occured the LOST_FS_VC0"); + break; + case REPORT_ERR_CIS_SOT_VC0: + warn("Occured the SOT_VC0"); + break; + case REPORT_ERR_CIS_SOT_VC1: + warn("Occured the SOT_VC1"); + break; + case REPORT_ERR_CIS_SOT_VC2: + warn("Occured the SOT_VC2"); + break; + case REPORT_ERR_CIS_SOT_VC3: + warn("Occured the SOT_VC3"); + break; + case REPORT_ERR_3AA_OVERFLOW: + warn("Occured the 3AA_OVERFLOW"); + break; + case REPORT_ERR_FLITE_D_OVERFLOW: + warn("Occured the FLITE_D_OVERFLOW"); + break; + case REPORT_ERR_COMPANION_LOST_FRAME: + warn("Occured the COMPANION_LOST_FRAME"); + break; + case REPORT_ERR_3A_ALGORITHM_DELAYED: + warn("Occured the 3A_ALGORITHM_DELAYED"); + break; + + default: + warn("parameter is default"); + break; + } + + return 0; +} + +int print_fre_work_list(struct fimc_is_work_list *this) +{ + struct list_head *temp; + struct fimc_is_work *work; + + if (!(this->id & TRACE_WORK_ID_MASK)) + return 0; + + printk(KERN_ERR "[INF] fre(%02X, %02d) :", + this->id, this->work_free_cnt); + + list_for_each(temp, &this->work_free_head) { + work = list_entry(temp, struct fimc_is_work, list); + printk(KERN_CONT "%X(%d)->", work->msg.command, work->fcount); + } + + printk(KERN_CONT "X\n"); + + return 0; +} + +static int set_free_work(struct fimc_is_work_list *this, + struct fimc_is_work *work) +{ + int ret = 0; + unsigned long flags; + + if (work) { + spin_lock_irqsave(&this->slock_free, flags); + + list_add_tail(&work->list, &this->work_free_head); + this->work_free_cnt++; +#ifdef TRACE_WORK + print_fre_work_list(this); +#endif + + spin_unlock_irqrestore(&this->slock_free, flags); + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +static int get_free_work(struct fimc_is_work_list *this, + struct fimc_is_work **work) +{ + int ret = 0; + unsigned long flags; + + if (work) { + spin_lock_irqsave(&this->slock_free, flags); + + if (this->work_free_cnt) { + *work = container_of(this->work_free_head.next, + struct fimc_is_work, list); + list_del(&(*work)->list); + this->work_free_cnt--; + } else + *work = NULL; + + spin_unlock_irqrestore(&this->slock_free, flags); + } else { + ret = -EFAULT; + err("item is null ptr"); + } + + return ret; +} + +static int get_free_work_irq(struct fimc_is_work_list *this, + struct fimc_is_work **work) +{ + int ret = 0; + + if (work) { + spin_lock(&this->slock_free); + + if (this->work_free_cnt) { + *work = container_of(this->work_free_head.next, + struct fimc_is_work, list); + list_del(&(*work)->list); + this->work_free_cnt--; + } else + *work = NULL; + + spin_unlock(&this->slock_free); + } else { + ret = -EFAULT; + err("item is null ptr"); + } + + return ret; +} + +int print_req_work_list(struct fimc_is_work_list *this) +{ + struct list_head *temp; + struct fimc_is_work *work; + + if (!(this->id & TRACE_WORK_ID_MASK)) + return 0; + + printk(KERN_ERR "[INF] req(%02X, %02d) :", + this->id, this->work_request_cnt); + + list_for_each(temp, &this->work_request_head) { + work = list_entry(temp, struct fimc_is_work, list); + printk(KERN_CONT "%X(%d)->", work->msg.command, work->fcount); + } + + printk(KERN_CONT "X\n"); + + return 0; +} + +static int print_work_data_state(struct fimc_is_interface *this) +{ + struct work_struct *work; + unsigned long *bits = NULL; + + work = &this->work_wq[INTR_GENERAL]; + bits = (work_data_bits(work)); + info("INTR_GENERAL wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_3A0C_FDONE]; + bits = (work_data_bits(work)); + info("INTR_3A0C_FDONE wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_3A1C_FDONE]; + bits = (work_data_bits(work)); + info("INTR_3A1C_FDONE wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_SCC_FDONE]; + bits = (work_data_bits(work)); + info("INTR_SCC_FDONE wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_DIS_FDONE]; + bits = (work_data_bits(work)); + info("INTR_DIS_FDONE wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_SCP_FDONE]; + bits = (work_data_bits(work)); + info("INTR_SCP_FDONE wq state : (0x%lx)", *bits); + work = &this->work_wq[INTR_SHOT_DONE]; + bits = (work_data_bits(work)); + info("INTR_SHOT_DONE wq state : (0x%lx)", *bits); + + return 0; +} + +static int set_req_work(struct fimc_is_work_list *this, + struct fimc_is_work *work) +{ + int ret = 0; + unsigned long flags; + + if (work) { + spin_lock_irqsave(&this->slock_request, flags); + + list_add_tail(&work->list, &this->work_request_head); + this->work_request_cnt++; +#ifdef TRACE_WORK + print_req_work_list(this); +#endif + + spin_unlock_irqrestore(&this->slock_request, flags); + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +static int set_req_work_irq(struct fimc_is_work_list *this, + struct fimc_is_work *work) +{ + int ret = 0; + + if (work) { + spin_lock(&this->slock_request); + + list_add_tail(&work->list, &this->work_request_head); + this->work_request_cnt++; +#ifdef TRACE_WORK + print_req_work_list(this); +#endif + + spin_unlock(&this->slock_request); + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +static int get_req_work(struct fimc_is_work_list *this, + struct fimc_is_work **work) +{ + int ret = 0; + unsigned long flags; + + if (work) { + spin_lock_irqsave(&this->slock_request, flags); + + if (this->work_request_cnt) { + *work = container_of(this->work_request_head.next, + struct fimc_is_work, list); + list_del(&(*work)->list); + this->work_request_cnt--; + } else + *work = NULL; + + spin_unlock_irqrestore(&this->slock_request, flags); + } else { + ret = -EFAULT; + err("item is null ptr\n"); + } + + return ret; +} + +static void init_work_list(struct fimc_is_work_list *this, u32 id, u32 count) +{ + u32 i; + + this->id = id; + this->work_free_cnt = 0; + this->work_request_cnt = 0; + INIT_LIST_HEAD(&this->work_free_head); + INIT_LIST_HEAD(&this->work_request_head); + spin_lock_init(&this->slock_free); + spin_lock_init(&this->slock_request); + for (i = 0; i < count; ++i) + set_free_work(this, &this->work[i]); + + init_waitqueue_head(&this->wait_queue); +} + +static int set_busystate(struct fimc_is_interface *this, + u32 command) +{ + int ret; + + ret = test_and_set_bit(IS_IF_STATE_BUSY, &this->state); + if (ret) + warn("%d command : busy state is already set", command); + + return ret; +} + +static int clr_busystate(struct fimc_is_interface *this, + u32 command) +{ + int ret; + + ret = test_and_clear_bit(IS_IF_STATE_BUSY, &this->state); + if (!ret) + warn("%d command : busy state is already clr", command); + + return !ret; +} + +static int test_busystate(struct fimc_is_interface *this) +{ + int ret = 0; + + ret = test_bit(IS_IF_STATE_BUSY, &this->state); + + return ret; +} + +static int wait_lockstate(struct fimc_is_interface *this) +{ + int ret = 0; + + ret = wait_event_timeout(this->lock_wait_queue, + !atomic_read(&this->lock_pid), FIMC_IS_COMMAND_TIMEOUT); + if (ret) { + ret = 0; + } else { + err("timeout"); + ret = -ETIME; + } + + return ret; +} + +static int wait_idlestate(struct fimc_is_interface *this) +{ + int ret = 0; + + ret = wait_event_timeout(this->idle_wait_queue, + !test_busystate(this), FIMC_IS_COMMAND_TIMEOUT); + if (ret) { + ret = 0; + } else { + err("timeout"); + ret = -ETIME; + } + + return ret; +} + +static int wait_initstate(struct fimc_is_interface *this) +{ + int ret = 0; + + ret = wait_event_timeout(this->init_wait_queue, + test_bit(IS_IF_STATE_START, &this->state), + FIMC_IS_STARTUP_TIMEOUT); + if (ret) { + ret = 0; + } else { + err("timeout"); + ret = -ETIME; + } + + return ret; +} + +static void testnclr_wakeup(struct fimc_is_interface *this, + u32 command) +{ + int ret = 0; + + ret = clr_busystate(this, command); + if (ret) + err("current state is invalid(%ld)", this->state); + + wake_up(&this->idle_wait_queue); +} + +static int waiting_is_ready(struct fimc_is_interface *interface) +{ + int ret = 0; + u32 try_count = TRY_RECV_AWARE_COUNT; + u32 cfg = readl(interface->regs + INTMSR0); + u32 status = INTMSR0_GET_INTMSD0(cfg); + + while (status) { + cfg = readl(interface->regs + INTMSR0); + status = INTMSR0_GET_INTMSD0(cfg); + udelay(100); + dbg("Retry to read INTMSR0(%d)\n", try_count); + + if (--try_count == 0) { + err("INTMSR0's 0 bit is not cleared."); + ret = -EINVAL; + break; + } + } + + return ret; +} + +static void send_interrupt(struct fimc_is_interface *interface) +{ + writel(INTGR0_INTGD0, interface->regs + INTGR0); +} + +static int fimc_is_set_cmd(struct fimc_is_interface *itf, + struct fimc_is_msg *msg, + struct fimc_is_msg *reply) +{ + int ret = 0; + int wait_lock = 0; + u32 lock_pid = 0; + u32 id; + volatile struct is_common_reg __iomem *com_regs; +#ifdef MEASURE_TIME +#ifdef INTERFACE_TIME + struct timeval measure_str, measure_end; +#endif +#endif + + BUG_ON(!itf); + BUG_ON(!msg); + BUG_ON(msg->instance >= FIMC_IS_MAX_NODES); + BUG_ON(msg->command >= HIC_COMMAND_END); + BUG_ON(!reply); + + if (!test_bit(IS_IF_STATE_OPEN, &itf->state)) { + warn("interface close, %d cmd is cancel", msg->command); + goto exit; + } + + lock_pid = atomic_read(&itf->lock_pid); + if (lock_pid && (lock_pid != current->pid)) { + pr_info("itf LOCK, %d(%d) wait\n", current->pid, msg->command); + wait_lock = wait_lockstate(itf); + pr_info("itf UNLOCK, %d(%d) go\n", current->pid, msg->command); + if (wait_lock) { + err("wait_lockstate is fail, lock reset"); + atomic_set(&itf->lock_pid, 0); + } + } + + dbg_interface("TP#1\n"); + enter_request_barrier(itf); + dbg_interface("TP#2\n"); + +#ifdef MEASURE_TIME +#ifdef INTERFACE_TIME + do_gettimeofday(&measure_str); +#endif +#endif + + switch (msg->command) { + case HIC_STREAM_ON: + if (itf->streaming[msg->instance] == IS_IF_STREAMING_ON) + warn("already stream on"); + break; + case HIC_STREAM_OFF: + if (itf->streaming[msg->instance] == IS_IF_STREAMING_OFF) + warn("already stream off"); + break; + case HIC_PROCESS_START: + if (itf->processing[msg->instance] == IS_IF_PROCESSING_ON) + warn("already process on"); + break; + case HIC_PROCESS_STOP: + if (itf->processing[msg->instance] == IS_IF_PROCESSING_OFF) + warn("already process off"); + break; + case HIC_POWER_DOWN: + if (itf->pdown_ready == IS_IF_POWER_DOWN_READY) + warn("already powerdown ready"); + break; + default: + if (itf->pdown_ready == IS_IF_POWER_DOWN_READY) { + exit_request_barrier(itf); + warn("already powerdown ready, %d cmd is cancel", + msg->command); + goto exit; + } + break; + } + + enter_process_barrier(itf); + + ret = waiting_is_ready(itf); + if (ret) { + exit_request_barrier(itf); + exit_process_barrier(itf); + err("waiting for ready is fail"); + ret = -EBUSY; + goto exit; + } + + set_busystate(itf, msg->command); + com_regs = itf->com_regs; + id = (msg->group << GROUP_ID_SHIFT) | msg->instance; + writel(msg->command, &com_regs->hicmd); + writel(id, &com_regs->hic_sensorid); + writel(msg->parameter1, &com_regs->hic_param1); + writel(msg->parameter2, &com_regs->hic_param2); + writel(msg->parameter3, &com_regs->hic_param3); + writel(msg->parameter4, &com_regs->hic_param4); + send_interrupt(itf); + + exit_process_barrier(itf); + + ret = wait_idlestate(itf); + if (ret) { + exit_request_barrier(itf); + err("%d command is timeout", msg->command); + fimc_is_hw_regdump(itf); + print_req_work_list(&itf->work_list[INTR_GENERAL]); + print_work_data_state(itf); + clr_busystate(itf, msg->command); + ret = -ETIME; + goto exit; + } + + reply->command = itf->reply.command; + reply->group = itf->reply.group; + reply->instance = itf->reply.instance; + reply->parameter1 = itf->reply.parameter1; + reply->parameter2 = itf->reply.parameter2; + reply->parameter3 = itf->reply.parameter3; + reply->parameter4 = itf->reply.parameter4; + + if (reply->command == ISR_DONE) { + if (msg->command != reply->parameter1) { + exit_request_barrier(itf); + err("invalid command reply(%d != %d)", msg->command, reply->parameter1); + ret = -EINVAL; + goto exit; + } + + switch (msg->command) { + case HIC_STREAM_ON: + itf->streaming[msg->instance] = IS_IF_STREAMING_ON; + break; + case HIC_STREAM_OFF: + itf->streaming[msg->instance] = IS_IF_STREAMING_OFF; + break; + case HIC_PROCESS_START: + itf->processing[msg->instance] = IS_IF_PROCESSING_ON; + break; + case HIC_PROCESS_STOP: + itf->processing[msg->instance] = IS_IF_PROCESSING_OFF; + break; + case HIC_POWER_DOWN: + itf->pdown_ready = IS_IF_POWER_DOWN_READY; + break; + case HIC_OPEN_SENSOR: + if (reply->parameter1 == HIC_POWER_DOWN) { + err("firmware power down"); + itf->pdown_ready = IS_IF_POWER_DOWN_READY; + ret = -ECANCELED; + } else { + itf->pdown_ready = IS_IF_POWER_DOWN_NREADY; + } + break; + default: + break; + } + } else { + err("ISR_NDONE is occured"); + ret = -EINVAL; + } + +#ifdef MEASURE_TIME +#ifdef INTERFACE_TIME + do_gettimeofday(&measure_end); + measure_time(&itf->time[msg->command], + msg->instance, + msg->group, + &measure_str, + &measure_end); +#endif +#endif + + exit_request_barrier(itf); + +exit: + if (ret) + fimc_is_hw_logdump(itf); + + return ret; +} + +static int fimc_is_set_cmd_shot(struct fimc_is_interface *this, + struct fimc_is_msg *msg) +{ + int ret = 0; + u32 id; + volatile struct is_common_reg __iomem *com_regs; + + BUG_ON(!this); + BUG_ON(!msg); + BUG_ON(msg->instance >= FIMC_IS_MAX_NODES); + + if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { + warn("interface close, %d cmd is cancel", msg->command); + goto exit; + } + + enter_process_barrier(this); + + ret = waiting_is_ready(this); + if (ret) { + exit_process_barrier(this); + err("waiting for ready is fail"); + ret = -EBUSY; + goto exit; + } + + spin_lock_irq(&this->shot_check_lock); + atomic_set(&this->shot_check[msg->instance], 1); + spin_unlock_irq(&this->shot_check_lock); + + com_regs = this->com_regs; + id = (msg->group << GROUP_ID_SHIFT) | msg->instance; + writel(msg->command, &com_regs->hicmd); + writel(id, &com_regs->hic_sensorid); + writel(msg->parameter1, &com_regs->hic_param1); + writel(msg->parameter2, &com_regs->hic_param2); + writel(msg->parameter3, &com_regs->hic_param3); + writel(msg->parameter4, &com_regs->hic_param4); + send_interrupt(this); + + exit_process_barrier(this); + +exit: + return ret; +} + +static int fimc_is_set_cmd_nblk(struct fimc_is_interface *this, + struct fimc_is_work *work) +{ + int ret = 0; + u32 id; + struct fimc_is_msg *msg; + volatile struct is_common_reg __iomem *com_regs; + + msg = &work->msg; + switch (msg->command) { + case HIC_SET_CAM_CONTROL: + set_req_work(&this->nblk_cam_ctrl, work); + break; + case HIC_MSG_TEST: + break; + default: + err("unresolved command\n"); + break; + } + + enter_process_barrier(this); + + ret = waiting_is_ready(this); + if (ret) { + err("waiting for ready is fail"); + ret = -EBUSY; + goto exit; + } + + com_regs = this->com_regs; + id = (msg->group << GROUP_ID_SHIFT) | msg->instance; + writel(msg->command, &com_regs->hicmd); + writel(id, &com_regs->hic_sensorid); + writel(msg->parameter1, &com_regs->hic_param1); + writel(msg->parameter2, &com_regs->hic_param2); + writel(msg->parameter3, &com_regs->hic_param3); + writel(msg->parameter4, &com_regs->hic_param4); + send_interrupt(this); + +exit: + exit_process_barrier(this); + return ret; +} + +static inline void fimc_is_get_cmd(struct fimc_is_interface *itf, + struct fimc_is_msg *msg, u32 index) +{ + volatile struct is_common_reg __iomem *com_regs = itf->com_regs; + + switch (index) { + case INTR_GENERAL: + msg->id = 0; + msg->command = readl(&com_regs->ihcmd); + msg->instance = readl(&com_regs->ihc_sensorid); + msg->parameter1 = readl(&com_regs->ihc_param1); + msg->parameter2 = readl(&com_regs->ihc_param2); + msg->parameter3 = readl(&com_regs->ihc_param3); + msg->parameter4 = readl(&com_regs->ihc_param4); + break; + case INTR_3A0C_FDONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->taa0c_sensor_id); + msg->parameter1 = readl(&com_regs->taa0c_param1); + msg->parameter2 = readl(&com_regs->taa0c_param2); + msg->parameter3 = readl(&com_regs->taa0c_param3); + msg->parameter4 = 0; + break; + case INTR_3A1C_FDONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->taa1c_sensor_id); + msg->parameter1 = readl(&com_regs->taa1c_param1); + msg->parameter2 = readl(&com_regs->taa1c_param2); + msg->parameter3 = readl(&com_regs->taa1c_param3); + msg->parameter4 = 0; + break; + case INTR_SCC_FDONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->scc_sensor_id); + msg->parameter1 = readl(&com_regs->scc_param1); + msg->parameter2 = readl(&com_regs->scc_param2); + msg->parameter3 = readl(&com_regs->scc_param3); + msg->parameter4 = 0; + break; + case INTR_DIS_FDONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->dis_sensor_id); + msg->parameter1 = readl(&com_regs->dis_param1); + msg->parameter2 = readl(&com_regs->dis_param2); + msg->parameter3 = readl(&com_regs->dis_param3); + msg->parameter4 = 0; + break; + case INTR_SCP_FDONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->scp_sensor_id); + msg->parameter1 = readl(&com_regs->scp_param1); + msg->parameter2 = readl(&com_regs->scp_param2); + msg->parameter3 = readl(&com_regs->scp_param3); + msg->parameter4 = 0; + break; + case INTR_SHOT_DONE: + msg->id = 0; + msg->command = IHC_FRAME_DONE; + msg->instance = readl(&com_regs->shot_sensor_id); + msg->parameter1 = readl(&com_regs->shot_param1); + msg->parameter2 = readl(&com_regs->shot_param2); + msg->parameter3 = readl(&com_regs->shot_param3); + msg->parameter4 = 0; + break; + default: + msg->id = 0; + msg->command = 0; + msg->instance = 0; + msg->parameter1 = 0; + msg->parameter2 = 0; + msg->parameter3 = 0; + msg->parameter4 = 0; + err("unknown command getting\n"); + break; + } + + msg->group = msg->instance >> GROUP_ID_SHIFT; + msg->instance = msg->instance & GROUP_ID_MASK; +} + +static inline u32 fimc_is_get_intr(struct fimc_is_interface *itf) +{ + u32 status = 0; + volatile struct is_common_reg __iomem *com_regs = itf->com_regs; + + if (itf->need_iflag) { + status = readl(&com_regs->ihcmd_iflag) | + readl(&com_regs->taa0c_iflag) | + readl(&com_regs->taa1c_iflag) | + readl(&com_regs->scc_iflag) | + readl(&com_regs->dis_iflag) | + readl(&com_regs->scp_iflag) | + readl(&com_regs->shot_iflag); + } + + status |= readl(itf->regs + INTMSR1); + + return status; +} + +static inline void fimc_is_clr_intr(struct fimc_is_interface *itf, + u32 index) +{ + volatile struct is_common_reg __iomem *com_regs = itf->com_regs; + + writel((1 << index), itf->regs + INTCR1); + + if (itf->need_iflag) { + switch (index) { + case INTR_GENERAL: + writel(0, &com_regs->ihcmd_iflag); + break; + case INTR_3A0C_FDONE: + writel(0, &com_regs->taa0c_iflag); + break; + case INTR_3A1C_FDONE: + writel(0, &com_regs->taa1c_iflag); + break; + case INTR_SCC_FDONE: + writel(0, &com_regs->scc_iflag); + break; + case INTR_DIS_FDONE: + writel(0, &com_regs->dis_iflag); + break; + case INTR_SCP_FDONE: + writel(0, &com_regs->scp_iflag); + break; + case INTR_SHOT_DONE: + writel(0, &com_regs->shot_iflag); + break; + default: + err("unknown command clear\n"); + break; + } + } +} + +static void wq_func_general(struct work_struct *data) +{ + struct fimc_is_interface *itf; + struct fimc_is_msg *msg; + struct fimc_is_work *work; + struct fimc_is_work *nblk_work; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_GENERAL]); + + get_req_work(&itf->work_list[INTR_GENERAL], &work); + while (work) { + msg = &work->msg; + switch (msg->command) { + case IHC_GET_SENSOR_NUMBER: + info("IS Version: %d.%d [0x%02x]\n", + ISDRV_VERSION, msg->parameter1, + get_drv_clock_gate() | + get_drv_dvfs()); + set_bit(IS_IF_STATE_START, &itf->state); + itf->pdown_ready = IS_IF_POWER_DOWN_NREADY; + wake_up(&itf->init_wait_queue); + break; + case ISR_DONE: + switch (msg->parameter1) { + case HIC_OPEN_SENSOR: + dbg_interface("open done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_CLOSE_SENSOR: + dbg_interface("close done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_GET_SET_FILE_ADDR: + dbg_interface("saddr(%p) done\n", + (void *)msg->parameter2); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_LOAD_SET_FILE: + dbg_interface("setfile done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_SET_A5_MAP: + dbg_interface("mapping done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_SET_A5_UNMAP: + dbg_interface("unmapping done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_PROCESS_START: + dbg_interface("process_on done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_PROCESS_STOP: + dbg_interface("process_off done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_STREAM_ON: + dbg_interface("stream_on done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_STREAM_OFF: + dbg_interface("stream_off done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_SET_PARAMETER: + dbg_interface("s_param done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_GET_STATIC_METADATA: + dbg_interface("g_capability done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_PREVIEW_STILL: + dbg_interface("a_param(%dx%d) done\n", + msg->parameter2, + msg->parameter3); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_POWER_DOWN: + dbg_interface("powerdown done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + case HIC_I2C_CONTROL_LOCK: + dbg_interface("i2c lock done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; +#if (FW_HAS_SYS_CTRL_CMD) + case HIC_SYSTEM_CONTROL: + dbg_interface("system control done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; +#endif +#if (FW_HAS_SENSOR_MODE_CMD) + case HIC_SENSOR_MODE_CHANGE: + dbg_interface("sensor mode change done\n"); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; +#endif + /*non-blocking command*/ + case HIC_SHOT: + err("shot done is not acceptable\n"); + break; + case HIC_SET_CAM_CONTROL: + /* this code will be used latter */ +#if 0 + dbg_interface("camctrl done\n"); + get_req_work(&itf->nblk_cam_ctrl , &nblk_work); + if (nblk_work) { + nblk_work->msg.command = ISR_DONE; + set_free_work(&itf->nblk_cam_ctrl, + nblk_work); + } else { + err("nblk camctrl request is empty"); + print_fre_work_list( + &itf->nblk_cam_ctrl); + print_req_work_list( + &itf->nblk_cam_ctrl); + } +#else + err("camctrl is not acceptable\n"); +#endif + break; + default: + err("unknown done is invokded\n"); + break; + } + break; + case ISR_NDONE: + switch (msg->parameter1) { + case HIC_SHOT: + err("[ITF:%d] shot NDONE is not acceptable", + msg->instance); + break; + case HIC_SET_CAM_CONTROL: + dbg_interface("camctrl NOT done\n"); + get_req_work(&itf->nblk_cam_ctrl , &nblk_work); + nblk_work->msg.command = ISR_NDONE; + set_free_work(&itf->nblk_cam_ctrl, nblk_work); + break; + case HIC_SET_PARAMETER: + err("s_param NOT done"); + err("param2 : 0x%08X", msg->parameter2); + err("param3 : 0x%08X", msg->parameter3); + err("param4 : 0x%08X", msg->parameter4); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + default: + err("a command(%d) not done", msg->parameter1); + memcpy(&itf->reply, msg, + sizeof(struct fimc_is_msg)); + testnclr_wakeup(itf, msg->parameter1); + break; + } + break; + case IHC_SET_FACE_MARK: + err("FACE_MARK(%d,%d,%d) is not acceptable\n", + msg->parameter1, + msg->parameter2, + msg->parameter3); + break; + case IHC_AA_DONE: + err("AA_DONE(%d,%d,%d) is not acceptable\n", + msg->parameter1, + msg->parameter2, + msg->parameter3); + break; + case IHC_FLASH_READY: + err("IHC_FLASH_READY is not acceptable"); + break; + case IHC_NOT_READY: + err("IHC_NOT_READY is occured, need reset"); + fimc_is_hw_logdump(itf); + break; +#if (FW_HAS_REPORT_ERR_CMD) + case IHC_REPORT_ERR: + err("IHC_REPORT_ERR is occured"); + fimc_is_err_report_handler(itf, msg); + break; +#endif + default: + err("func_general unknown(0x%08X) end\n", msg->command); + break; + } + + set_free_work(&itf->work_list[INTR_GENERAL], work); + get_req_work(&itf->work_list[INTR_GENERAL], &work); + } +} + +static void wq_func_subdev(struct fimc_is_subdev *leader, + struct fimc_is_subdev *subdev, + u32 fcount, u32 rcount, u32 status, u32 instance) +{ + u32 findex, out_flag; + u32 capture_vid, capture_id; + char name; + unsigned long flags; + struct fimc_is_video_ctx *ldr_vctx, *sub_vctx; + struct fimc_is_framemgr *ldr_framemgr, *sub_framemgr; + struct fimc_is_frame *ldr_frame, *sub_frame; + struct camera2_node *capture; + + BUG_ON(!leader); + BUG_ON(!subdev); + + ldr_vctx = leader->vctx; + if (!ldr_vctx) { + err("ldr_vctx is NULL"); + return; + } + + sub_vctx = subdev->vctx; + if (!sub_vctx) { + err("sub_vctx is NULL"); + return; + } + + if (!sub_vctx->video) { + err("video is NULL"); + return; + } + + ldr_framemgr = GET_SRC_FRAMEMGR(ldr_vctx); + sub_framemgr = GET_DST_FRAMEMGR(sub_vctx); + + capture_vid = sub_vctx->video->id; + switch (capture_vid) { + case FIMC_IS_VIDEO_SCC_NUM: + out_flag = OUT_SCC_FRAME; + name = 'C'; + break; + case FIMC_IS_VIDEO_VDC_NUM: + out_flag = OUT_DIS_FRAME; + name = 'D'; + break; + case FIMC_IS_VIDEO_SCP_NUM: + out_flag = OUT_SCP_FRAME; + name = 'P'; + break; + case FIMC_IS_VIDEO_3A0C_NUM: + out_flag = OUT_3AAC_FRAME; + name = '0'; + break; + case FIMC_IS_VIDEO_3A1C_NUM: + out_flag = OUT_3AAC_FRAME; + name = '1'; + break; + default: + err("video node is invalid(%d)", sub_vctx->video->id); + return; + } + + framemgr_e_barrier_irqs(sub_framemgr, 0, flags); + + fimc_is_frame_process_head(sub_framemgr, &sub_frame); + if (sub_frame && test_bit(REQ_FRAME, &sub_frame->req_flag)) { + +#ifdef DBG_STREAMING + info("[%c:D:%d] %d(%d,%d)\n", name, instance, + sub_frame->index, fcount, rcount); +#endif + + if (!sub_frame->stream) { + err("stream is NULL, critical error"); + goto p_err; + } + + findex = sub_frame->stream->findex; + if (findex >= ldr_vctx->q_src->buf_maxcount) { + err("findex(%d) is invalid(max : %d)", + findex, ldr_vctx->q_src->buf_maxcount); + sub_frame->stream->fvalid = 0; + goto done; + } + + ldr_frame = &ldr_framemgr->frame[findex]; + if (status) { + info("[%c:D:%d] FRM%d NOT DONE(%d)\n", name, + instance, fcount, status); + sub_frame->stream->fvalid = 0; + clear_bit(out_flag, &ldr_frame->out_flag); + goto done; + } + + if (ldr_frame->fcount != fcount) { + err("%c frame mismatched(ldr%d, sub%d)", name, + ldr_frame->fcount, fcount); + sub_frame->stream->fvalid = 0; + } else { + sub_frame->stream->fvalid = 1; + clear_bit(out_flag, &ldr_frame->out_flag); + } + + for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) { + capture = &ldr_frame->shot_ext->node_group.capture[capture_id]; + if (capture_vid == capture->vid) { + sub_frame->stream->output_crop_region[0] = capture->output.cropRegion[0]; + sub_frame->stream->output_crop_region[1] = capture->output.cropRegion[1]; + sub_frame->stream->output_crop_region[2] = capture->output.cropRegion[2]; + sub_frame->stream->output_crop_region[3] = capture->output.cropRegion[3]; + + sub_frame->stream->input_crop_region[0] = capture->input.cropRegion[0]; + sub_frame->stream->input_crop_region[1] = capture->input.cropRegion[1]; + sub_frame->stream->input_crop_region[2] = capture->input.cropRegion[2]; + sub_frame->stream->input_crop_region[3] = capture->input.cropRegion[3]; + break; + } + } + + if (capture_id >= CAPTURE_NODE_MAX) { + err("can't find capture stream(%d > %d)", capture_id, CAPTURE_NODE_MAX); + sub_frame->stream->output_crop_region[0] = 0; + sub_frame->stream->output_crop_region[1] = 0; + sub_frame->stream->output_crop_region[2] = 0; + sub_frame->stream->output_crop_region[3] = 0; + } + +done: + clear_bit(REQ_FRAME, &sub_frame->req_flag); + sub_frame->stream->fcount = fcount; + sub_frame->stream->rcount = rcount; + + fimc_is_frame_trans_pro_to_com(sub_framemgr, sub_frame); + buffer_done(sub_vctx, sub_frame->index); + } else + err("done(%p) is occured without request", sub_frame); + +p_err: + framemgr_x_barrier_irqr(sub_framemgr, 0, flags); +} + +static void wq_func_3a0c(struct work_struct *data) +{ + u32 instance, fcount, rcount, status; + struct fimc_is_interface *itf; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader, *subdev; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_3A0C_FDONE]); + + get_req_work(&itf->work_list[INTR_3A0C_FDONE], &work); + while (work) { + msg = &work->msg; + instance = msg->instance; + fcount = msg->parameter1; + status = msg->parameter2; + rcount = msg->parameter3; + + if (instance >= FIMC_IS_MAX_NODES) { + err("instance is invalid(%d)", instance); + goto p_err; + } + + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + if (!device) { + err("device is NULL"); + goto p_err; + } + + subdev = &device->taac; + leader = subdev->leader; + + wq_func_subdev(leader, subdev, fcount, rcount, status, instance); + +p_err: + set_free_work(&itf->work_list[INTR_3A0C_FDONE], work); + get_req_work(&itf->work_list[INTR_3A0C_FDONE], &work); + } +} + +static void wq_func_3a1c(struct work_struct *data) +{ + u32 instance, fcount, rcount, status; + struct fimc_is_interface *itf; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader, *subdev; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_3A1C_FDONE]); + + get_req_work(&itf->work_list[INTR_3A1C_FDONE], &work); + while (work) { + msg = &work->msg; + instance = msg->instance; + fcount = msg->parameter1; + status = msg->parameter2; + rcount = msg->parameter3; + + if (instance >= FIMC_IS_MAX_NODES) { + err("instance is invalid(%d)", instance); + goto p_err; + } + + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + if (!device) { + err("device is NULL"); + goto p_err; + } + + subdev = &device->taac; + leader = subdev->leader; + + wq_func_subdev(leader, subdev, fcount, rcount, status, instance); + +p_err: + set_free_work(&itf->work_list[INTR_3A1C_FDONE], work); + get_req_work(&itf->work_list[INTR_3A1C_FDONE], &work); + } +} + +static void wq_func_scc(struct work_struct *data) +{ + u32 instance, fcount, rcount, status; + struct fimc_is_interface *itf; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader, *subdev; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_SCC_FDONE]); + + get_req_work(&itf->work_list[INTR_SCC_FDONE], &work); + while (work) { + msg = &work->msg; + instance = msg->instance; + fcount = msg->parameter1; + status = msg->parameter2; + rcount = msg->parameter3; + + if (instance >= FIMC_IS_MAX_NODES) { + err("instance is invalid(%d)", instance); + goto p_err; + } + + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + if (!device) { + err("device is NULL"); + goto p_err; + } + + subdev = &device->scc; + leader = subdev->leader; + + wq_func_subdev(leader, subdev, fcount, rcount, status, instance); + +p_err: + set_free_work(&itf->work_list[INTR_SCC_FDONE], work); + get_req_work(&itf->work_list[INTR_SCC_FDONE], &work); + } +} + +static void wq_func_dis(struct work_struct *data) +{ + u32 instance, fcount, rcount, status; + struct fimc_is_interface *itf; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader, *subdev; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_DIS_FDONE]); + + get_req_work(&itf->work_list[INTR_DIS_FDONE], &work); + while (work) { + msg = &work->msg; + instance = msg->instance; + fcount = msg->parameter1; + status = msg->parameter2; + rcount = msg->parameter3; + + if (instance >= FIMC_IS_MAX_NODES) { + err("instance is invalid(%d)", instance); + goto p_err; + } + + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + if (!device) { + err("device is NULL"); + goto p_err; + } + + subdev = &device->dis; + leader = subdev->leader; + + wq_func_subdev(leader, subdev, fcount, rcount, status, instance); + +p_err: + set_free_work(&itf->work_list[INTR_DIS_FDONE], work); + get_req_work(&itf->work_list[INTR_DIS_FDONE], &work); + } +} + +static void wq_func_scp(struct work_struct *data) +{ + u32 instance, fcount, rcount, status; + struct fimc_is_interface *itf; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader, *subdev; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_SCP_FDONE]); + + get_req_work(&itf->work_list[INTR_SCP_FDONE], &work); + while (work) { + msg = &work->msg; + instance = msg->instance; + fcount = msg->parameter1; + status = msg->parameter2; + rcount = msg->parameter3; + + if (instance >= FIMC_IS_MAX_NODES) { + err("instance is invalid(%d)", instance); + goto p_err; + } + + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + if (!device) { + err("device is NULL"); + goto p_err; + } + + subdev = &device->scp; + leader = subdev->leader; + + wq_func_subdev(leader, subdev, fcount, rcount, status, instance); + +p_err: + set_free_work(&itf->work_list[INTR_SCP_FDONE], work); + get_req_work(&itf->work_list[INTR_SCP_FDONE], &work); + } +} + +static void wq_func_group_3a0(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_framemgr *ldr_framemgr, + struct fimc_is_frame *ldr_frame, + struct fimc_is_framemgr *sub_framemgr, + struct fimc_is_video_ctx *vctx, + u32 status) +{ + u32 done_state = VB2_BUF_STATE_DONE; + unsigned long flags; + struct fimc_is_queue *src_queue, *dst_queue; + struct fimc_is_frame *sub_frame; + + BUG_ON(!vctx); + BUG_ON(!ldr_framemgr); + BUG_ON(!ldr_frame); + BUG_ON(!sub_framemgr); + + if (status != ISR_DONE) { + info("[3A0:D:%d] GRP0 NOT DONE(%d, %d)\n", group->instance, + ldr_frame->fcount, ldr_frame->index); + done_state = VB2_BUF_STATE_ERROR; + } else if ((group->device->taa_size_forceset) && + (ldr_frame->fcount >= group->device->taa_size_changed_fcount)) { + /* Acknowledge the group size change settings */ + group->device->taa_size_forceset = 0; + group->device->taa_size_changed_fcount = 0; + } + +#ifdef DBG_STREAMING + if (status == ISR_DONE) + info("[3A0:D:%d] GRP0 DONE(%d)\n", group->instance, + ldr_frame->fcount); +#endif + + src_queue = GET_SRC_QUEUE(vctx); + dst_queue = GET_DST_QUEUE(vctx); + + /* 1. sub frame done */ + framemgr_e_barrier_irqs(sub_framemgr, 0, flags); + + fimc_is_frame_process_head(sub_framemgr, &sub_frame); + if (sub_frame && test_bit(REQ_FRAME, &sub_frame->req_flag)) { + clear_bit(REQ_FRAME, &sub_frame->req_flag); + + sub_frame->stream->fvalid = 1; + sub_frame->stream->fcount = ldr_frame->fcount; + sub_frame->stream->rcount = ldr_frame->rcount; + + fimc_is_frame_trans_pro_to_com(sub_framemgr, sub_frame); + queue_done(vctx, dst_queue, sub_frame->index, done_state); + } else + err("done is occured without request(%p, %d)", sub_frame, ldr_frame->fcount); + + framemgr_x_barrier_irqr(sub_framemgr, 0, flags); + + /* 2. leader frame done */ + fimc_is_ischain_meta_invalid(ldr_frame); + + fimc_is_frame_trans_pro_to_com(ldr_framemgr, ldr_frame); + fimc_is_group_done(groupmgr, group, ldr_frame, done_state); + queue_done(vctx, src_queue, ldr_frame->index, done_state); +} + +static void wq_func_group_3a1(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_framemgr *ldr_framemgr, + struct fimc_is_frame *ldr_frame, + struct fimc_is_framemgr *sub_framemgr, + struct fimc_is_video_ctx *vctx, + u32 status) +{ + u32 done_state = VB2_BUF_STATE_DONE; + unsigned long flags; + struct fimc_is_queue *src_queue, *dst_queue; + struct fimc_is_frame *sub_frame; + + BUG_ON(!vctx); + BUG_ON(!ldr_framemgr); + BUG_ON(!ldr_frame); + BUG_ON(!sub_framemgr); + + if (status != ISR_DONE) { + info("[3A1:D:%d] GRP1 NOT DONE(%d, %d)\n", group->instance, + ldr_frame->fcount, ldr_frame->index); + done_state = VB2_BUF_STATE_ERROR; + } else if ((group->device->taa_size_forceset) && + (ldr_frame->fcount >= group->device->taa_size_changed_fcount)) { + /* Acknowledge the group size change settings */ + group->device->taa_size_forceset = 0; + group->device->taa_size_changed_fcount = 0; + } + +#ifdef DBG_STREAMING + if (status == ISR_DONE) + info("[3A1:D:%d] GRP1 DONE(%d)\n", group->instance, + ldr_frame->fcount); +#endif + + src_queue = GET_SRC_QUEUE(vctx); + dst_queue = GET_DST_QUEUE(vctx); + + /* 1. sub frame done */ + framemgr_e_barrier_irqs(sub_framemgr, 0, flags); + + fimc_is_frame_process_head(sub_framemgr, &sub_frame); + if (sub_frame && test_bit(REQ_FRAME, &sub_frame->req_flag)) { + clear_bit(REQ_FRAME, &sub_frame->req_flag); + + sub_frame->stream->fvalid = 1; + sub_frame->stream->fcount = ldr_frame->fcount; + sub_frame->stream->rcount = ldr_frame->rcount; + + fimc_is_frame_trans_pro_to_com(sub_framemgr, sub_frame); + queue_done(vctx, dst_queue, sub_frame->index, done_state); + } else + err("done is occured without request(%p)", sub_frame); + + framemgr_x_barrier_irqr(sub_framemgr, 0, flags); + + /* 2. leader frame done */ + fimc_is_ischain_meta_invalid(ldr_frame); + + fimc_is_frame_trans_pro_to_com(ldr_framemgr, ldr_frame); + fimc_is_group_done(groupmgr, group, ldr_frame, done_state); + queue_done(vctx, src_queue, ldr_frame->index, done_state); +} + +static void wq_func_group_isp(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_video_ctx *vctx, + u32 status) +{ + u32 done_state = VB2_BUF_STATE_DONE; + struct fimc_is_queue *queue; +#ifdef ENABLE_SENSOR_DRIVER + struct camera2_lens_uctl *isp_lens_uctl; + struct camera2_lens_uctl *lens_uctl; + struct camera2_sensor_uctl *isp_sensor_uctl; + struct camera2_sensor_uctl *sensor_uctl; + struct camera2_flash_uctl *isp_flash_uctl; + struct camera2_flash_uctl *flash_uctl; +#endif + + BUG_ON(!framemgr); + BUG_ON(!frame); + + if (status != ISR_DONE) { + info("[ISP:D:%d] GRP2 NOT DONE(%d, %d)\n", group->instance, + frame->fcount, frame->index); + done_state = VB2_BUF_STATE_ERROR; + } else if ((group->device->isp_size_forceset) && + (frame->fcount >= group->device->isp_size_changed_fcount)) { + /* Acknowledge the group size change settings */ + group->device->isp_size_forceset = 0; + group->device->isp_size_changed_fcount = 0; + } + +#ifdef DBG_STREAMING + if (status == ISR_DONE) + info("[ISP:D:%d] GRP2 DONE(%d)\n", group->instance, + frame->fcount); +#endif + + queue = GET_SRC_QUEUE(vctx); + + /* Cache Invalidation */ + fimc_is_ischain_meta_invalid(frame); + +#ifdef ENABLE_SENSOR_DRIVER + isp_lens_uctl = &itf->isp_peri_ctl.lensUd; + isp_sensor_uctl = &itf->isp_peri_ctl.sensorUd; + isp_flash_uctl = &itf->isp_peri_ctl.flashUd; + + if (frame->shot->uctl.uUpdateBitMap & CAM_SENSOR_CMD) { + sensor_uctl = &frame->shot->uctl.sensorUd; + isp_sensor_uctl->ctl.exposureTime = + sensor_uctl->ctl.exposureTime; + isp_sensor_uctl->ctl.frameDuration = + sensor_uctl->ctl.frameDuration; + isp_sensor_uctl->ctl.sensitivity = + sensor_uctl->ctl.sensitivity; + + frame->shot->uctl.uUpdateBitMap &= + ~CAM_SENSOR_CMD; + } + + if (frame->shot->uctl.uUpdateBitMap & CAM_LENS_CMD) { + lens_uctl = &frame->shot->uctl.lensUd; + isp_lens_uctl->ctl.focusDistance = + lens_uctl->ctl.focusDistance; + isp_lens_uctl->maxPos = + lens_uctl->maxPos; + isp_lens_uctl->slewRate = + lens_uctl->slewRate; + + frame->shot->uctl.uUpdateBitMap &= + ~CAM_LENS_CMD; + } + + if (frame->shot->uctl.uUpdateBitMap & CAM_FLASH_CMD) { + flash_uctl = &frame->shot->uctl.flashUd; + isp_flash_uctl->ctl.flashMode = + flash_uctl->ctl.flashMode; + isp_flash_uctl->ctl.firingPower = + flash_uctl->ctl.firingPower; + isp_flash_uctl->ctl.firingTime = + flash_uctl->ctl.firingTime; + + frame->shot->uctl.uUpdateBitMap &= + ~CAM_FLASH_CMD; + } +#endif + + fimc_is_frame_trans_pro_to_com(framemgr, frame); + fimc_is_group_done(groupmgr, group, frame, done_state); + queue_done(vctx, queue, frame->index, done_state); +} + +static void wq_func_group_dis(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_framemgr *framemgr, + struct fimc_is_frame *frame, + struct fimc_is_video_ctx *vctx, + u32 status) +{ + u32 done_state = VB2_BUF_STATE_DONE; + struct fimc_is_queue *queue; + + BUG_ON(!framemgr); + BUG_ON(!frame); + + if (status != ISR_DONE) { + info("[DIS:D:%d] GRP3 NOT DONE(%d, %d)\n", group->instance, + frame->fcount, frame->index); + done_state = VB2_BUF_STATE_ERROR; + } + +#ifdef DBG_STREAMING + if (status == ISR_DONE) + info("[DIS:D:%d] GRP3 DONE(%d)\n", group->instance, + frame->fcount); +#endif + + queue = GET_SRC_QUEUE(vctx); + + /* Cache Invalidation */ + fimc_is_ischain_meta_invalid(frame); + + fimc_is_frame_trans_pro_to_com(framemgr, frame); + fimc_is_group_done(groupmgr, group, frame, done_state); + queue_done(vctx, queue, frame->index, done_state); +} + +void wq_func_group(struct fimc_is_groupmgr *groupmgr, + struct fimc_is_group *group, + struct fimc_is_framemgr *ldr_framemgr, + struct fimc_is_frame *ldr_frame, + struct fimc_is_video_ctx *vctx, + u32 status1, u32 status2, u32 fcount) +{ + u32 lindex, hindex; + struct fimc_is_framemgr *sub_framemgr; + + BUG_ON(!groupmgr); + BUG_ON(!group); + BUG_ON(!ldr_framemgr); + BUG_ON(!ldr_frame); + BUG_ON(!vctx); + + /* + * complete count should be lower than 3 when + * buffer is queued or overflow can be occured + */ + if (ldr_framemgr->frame_com_cnt >= 2) + warn("remained completes is %d(%X)", (ldr_framemgr->frame_com_cnt + 1), group->id); + + if (status2 != IS_SHOT_SUCCESS) { + lindex = ldr_frame->shot->ctl.entry.lowIndexParam; + lindex &= ~ldr_frame->shot->dm.entry.lowIndexParam; + hindex = ldr_frame->shot->ctl.entry.highIndexParam; + hindex &= ~ldr_frame->shot->dm.entry.highIndexParam; + if (lindex || hindex) + err("cause : %d : invalid parameter(%08X %08X)", status2, lindex, hindex); + else + err("cause : %d", status2); + } + + switch (group->id) { + case GROUP_ID_3A0: + sub_framemgr = GET_DST_FRAMEMGR(vctx); + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + /* + * When OTF is enabled, 3A0 can have 2 more shots + * done is reported to hal if frame count is same + * not done is reported to hal if driver frame count is + * less than firmware frame count + * this correction is repeated + */ + if (fcount != ldr_frame->fcount) { + while (ldr_frame) { + if (fcount == ldr_frame->fcount) { + status1 = ISR_NDONE; + wq_func_group_3a0(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + break; + } else if (fcount > ldr_frame->fcount) { + err("cause: mismatch(%d != %d)", + fcount, + ldr_frame->fcount); + status1 = ISR_NDONE; + wq_func_group_3a0(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + + /* get next leader frame */ + fimc_is_frame_process_head(ldr_framemgr, + &ldr_frame); + } else { + warn("%d done is ignored", fcount); + break; + } + } + } else { + wq_func_group_3a0(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + } + } else { + if (fcount != ldr_frame->fcount) { + err("cause : mismatch(%d != %d)", fcount, ldr_frame->fcount); + status1 = ISR_NDONE; + } + + wq_func_group_3a0(groupmgr, group, ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + } + break; + case GROUP_ID_3A1: + sub_framemgr = GET_DST_FRAMEMGR(vctx); + + if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { + /* + * When OTF is enabled, 3A1 can have 2 more shots + * done is reported to hal if frame count is same + * not done is reported to hal if driver frame count is + * less than firmware frame count + * this correction is repeated + */ + if (fcount != ldr_frame->fcount) { + + while (ldr_frame) { + if (fcount == ldr_frame->fcount) { + status1 = ISR_NDONE; + wq_func_group_3a1(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + break; + } else if (fcount > ldr_frame->fcount) { + err("cause : mismatch(%d != %d)", fcount, + ldr_frame->fcount); + status1 = ISR_NDONE; + wq_func_group_3a1(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + + /* get next leader frame */ + fimc_is_frame_process_head(ldr_framemgr, + &ldr_frame); + } else { + warn("%d done is ignored", fcount); + break; + } + } + } else { + wq_func_group_3a1(groupmgr, group, + ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + } + } else { + if (fcount != ldr_frame->fcount) { + err("cause : mismatch(%d != %d)", fcount, ldr_frame->fcount); + status1 = ISR_NDONE; + } + + wq_func_group_3a1(groupmgr, group, ldr_framemgr, ldr_frame, + sub_framemgr, vctx, status1); + + } + break; + case GROUP_ID_ISP: + if (fcount != ldr_frame->fcount) { + err("cause : mismatch(%d != %d)", fcount, + ldr_frame->fcount); + status1 = ISR_NDONE; + } + + wq_func_group_isp(groupmgr, group, ldr_framemgr, ldr_frame, + vctx, status1); + break; + case GROUP_ID_DIS: + if (fcount != ldr_frame->fcount) { + err("cause : mismatch(%d != %d)", fcount, + ldr_frame->fcount); + status1 = ISR_NDONE; + } + + wq_func_group_dis(groupmgr, group, ldr_framemgr, ldr_frame, + vctx, status1); + break; + default: + err("unresolved group id %d", group->id); + break; + } +} + +static void wq_func_shot(struct work_struct *data) +{ + struct fimc_is_device_ischain *device; + struct fimc_is_interface *itf; + struct fimc_is_msg *msg; + struct fimc_is_framemgr *grp_framemgr; + struct fimc_is_frame *frame; + struct fimc_is_groupmgr *groupmgr; + struct fimc_is_group *group; + struct fimc_is_work_list *work_list; + struct fimc_is_work *work; + struct fimc_is_video_ctx *vctx; + unsigned long flags; + u32 req_flag; + u32 fcount; + u32 status1, status2; + int instance; + int group_id; + struct fimc_is_core *core; + + BUG_ON(!data); + + itf = container_of(data, struct fimc_is_interface, + work_wq[INTR_SHOT_DONE]); + work_list = &itf->work_list[INTR_SHOT_DONE]; + group = NULL; + vctx = NULL; + grp_framemgr = NULL; + + get_req_work(work_list, &work); + while (work) { + core = (struct fimc_is_core *)itf->core; + instance = work->msg.instance; + group_id = work->msg.group; + device = &((struct fimc_is_core *)itf->core)->ischain[instance]; + groupmgr = device->groupmgr; + + msg = &work->msg; + fcount = msg->parameter1; + status1 = msg->parameter2; + status2 = msg->parameter3; + + switch (group_id) { + case GROUP_ID(GROUP_ID_3A0): + /* if there's no entry to 3a1 */ + if (GET_FIMC_IS_NUM_OF_SUBIP(core, 3a0)) { + req_flag = REQ_3AA_SHOT; + group = &device->group_3aa; + } else { + req_flag = REQ_ISP_SHOT; + group = &device->group_isp; + } + break; + case GROUP_ID(GROUP_ID_3A1): + req_flag = REQ_3AA_SHOT; + group = &device->group_3aa; + break; + case GROUP_ID(GROUP_ID_ISP): + req_flag = REQ_ISP_SHOT; + group = &device->group_isp; + break; + case GROUP_ID(GROUP_ID_DIS): + req_flag = REQ_DIS_SHOT; + group = &device->group_dis; + break; + default: + merr("unresolved group id %d", device, group_id); + group = NULL; + vctx = NULL; + grp_framemgr = NULL; + goto remain; + } + + if (!group) { + merr("group is NULL", device); + goto remain; + } + + vctx = group->leader.vctx; + if (!vctx) { + merr("vctx is NULL", device); + goto remain; + } + + grp_framemgr = GET_SRC_FRAMEMGR(vctx); + if (!grp_framemgr) { + merr("grp_framemgr is NULL", device); + goto remain; + } + + framemgr_e_barrier_irqs(grp_framemgr, FMGR_IDX_7, flags); + + fimc_is_frame_process_head(grp_framemgr, &frame); + + if (frame) { +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + do_gettimeofday(&frame->time_shotdone); +#endif +#ifdef EXTERNAL_TIME + do_gettimeofday(&frame->tzone[TM_SHOT_D]); +#endif +#endif + + clear_bit(req_flag, &frame->req_flag); + if (frame->req_flag) + merr("group(%d) req flag is not clear all(%X)", + device, group->id, (u32)frame->req_flag); + +#ifdef ENABLE_CLOCK_GATE + /* dynamic clock off */ + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_set(core, group->id, false, false, true); +#endif + wq_func_group(groupmgr, group, grp_framemgr, frame, + vctx, status1, status2, fcount); + } else { + merr("GRP%d DONE(%d) is occured without request", + device, group->id, fcount); + fimc_is_frame_print_all(grp_framemgr); + } + + framemgr_x_barrier_irqr(grp_framemgr, FMGR_IDX_7, flags); +#ifdef ENABLE_CLOCK_GATE + if (fcount == 1 && + sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_lock_set(core, instance, false); +#endif +remain: + set_free_work(work_list, work); + get_req_work(work_list, &work); + } +} + +static inline void wq_func_schedule(struct fimc_is_interface *itf, + struct work_struct *work_wq) +{ + if (itf->workqueue) + queue_work(itf->workqueue, work_wq); + else + schedule_work(work_wq); +} + +static void interface_timer(unsigned long data) +{ + u32 shot_count, scount_3ax, scount_isp; + u32 fcount, i; + unsigned long flags; + struct fimc_is_interface *itf = (struct fimc_is_interface *)data; + struct fimc_is_core *core; + struct fimc_is_device_ischain *device; + struct fimc_is_device_sensor *sensor; + struct fimc_is_framemgr *framemgr; + struct fimc_is_work_list *work_list; + struct work_struct *work_wq; + + BUG_ON(!itf); + BUG_ON(!itf->core); + + if (!test_bit(IS_IF_STATE_OPEN, &itf->state)) { + pr_info("shot timer is terminated\n"); + return; + } + + core = itf->core; + + for (i = 0; i < FIMC_IS_MAX_NODES; ++i) { + device = &core->ischain[i]; + shot_count = 0; + scount_3ax = 0; + scount_isp = 0; + + sensor = device->sensor; + if (!sensor) + continue; + + if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state)) + continue; + + if (test_bit(FIMC_IS_ISCHAIN_OPEN_SENSOR, &device->state)) { + spin_lock_irq(&itf->shot_check_lock); + if (atomic_read(&itf->shot_check[i])) { + atomic_set(&itf->shot_check[i], 0); + atomic_set(&itf->shot_timeout[i], 0); + spin_unlock_irq(&itf->shot_check_lock); + continue; + } + spin_unlock_irq(&itf->shot_check_lock); + + if (test_bit(FIMC_IS_GROUP_READY, &device->group_3aa.state)) { + framemgr = GET_GROUP_FRAMEMGR(&device->group_3aa); + framemgr_e_barrier_irqs(framemgr, 0, flags); + scount_3ax = framemgr->frame_pro_cnt; + shot_count += scount_3ax; + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + + if (test_bit(FIMC_IS_GROUP_READY, &device->group_isp.state)) { + framemgr = GET_GROUP_FRAMEMGR(&device->group_isp); + framemgr_e_barrier_irqs(framemgr, 0, flags); + scount_isp = framemgr->frame_pro_cnt; + shot_count += scount_isp; + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + + if (test_bit(FIMC_IS_GROUP_READY, &device->group_dis.state)) { + framemgr = GET_GROUP_FRAMEMGR(&device->group_dis); + framemgr_e_barrier_irqs(framemgr, 0, flags); + shot_count += framemgr->frame_pro_cnt; + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + } + + if (shot_count) { + atomic_inc(&itf->shot_timeout[i]); + pr_err ("shot timer[%d] is increased to %d\n", i, + atomic_read(&itf->shot_timeout[i])); + } + + if (atomic_read(&itf->shot_timeout[i]) > TRY_TIMEOUT_COUNT) { + merr("shot command is timeout(%d, %d(%d+%d))", device, + atomic_read(&itf->shot_timeout[i]), + shot_count, scount_3ax, scount_isp); + + pr_err("\n### 3ax framemgr info ###\n"); + if (scount_3ax) { + framemgr = GET_GROUP_FRAMEMGR(&device->group_3aa); + fimc_is_frame_print_all(framemgr); + } + + pr_err("\n### isp framemgr info ###\n"); + if (scount_isp) { + framemgr = GET_GROUP_FRAMEMGR(&device->group_isp); + fimc_is_frame_print_all(framemgr); + } + + pr_err("\n### work list info ###\n"); + work_list = &itf->work_list[INTR_SHOT_DONE]; + print_fre_work_list(work_list); + print_req_work_list(work_list); + + if (work_list->work_request_cnt > 0) { + pr_err("\n### processing work lately ###\n"); + work_wq = &itf->work_wq[INTR_SHOT_DONE]; + wq_func_shot(work_wq); + + atomic_set(&itf->shot_check[i], 0); + atomic_set(&itf->shot_timeout[i], 0); + } else { + fimc_is_hw_logdump(itf); + fimc_is_hw_regdump(itf); + + if (itf->need_iflag && + readl(&itf->com_regs->shot_iflag)) { + pr_err("\n### MCUCTL check ###\n"); + fimc_is_clr_intr(itf, INTR_SHOT_DONE); + fimc_is_hw_regdump(itf); + } +#ifdef BUG_ON_ENABLE + BUG(); +#endif + return; + } + } + } + + for (i = 0; i < FIMC_IS_MAX_NODES; ++i) { + sensor = &core->sensor[i]; + + if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state)) + continue; + + fcount = fimc_is_sensor_g_fcount(sensor); + if (fcount == atomic_read(&itf->sensor_check[i])) { + atomic_inc(&itf->sensor_timeout[i]); + pr_err ("sensor timer[%d] is increased to %d(fcount : %d)\n", i, + atomic_read(&itf->sensor_timeout[i]), fcount); + } else { + atomic_set(&itf->sensor_timeout[i], 0); + atomic_set(&itf->sensor_check[i], fcount); + } + + if (atomic_read(&itf->sensor_timeout[i]) > SENSOR_TIMEOUT_COUNT) { + merr("sensor is timeout(%d, %d)", sensor, + atomic_read(&itf->sensor_timeout[i]), + atomic_read(&itf->sensor_check[i])); + + /* print sensor clk , pwr state */ + CALL_POPS(core, print_clk, device->pdev); + CALL_POPS(core, print_pwr, device->pdev); + + /* print sensor info */ + merr("sensor fcount : %d , framerate : %d\n", sensor, + fimc_is_sensor_g_fcount(sensor), + fimc_is_sensor_g_framerate(sensor)); + merr("sensor w: %d , h: %d, bns_w: %d, bns_h: %d\n", sensor, + fimc_is_sensor_g_width(sensor), + fimc_is_sensor_g_height(sensor), + fimc_is_sensor_g_bns_width(sensor), + fimc_is_sensor_g_bns_height(sensor)); + + fimc_is_hw_logdump(itf); + fimc_is_hw_regdump(itf); +#ifdef BUG_ON_ENABLE + BUG(); +#endif + return; + } + } + + mod_timer(&itf->timer, jiffies + (FIMC_IS_COMMAND_TIMEOUT/TRY_TIMEOUT_COUNT)); +} + +static irqreturn_t interface_isr(int irq, void *data) +{ + struct fimc_is_interface *itf; + struct work_struct *work_wq; + struct fimc_is_work_list *work_list; + struct fimc_is_work *work; + u32 status; + + itf = (struct fimc_is_interface *)data; + status = fimc_is_get_intr(itf); + + if (status & (1<work_wq[INTR_SHOT_DONE]; + work_list = &itf->work_list[INTR_SHOT_DONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_SHOT_DONE); + work->fcount = work->msg.parameter1; + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else + err("free work item is empty5"); + + status &= ~(1<work_wq[INTR_GENERAL]; + work_list = &itf->work_list[INTR_GENERAL]; + + get_free_work_irq(&itf->work_list[INTR_GENERAL], &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_GENERAL); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else + err("free work item is empty1"); + + status &= ~(1<work_wq[INTR_3A0C_FDONE]; + work_list = &itf->work_list[INTR_3A0C_FDONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_3A0C_FDONE); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else + err("free work item is empty2"); + + status &= ~(1<work_wq[INTR_3A1C_FDONE]; + work_list = &itf->work_list[INTR_3A1C_FDONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_3A1C_FDONE); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else + err("free work item is empty2"); + + status &= ~(1<work_wq[INTR_SCC_FDONE]; + work_list = &itf->work_list[INTR_SCC_FDONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_SCC_FDONE); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else + err("free work item is empty2"); + + status &= ~(1<work_wq[INTR_DIS_FDONE]; + work_list = &itf->work_list[INTR_DIS_FDONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_DIS_FDONE); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else { + err("free work item is empty3"); + } + + status &= ~(1<work_wq[INTR_SCP_FDONE]; + work_list = &itf->work_list[INTR_SCP_FDONE]; + + get_free_work_irq(work_list, &work); + if (work) { + fimc_is_get_cmd(itf, &work->msg, INTR_SCP_FDONE); + set_req_work_irq(work_list, work); + + if (!work_pending(work_wq)) + wq_func_schedule(itf, work_wq); + } else { + err("free work item is empty4"); + } + + status &= ~(1<lock_wait_queue); + init_waitqueue_head(&this->init_wait_queue); + init_waitqueue_head(&this->idle_wait_queue); + spin_lock_init(&this->shot_check_lock); + + this->workqueue = alloc_workqueue("fimc-is/highpri", WQ_HIGHPRI, 0); + if (!this->workqueue) + warn("failed to alloc own workqueue, will be use global one"); + + INIT_WORK(&this->work_wq[INTR_GENERAL], wq_func_general); + INIT_WORK(&this->work_wq[INTR_3A0C_FDONE], wq_func_3a0c); + INIT_WORK(&this->work_wq[INTR_3A1C_FDONE], wq_func_3a1c); + INIT_WORK(&this->work_wq[INTR_SCC_FDONE], wq_func_scc); + INIT_WORK(&this->work_wq[INTR_DIS_FDONE], wq_func_dis); + INIT_WORK(&this->work_wq[INTR_SCP_FDONE], wq_func_scp); + INIT_WORK(&this->work_wq[INTR_SHOT_DONE], wq_func_shot); + + this->regs = (void *)regs; + this->com_regs = (struct is_common_reg *)(regs + ISSR0); + + if (GET_FIMC_IS_VER_OF_SUBIP(core, mcuctl) < VERSION_OF_NO_NEED_IFLAG) + this->need_iflag = true; + else + this->need_iflag = false; + + ret = request_irq(irq, interface_isr, 0, "mcuctl", this); + if (ret) + err("request_irq failed\n"); + + notify_fcount_sen0 = &this->com_regs->fcount_sen0; + notify_fcount_sen1 = &this->com_regs->fcount_sen1; + notify_fcount_sen2 = &this->com_regs->fcount_sen2; + last_fcount1 = &this->com_regs->fcount_sen3; + last_fcount0 = &this->com_regs->grp1_done_frame_num; + this->core = (void *)core; + clear_bit(IS_IF_STATE_OPEN, &this->state); + clear_bit(IS_IF_STATE_START, &this->state); + clear_bit(IS_IF_STATE_BUSY, &this->state); + clear_bit(IS_IF_STATE_READY, &this->state); + + init_work_list(&this->nblk_cam_ctrl, + TRACE_WORK_ID_CAMCTRL, MAX_NBLOCKING_COUNT); + init_work_list(&this->work_list[INTR_GENERAL], + TRACE_WORK_ID_GENERAL, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_3A0C_FDONE], + TRACE_WORK_ID_3A0C, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_3A1C_FDONE], + TRACE_WORK_ID_3A1C, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_SCC_FDONE], + TRACE_WORK_ID_SCC, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_DIS_FDONE], + TRACE_WORK_ID_DIS, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_SCP_FDONE], + TRACE_WORK_ID_SCP, MAX_WORK_COUNT); + init_work_list(&this->work_list[INTR_SHOT_DONE], + TRACE_WORK_ID_SHOT, MAX_WORK_COUNT); + + this->err_report_vendor = NULL; +#ifdef MEASURE_TIME +#ifdef INTERFACE_TIME + { + u32 i; + for (i = 0; i < HIC_COMMAND_END; ++i) + measure_init(&this->time[i], i); + } +#endif +#endif + + return ret; +} + +int fimc_is_interface_open(struct fimc_is_interface *this) +{ + int i; + int ret = 0; + + if (test_bit(IS_IF_STATE_OPEN, &this->state)) { + err("already open"); + ret = -EMFILE; + goto exit; + } + + dbg_interface("%s\n", __func__); + + for (i = 0; i < FIMC_IS_MAX_NODES; i++) { + this->streaming[i] = IS_IF_STREAMING_INIT; + this->processing[i] = IS_IF_PROCESSING_INIT; + atomic_set(&this->shot_check[i], 0); + atomic_set(&this->shot_timeout[i], 0); + atomic_set(&this->sensor_check[i], 0); + atomic_set(&this->sensor_timeout[i], 0); + } + this->pdown_ready = IS_IF_POWER_DOWN_READY; + atomic_set(&this->lock_pid, 0); + clear_bit(IS_IF_STATE_START, &this->state); + clear_bit(IS_IF_STATE_BUSY, &this->state); + clear_bit(IS_IF_STATE_READY, &this->state); + + init_timer(&this->timer); + this->timer.expires = jiffies + + (FIMC_IS_COMMAND_TIMEOUT/TRY_TIMEOUT_COUNT); + this->timer.data = (unsigned long)this; + this->timer.function = interface_timer; + add_timer(&this->timer); + + set_bit(IS_IF_STATE_OPEN, &this->state); + +exit: + return ret; +} + +int fimc_is_interface_close(struct fimc_is_interface *this) +{ + int ret = 0; + int retry; + + if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { + err("already close"); + ret = -EMFILE; + goto exit; + } + + retry = 10; + while (test_busystate(this) && retry) { + err("interface is busy"); + msleep(20); + retry--; + } + if (!retry) + err("waiting idle is fail"); + + del_timer_sync(&this->timer); + dbg_interface("%s\n", __func__); + + clear_bit(IS_IF_STATE_OPEN, &this->state); + +exit: + return ret; +} + +void fimc_is_interface_lock(struct fimc_is_interface *this) +{ + atomic_set(&this->lock_pid, current->pid); +} + +void fimc_is_interface_unlock(struct fimc_is_interface *this) +{ + atomic_set(&this->lock_pid, 0); + wake_up(&this->lock_wait_queue); +} + +void fimc_is_interface_reset(struct fimc_is_interface *this) +{ + if (this->need_iflag) { + writel(0, &this->com_regs->ihcmd_iflag); + writel(0, &this->com_regs->taa0c_iflag); + writel(0, &this->com_regs->taa1c_iflag); + writel(0, &this->com_regs->scc_iflag); + writel(0, &this->com_regs->dis_iflag); + writel(0, &this->com_regs->scp_iflag); + writel(0, &this->com_regs->shot_iflag); + } +} + +int fimc_is_hw_logdump(struct fimc_is_interface *this) +{ + int debug_cnt, sentence_i; + char *debug; + char letter; + char sentence[250]; + int count = 0, i; + struct fimc_is_core *core; + + if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { + warn("interface is closed"); + count = -EINVAL; + goto p_err; + } + + pr_err("\n### firmware messsage dump ###\n"); + + core = (struct fimc_is_core *)this->core; + sentence_i = 0; + + vb2_ion_sync_for_device(core->minfo.fw_cookie, + DEBUG_OFFSET, DEBUG_CNT, DMA_FROM_DEVICE); + + debug = (char *)(core->minfo.kvaddr + DEBUG_OFFSET); + debug_cnt = *((int *)(core->minfo.kvaddr + DEBUGCTL_OFFSET)) + - DEBUG_OFFSET; + + if (core->debug_cnt > debug_cnt) + count = (DEBUG_CNT - core->debug_cnt) + debug_cnt; + else + count = debug_cnt - core->debug_cnt; + + if (count) { + printk(KERN_ERR "start(%d %d)\n", debug_cnt, count); + for (i = core->debug_cnt; count > 0; count--) { + letter = debug[i]; + if (letter) { + sentence[sentence_i] = letter; + if (sentence_i >= 247) { + sentence[sentence_i + 1] = '\n'; + sentence[sentence_i + 2] = 0; + printk(KERN_ERR "%s", sentence); + sentence_i = 0; + } else if (letter == '\n') { + sentence[sentence_i + 1] = 0; + printk(KERN_ERR "%s", sentence); + sentence_i = 0; + } else { + sentence_i++; + } + } + i++; + if (i > DEBUG_CNT) + i = 0; + } + core->debug_cnt = debug_cnt; + printk(KERN_ERR "end\n"); + } + +p_err: + return count; +} + +int fimc_is_hw_regdump(struct fimc_is_interface *this) +{ + int ret = 0; + u32 i; + void __iomem *regs; + + if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { + warn("interface is closed"); + ret = -EINVAL; + goto p_err; + } + + info("\n### MCUCTL Raw Dump ###\n"); + regs = (this->regs); + for (i = 0; i < 16; ++i) + info("MCTL Raw[%d] : %08X\n", i, readl(regs + (4 * i))); + + info("\n### COMMON REGS dump ###\n"); + regs = this->com_regs; + for (i = 0; i < 64; ++i) + info("MCTL[%d] : %08X\n", i, readl(regs + (4 * i))); + +p_err: + return ret; +} + +int fimc_is_hw_memdump(struct fimc_is_interface *this, + u32 start, + u32 end) +{ + int ret = 0; + u32 *cur; + u32 items, offset; + char term[50], sentence[250]; + + if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { + warn("interface is closed"); + ret = -EINVAL; + goto p_err; + } + + cur = (u32 *)start; + items = 0; + offset = 0; + + memset(sentence, 0, sizeof(sentence)); + printk(KERN_ERR "Memory Dump(0x%08X ~ 0x%08X)\n", start, end); + + while ((u32)cur <= end) { + if (!(items % 8)) { + printk(KERN_ERR "%s", sentence); + offset = 0; + snprintf(term, sizeof(term), "%p: ", cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + } + + snprintf(term, sizeof(term), "%08X ", *cur); + snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); + offset += strlen(term); + cur++; + items++; + } + + ret = (u32)cur - end; + +p_err: + return ret; +} + +int fimc_is_hw_msg_test(struct fimc_is_interface *this, u32 sync_id, u32 msg_test_id) +{ + int ret = 0; + struct fimc_is_work work; + struct fimc_is_msg *msg; + + dbg_interface("msg_test_nblk(%d)\n", msg_test_id); + + msg = &work.msg; + msg->id = 0; + msg->command = HIC_MSG_TEST; + msg->instance = 0; + msg->group = 0; + msg->parameter1 = msg_test_id; + msg->parameter2 = sync_id; + msg->parameter3 = 0; + msg->parameter4 = 0; + + ret = fimc_is_set_cmd_nblk(this, &work); + + return ret; +} + +int fimc_is_hw_enum(struct fimc_is_interface *this) +{ + int ret = 0; + struct fimc_is_msg msg; + volatile struct is_common_reg __iomem *com_regs; + + dbg_interface("enum()\n"); + + /* check if hw_enum is already operated */ + if (test_bit(IS_IF_STATE_READY, &this->state)) + goto exit; + + ret = wait_initstate(this); + if (ret) { + err("enum time out"); + ret = -ETIME; + goto exit; + } + + msg.id = 0; + msg.command = ISR_DONE; + msg.instance = 0; + msg.group = 0; + msg.parameter1 = IHC_GET_SENSOR_NUMBER; + /* + * this mean sensor numbers + * signel sensor: 1, dual sensor: 2, triple sensor: 3 + */ + msg.parameter2 = 3; + msg.parameter3 = 0; + msg.parameter4 = 0; + + waiting_is_ready(this); + com_regs = this->com_regs; + writel(msg.command, &com_regs->hicmd); + writel(msg.instance, &com_regs->hic_sensorid); + writel(msg.parameter1, &com_regs->hic_param1); + writel(msg.parameter2, &com_regs->hic_param2); + writel(msg.parameter3, &com_regs->hic_param3); + writel(msg.parameter4, &com_regs->hic_param4); + send_interrupt(this); + + set_bit(IS_IF_STATE_READY, &this->state); + +exit: + return ret; +} + +int fimc_is_hw_saddr(struct fimc_is_interface *this, + u32 instance, u32 *setfile_addr) +{ + int ret = 0; + struct fimc_is_msg msg, reply; + + dbg_interface("saddr(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_GET_SET_FILE_ADDR; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + *setfile_addr = reply.parameter2; + + return ret; +} + +int fimc_is_hw_setfile(struct fimc_is_interface *this, + u32 instance) +{ + int ret = 0; + struct fimc_is_msg msg, reply; + + dbg_interface("setfile(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_LOAD_SET_FILE; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_open(struct fimc_is_interface *this, + u32 instance, + u32 module_id, + u32 info, + u32 group, + u32 flag, + u32 *mwidth, + u32 *mheight) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("open(%d,%d,%08X)\n", module_id, group, flag); + + msg.id = 0; + msg.command = HIC_OPEN_SENSOR; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = module_id; + msg.parameter2 = info; + msg.parameter3 = group; + msg.parameter4 = flag; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + *mwidth = reply.parameter2; + *mheight = reply.parameter3; + + return ret; +} + +int fimc_is_hw_close(struct fimc_is_interface *this, + u32 instance) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("close sensor(instance:%d)\n", instance); + + msg.id = 0; + msg.command = HIC_CLOSE_SENSOR; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_stream_on(struct fimc_is_interface *this, + u32 instance) +{ + int ret; + struct fimc_is_msg msg, reply; + + BUG_ON(!this); + + dbg_interface("stream_on(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_STREAM_ON; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_stream_off(struct fimc_is_interface *this, + u32 instance) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("stream_off(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_STREAM_OFF; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_process_on(struct fimc_is_interface *this, + u32 instance, u32 group) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("process_on(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_PROCESS_START; + msg.instance = instance; + msg.group = group; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_process_off(struct fimc_is_interface *this, + u32 instance, u32 group, u32 mode) +{ + int ret; + struct fimc_is_msg msg, reply; + + WARN_ON(mode >= 2); + + dbg_interface("process_off(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_PROCESS_STOP; + msg.instance = instance; + msg.group = group; + msg.parameter1 = mode; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_i2c_lock(struct fimc_is_interface *this, + u32 instance, int i2c_clk, bool lock) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("i2c_lock(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_I2C_CONTROL_LOCK; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = lock; + msg.parameter2 = i2c_clk; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_s_param(struct fimc_is_interface *this, + u32 instance, u32 lindex, u32 hindex, u32 indexes) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("s_param(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_SET_PARAMETER; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = ISS_PREVIEW_STILL; + msg.parameter2 = indexes; + msg.parameter3 = lindex; + msg.parameter4 = hindex; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_a_param(struct fimc_is_interface *this, + u32 instance, u32 group, u32 sub_mode) +{ + int ret = 0; + struct fimc_is_msg msg, reply; + + dbg_interface("a_param(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_PREVIEW_STILL; + msg.instance = instance; + msg.group = group; + msg.parameter1 = sub_mode; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_g_capability(struct fimc_is_interface *this, + u32 instance, u32 address) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("g_capability(%d)\n", instance); + + msg.id = 0; + msg.command = HIC_GET_STATIC_METADATA; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = address; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_map(struct fimc_is_interface *this, + u32 instance, u32 group, u32 address, u32 size) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("[%d][%d] map(%08X, %X)\n", instance, group, address, size); + + msg.id = 0; + msg.command = HIC_SET_A5_MAP; + msg.instance = instance; + msg.group = group; + msg.parameter1 = address; + msg.parameter2 = size; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_unmap(struct fimc_is_interface *this, + u32 instance, u32 group) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("[%d][%d] unmap\n", instance, group); + + msg.id = 0; + msg.command = HIC_SET_A5_UNMAP; + msg.instance = instance; + msg.group = group; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +int fimc_is_hw_power_down(struct fimc_is_interface *this, + u32 instance) +{ + int ret = 0; + struct fimc_is_msg msg, reply; + + dbg_interface("pwr_down(%d)\n", instance); + + if (!test_bit(IS_IF_STATE_START, &this->state)) { + warn("instance(%d): FW is not initialized, wait\n", instance); + ret = fimc_is_hw_enum(this); + if (ret) + err("fimc_is_itf_enum is fail(%d)", ret); + } + + msg.id = 0; + msg.command = HIC_POWER_DOWN; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = 0; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} + +#if (FW_HAS_SYS_CTRL_CMD) +int fimc_is_hw_sys_ctl(struct fimc_is_interface *this, + u32 instance, int cmd, int val) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("sys_ctl(cmd(%d), val(%d))\n", cmd, val); + + msg.id = 0; + msg.command = HIC_SYSTEM_CONTROL; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = cmd; + msg.parameter2 = val; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} +#else +int fimc_is_hw_sys_ctl(struct fimc_is_interface *this, + u32 instance, int cmd, int val) +{ + return 0; +} +#endif + +#if (FW_HAS_SENSOR_MODE_CMD) +int fimc_is_hw_sensor_mode(struct fimc_is_interface *this, + u32 instance, int cfg) +{ + int ret; + struct fimc_is_msg msg, reply; + + dbg_interface("sensor mode(%d): %d\n", instance, cfg); + + msg.id = 0; + msg.command = HIC_SENSOR_MODE_CHANGE; + msg.instance = instance; + msg.group = 0; + msg.parameter1 = cfg; + msg.parameter2 = 0; + msg.parameter3 = 0; + msg.parameter4 = 0; + + ret = fimc_is_set_cmd(this, &msg, &reply); + + return ret; +} +#else +int fimc_is_hw_sensor_mode(struct fimc_is_interface *this, + u32 instance, int cfg) +{ + return 0; +} +#endif + +int fimc_is_hw_shot_nblk(struct fimc_is_interface *this, + u32 instance, u32 group, u32 bayer, u32 shot, u32 fcount, u32 rcount) +{ + int ret = 0; + struct fimc_is_msg msg; + + /*dbg_interface("shot_nblk(%d, %d)\n", instance, fcount);*/ + + msg.id = 0; + msg.command = HIC_SHOT; + msg.instance = instance; + msg.group = group; + msg.parameter1 = bayer; + msg.parameter2 = shot; + msg.parameter3 = fcount; + msg.parameter4 = rcount; + + ret = fimc_is_set_cmd_shot(this, &msg); + + return ret; +} + +int fimc_is_hw_s_camctrl_nblk(struct fimc_is_interface *this, + u32 instance, u32 address, u32 fcount) +{ + int ret = 0; + struct fimc_is_work *work; + struct fimc_is_msg *msg; + + dbg_interface("cam_ctrl_nblk(%d)\n", instance); + + get_free_work(&this->nblk_cam_ctrl, &work); + + if (work) { + work->fcount = fcount; + msg = &work->msg; + msg->id = 0; + msg->command = HIC_SET_CAM_CONTROL; + msg->instance = instance; + msg->group = 0; + msg->parameter1 = address; + msg->parameter2 = fcount; + msg->parameter3 = 0; + msg->parameter4 = 0; + + ret = fimc_is_set_cmd_nblk(this, work); + } else { + err("g_free_nblk return NULL"); + print_fre_work_list(&this->nblk_cam_ctrl); + print_req_work_list(&this->nblk_cam_ctrl); + ret = 1; + } + + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-interface.h b/drivers/media/platform/exynos/fimc-is/fimc-is-interface.h new file mode 100644 index 000000000000..4c814dd6db1d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-interface.h @@ -0,0 +1,230 @@ +/* + * drivers/media/video/exynos/fimc-is-mc2/fimc-is-interface.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * The header file related to camera + * + * 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-metadata.h" +#include "fimc-is-framemgr.h" +#include "fimc-is-video.h" +#include "fimc-is-time.h" +#include "fimc-is-cmd.h" + +/*#define TRACE_WORK*/ +/* cam_ctrl : 1 + shot : 2 */ +#define TRACE_WORK_ID_CAMCTRL 0x1 +#define TRACE_WORK_ID_SHOT 0x2 +#define TRACE_WORK_ID_GENERAL 0x4 +#define TRACE_WORK_ID_3A0C 0x8 +#define TRACE_WORK_ID_3A1C 0x10 +#define TRACE_WORK_ID_SCC 0x20 +#define TRACE_WORK_ID_DIS 0x40 +#define TRACE_WORK_ID_SCP 0x80 +#define TRACE_WORK_ID_MASK 0xFF + +#define MAX_NBLOCKING_COUNT 3 +#define MAX_WORK_COUNT 10 + +#define TRY_TIMEOUT_COUNT 2 +#define SENSOR_TIMEOUT_COUNT 2 +#define TRY_RECV_AWARE_COUNT 100 + +#define LOWBIT_OF(num) (num >= 32 ? 0 : (u32)1<= 32 ? (u32)1<<(num-32) : 0) + +enum fimc_is_interface_state { + IS_IF_STATE_OPEN, + IS_IF_STATE_START, + IS_IF_STATE_BUSY, + IS_IF_STATE_READY +}; + +enum interrupt_map { + INTR_GENERAL = 0, + INTR_ISP_FDONE = 1, + INTR_3A0C_FDONE = 2, + INTR_3A1C_FDONE = 3, + INTR_SCC_FDONE = 4, + INTR_DIS_FDONE = 5, + INTR_SCP_FDONE = 6, + INTR_SHOT_DONE = 7, + INTR_MAX_MAP +}; + +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 group; + u32 parameter1; + u32 parameter2; + u32 parameter3; + u32 parameter4; +}; + +struct fimc_is_work { + struct list_head list; + struct fimc_is_msg msg; + u32 fcount; + struct fimc_is_frame *frame; +}; + +struct fimc_is_work_list { + u32 id; + struct fimc_is_work work[MAX_WORK_COUNT]; + spinlock_t slock_free; + spinlock_t slock_request; + struct list_head work_free_head; + u32 work_free_cnt; + struct list_head work_request_head; + u32 work_request_cnt; + wait_queue_head_t wait_queue; +}; + +struct fimc_is_interface { + void __iomem *regs; + struct is_common_reg __iomem *com_regs; + bool need_iflag; + unsigned long state; + spinlock_t process_barrier; + struct mutex request_barrier; + + atomic_t lock_pid; + wait_queue_head_t lock_wait_queue; + wait_queue_head_t init_wait_queue; + wait_queue_head_t idle_wait_queue; + struct fimc_is_msg reply; +#ifdef MEASURE_TIME +#ifdef INTERFACE_TIME + struct fimc_is_interface_time time[HIC_COMMAND_END]; +#endif +#endif + + struct workqueue_struct *workqueue; + struct work_struct work_wq[INTR_MAX_MAP]; + struct fimc_is_work_list work_list[INTR_MAX_MAP]; + + /* sensor streaming flag */ + enum streaming_state streaming[FIMC_IS_MAX_NODES]; + /* firmware processing flag */ + enum processing_state processing[FIMC_IS_MAX_NODES]; + /* frrmware power down ready flag */ + enum pdown_ready_state pdown_ready; + + struct fimc_is_framemgr *framemgr; + + struct fimc_is_work_list nblk_cam_ctrl; + + /* shot timeout check */ + spinlock_t shot_check_lock; + atomic_t shot_check[FIMC_IS_MAX_NODES]; + atomic_t shot_timeout[FIMC_IS_MAX_NODES]; + /* sensor timeout check */ + atomic_t sensor_check[FIMC_IS_MAX_NODES]; + atomic_t sensor_timeout[FIMC_IS_MAX_NODES]; + struct timer_list timer; + + /* callback func to handle error report for specific purpose */ + void *err_report_data; + int (*err_report_vendor)(void *data, u32 err_report_type); + + struct camera2_uctl isp_peri_ctl; + void *core; +}; + +int fimc_is_interface_probe(struct fimc_is_interface *this, + u32 regs, + u32 irq, + void *core_data); +int fimc_is_interface_open(struct fimc_is_interface *this); +int fimc_is_interface_close(struct fimc_is_interface *this); +void fimc_is_interface_lock(struct fimc_is_interface *this); +void fimc_is_interface_unlock(struct fimc_is_interface *this); +void fimc_is_interface_reset(struct fimc_is_interface *this); + +/*for debugging*/ +int print_fre_work_list(struct fimc_is_work_list *this); +int print_req_work_list(struct fimc_is_work_list *this); + +int fimc_is_hw_msg_test(struct fimc_is_interface *this, u32 sync_id, u32 msg_test_id); +int fimc_is_hw_logdump(struct fimc_is_interface *this); +int fimc_is_hw_regdump(struct fimc_is_interface *this); +int fimc_is_hw_memdump(struct fimc_is_interface *this, + u32 start, + u32 end); +int fimc_is_hw_enum(struct fimc_is_interface *this); +int fimc_is_hw_open(struct fimc_is_interface *this, + u32 instance, u32 module, u32 info, u32 group, u32 flag, + u32 *mwidth, u32 *mheight); +int fimc_is_hw_close(struct fimc_is_interface *this, + u32 instance); +int fimc_is_hw_saddr(struct fimc_is_interface *interface, + u32 instance, u32 *setfile_addr); +int fimc_is_hw_setfile(struct fimc_is_interface *interface, + u32 instance); +int fimc_is_hw_process_on(struct fimc_is_interface *this, + u32 instance, u32 group); +int fimc_is_hw_process_off(struct fimc_is_interface *this, + u32 instance, u32 group, u32 mode); +int fimc_is_hw_stream_on(struct fimc_is_interface *interface, + u32 instance); +int fimc_is_hw_stream_off(struct fimc_is_interface *interface, + u32 instance); +int fimc_is_hw_s_param(struct fimc_is_interface *interface, + u32 instance, u32 lindex, u32 hindex, u32 indexes); +int fimc_is_hw_a_param(struct fimc_is_interface *this, + u32 instance, u32 group, u32 sub_mode); +int fimc_is_hw_g_capability(struct fimc_is_interface *this, + u32 instance, u32 address); +int fimc_is_hw_map(struct fimc_is_interface *this, + u32 instance, u32 group, u32 address, u32 size); +int fimc_is_hw_unmap(struct fimc_is_interface *this, + u32 instance, u32 group); +int fimc_is_hw_power_down(struct fimc_is_interface *interface, + u32 instance); +int fimc_is_hw_i2c_lock(struct fimc_is_interface *interface, + u32 instance, int clk, bool lock); +int fimc_is_hw_sys_ctl(struct fimc_is_interface *this, + u32 instance, int cmd, int val); +int fimc_is_hw_sensor_mode(struct fimc_is_interface *this, + u32 instance, int cfg); + +int fimc_is_hw_shot_nblk(struct fimc_is_interface *this, + u32 instance, u32 group, u32 bayer, u32 shot, u32 fcount, u32 rcount); +int fimc_is_hw_s_camctrl_nblk(struct fimc_is_interface *this, + u32 instance, u32 address, u32 fcount); + +/* func to register error report callback */ +int fimc_is_set_err_report_vendor(struct fimc_is_interface *itf, + void *err_report_data, + int (*err_report_vendor)(void *data, u32 err_report_type)); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-mem.c b/drivers/media/platform/exynos/fimc-is/fimc-is-mem.c new file mode 100644 index 000000000000..30d5dabc0111 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-mem.c @@ -0,0 +1,100 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) +#include +#else +#include +#endif + +static void *fimc_is_ion_init(struct platform_device *pdev) +{ + return vb2_ion_create_context(&pdev->dev, SZ_4K, + VB2ION_CTX_IOMMU | + VB2ION_CTX_VMCONTIG | + VB2ION_CTX_KVA_ONDEMAND); +} + +static unsigned long plane_addr(struct vb2_buffer *vb, u32 plane_no) +{ + void *cookie = vb2_plane_cookie(vb, plane_no); + dma_addr_t dva = 0; + + WARN_ON(vb2_ion_dma_address(cookie, &dva) != 0); + + return dva; +} + +static unsigned long plane_kvaddr(struct vb2_buffer *vb, u32 plane_no) +{ + void *kvaddr = vb2_plane_vaddr(vb, plane_no); + + return (unsigned long)kvaddr; +} + +const struct fimc_is_vb2 fimc_is_vb2_ion = { + .ops = &vb2_ion_memops, + .init = fimc_is_ion_init, + .cleanup = vb2_ion_destroy_context, + .plane_addr = plane_addr, + .plane_kvaddr = plane_kvaddr, + .resume = vb2_ion_attach_iommu, + .suspend = vb2_ion_detach_iommu, + .set_cacheable = vb2_ion_set_cached, +}; + +int fimc_is_mem_probe(struct fimc_is_mem *this, + struct platform_device *pdev) +{ + u32 ret = 0; + + this->vb2 = &fimc_is_vb2_ion; + + this->alloc_ctx = this->vb2->init(pdev); + if (IS_ERR(this->alloc_ctx)) { + ret = PTR_ERR(this->alloc_ctx); + goto p_err; + } + + /* FIXME: should be different by device type */ + exynos_create_iovmm(&pdev->dev, 1, 4); + +p_err: + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-mem.h b/drivers/media/platform/exynos/fimc-is/fimc-is-mem.h new file mode 100644 index 000000000000..4000162d73de --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-mem.h @@ -0,0 +1,73 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_MEM_H +#define FIMC_IS_MEM_H + +#include +#include +#if defined(CONFIG_VIDEOBUF2_CMA_PHYS) +#include +#elif defined(CONFIG_VIDEOBUF2_ION) +#include +#endif + +struct fimc_is_minfo { + dma_addr_t base; /* buffer base */ + size_t size; /* total length */ + dma_addr_t vaddr_base; /* buffer base */ + dma_addr_t vaddr_curr; /* current addr */ + void *bitproc_buf; + void *fw_cookie; + + u32 dvaddr; + u32 kvaddr; + u32 dvaddr_debug; + u32 kvaddr_debug; + u32 dvaddr_fshared; + u32 kvaddr_fshared; + u32 dvaddr_region; + u32 kvaddr_region; + u32 dvaddr_shared; /*shared region of is region*/ + u32 kvaddr_shared; + u32 dvaddr_odc; + u32 kvaddr_odc; + u32 dvaddr_dis; + u32 kvaddr_dis; + u32 dvaddr_3dnr; + u32 kvaddr_3dnr; +}; + +struct fimc_is_vb2 { + const struct vb2_mem_ops *ops; + void *(*init)(struct platform_device *pdev); + void (*cleanup)(void *alloc_ctx); + + unsigned long (*plane_addr)(struct vb2_buffer *vb, u32 plane_no); + unsigned long (*plane_kvaddr)(struct vb2_buffer *vb, u32 plane_no); + + int (*resume)(void *alloc_ctx); + void (*suspend)(void *alloc_ctx); + + void (*set_cacheable)(void *alloc_ctx, bool cacheable); +}; + +struct fimc_is_mem { + struct platform_device *pdev; + struct vb2_alloc_ctx *alloc_ctx; + + const struct fimc_is_vb2 *vb2; +}; + +int fimc_is_mem_probe(struct fimc_is_mem *this, + struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-metadata.h b/drivers/media/platform/exynos/fimc-is/fimc-is-metadata.h new file mode 100644 index 000000000000..f2f27c76b33a --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-metadata.h @@ -0,0 +1,1472 @@ +/* Copyright (c) 2011 Samsung Electronics Co, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * + + * Alternatively, Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + */ + +#ifndef FIMC_IS_METADATA_H_ +#define FIMC_IS_METADATA_H_ + +#ifndef _LINUX_TYPES_H +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#endif + +struct rational { + uint32_t num; + uint32_t den; +}; + +#define CAMERA2_MAX_AVAILABLE_MODE 21 +#define CAMERA2_MAX_FACES 16 +#define CAMERA2_MAX_VENDER_LENGTH 400 +#define CAPTURE_NODE_MAX 2 +#define CAMERA2_MAX_PDAF_MULTIROI_COLUMN 9 +#define CAMERA2_MAX_PDAF_MULTIROI_ROW 5 + +#define OPEN_MAGIC_NUMBER 0x01020304 +#define SHOT_MAGIC_NUMBER 0x23456789 + +/* + *controls/dynamic metadata +*/ + +/* android.request */ + +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; + uint32_t requestCount; +}; + +struct camera2_request_dm { + uint32_t id; + enum metadata_mode metadataMode; + uint32_t frameCount; + uint32_t requestCount; +}; + +struct camera2_entry_ctl { + /** \brief + per-frame control for entry control + \remarks + low parameter is 0bit ~ 31bit flag + high parameter is 32bit ~ 63bit flag + */ + uint32_t lowIndexParam; + uint32_t highIndexParam; + uint32_t parameter[2048]; +}; + +struct camera2_entry_dm { + uint32_t lowIndexParam; + uint32_t highIndexParam; +}; + +/* android.lens */ + +enum optical_stabilization_mode { + OPTICAL_STABILIZATION_MODE_OFF = 1, + OPTICAL_STABILIZATION_MODE_ON = 2, + OPTICAL_STABILIZATION_MODE_STILL = 3, // Still mode + OPTICAL_STABILIZATION_MODE_STILL_ZOOM = 4, // Still Zoom mode + OPTICAL_STABILIZATION_MODE_VIDEO = 5, // Recording mode + OPTICAL_STABILIZATION_MODE_SINE_X = 6, // factory mode x + OPTICAL_STABILIZATION_MODE_SINE_Y = 7, // factory mode y + OPTICAL_STABILIZATION_MODE_CENTERING = 8 // Centering mode +}; + +enum lens_facing { + LENS_FACING_BACK, + LENS_FACING_FRONT +}; + +struct camera2_lens_ctl { + uint32_t focusDistance; + float aperture; + float focalLength; + float filterDensity; + enum optical_stabilization_mode opticalStabilizationMode; + +}; + +struct camera2_lens_dm { + uint32_t focusDistance; + float aperture; + float focalLength; + float filterDensity; + enum optical_stabilization_mode opticalStabilizationMode; + float focusRange[2]; +}; + +struct camera2_lens_sm { + float minimumFocusDistance; + float hyperfocalDistance; + float availableFocalLength[2]; + float availableApertures; + /*assuming 1 aperture*/ + float availableFilterDensities; + /*assuming 1 ND filter value*/ + enum optical_stabilization_mode availableOpticalStabilization; + /*assuming 1*/ + uint32_t shadingMapSize; + float shadingMap[3][40][30]; + uint32_t geometricCorrectionMapSize; + float geometricCorrectionMap[2][3][40][30]; + enum lens_facing facing; + float position[2]; +}; + +/* android.sensor */ + +enum sensor_colorfilterarrangement { + SENSOR_COLORFILTERARRANGEMENT_RGGB, + SENSOR_COLORFILTERARRANGEMENT_GRBG, + SENSOR_COLORFILTERARRANGEMENT_GBRG, + SENSOR_COLORFILTERARRANGEMENT_BGGR, + SENSOR_COLORFILTERARRANGEMENT_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 exposureTime; + /* unit : nano(It's min frame duration */ + uint64_t frameDuration; + /* unit : percent(need to change ISO value?) */ + uint32_t sensitivity; +}; + +struct camera2_sensor_dm { + uint64_t exposureTime; + uint64_t frameDuration; + uint32_t sensitivity; + uint64_t timeStamp; + uint32_t analogGain; + uint32_t digitalGain; +}; + +struct camera2_sensor_sm { + uint32_t exposureTimeRange[2]; + uint32_t maxFrameDuration; + /* list of available sensitivities. */ + uint32_t availableSensitivities[10]; + enum sensor_colorfilterarrangement colorFilterArrangement; + float physicalSize[2]; + uint32_t pixelArraySize[2]; + uint32_t activeArraySize[4]; + uint32_t whiteLevel; + uint32_t blackLevelPattern[4]; + struct rational colorTransform1[9]; + struct rational colorTransform2[9]; + enum sensor_ref_illuminant referenceIlluminant1; + enum sensor_ref_illuminant referenceIlluminant2; + struct rational forwardMatrix1[9]; + struct rational forwardMatrix2[9]; + struct rational calibrationTransform1[9]; + struct rational calibrationTransform2[9]; + struct rational baseGainFactor; + uint32_t maxAnalogSensitivity; + float noiseModelCoefficients[2]; + uint32_t orientation; +}; + + + +/* android.flash */ + +enum flash_mode { + CAM2_FLASH_MODE_OFF = 1, + CAM2_FLASH_MODE_SINGLE, + CAM2_FLASH_MODE_TORCH, + CAM2_FLASH_MODE_BEST +}; + +enum capture_state { + CAPTURE_STATE_NONE = 0, + CAPTURE_STATE_FLASH = 1, + CAPTURE_STATE_HDR_DARK = 12, + CAPTURE_STATE_HDR_NORMAL = 13, + CAPTURE_STATE_HDR_BRIGHT = 14, + CAPTURE_STATE_ZSL_LIKE = 20, +}; + +struct camera2_flash_ctl { + enum flash_mode flashMode; + uint32_t firingPower; + uint64_t firingTime; +}; + +struct camera2_flash_dm { + enum flash_mode flashMode; + /*10 is max power*/ + uint32_t firingPower; + /*unit : microseconds*/ + uint64_t firingTime; + /*1 : stable, 0 : unstable*/ + uint32_t firingStable; + /*1 : success, 0 : fail*/ + uint32_t decision; + /*0: None, 1 : pre, 2 : main flash ready*/ + uint32_t flashReady; + /*0: None, 1 : pre, 2 : main flash off ready*/ + uint32_t flashOffReady; +}; + +struct camera2_flash_sm { + uint32_t available; + uint64_t chargeDuration; +}; + + +/* android.hotpixel */ + +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; +}; + + + +/* android.demosaic */ + +struct camera2_demosaic_ctl { + enum processing_mode mode; +}; + +struct camera2_demosaic_dm { + enum processing_mode mode; +}; + + + +/* android.noiseReduction */ + +struct camera2_noisereduction_ctl { + enum processing_mode mode; + uint32_t strength; +}; + +struct camera2_noisereduction_dm { + enum processing_mode mode; + uint32_t strength; +}; + + + +/* android.shading */ + +struct camera2_shading_ctl { + enum processing_mode mode; +}; + +struct camera2_shading_dm { + enum processing_mode mode; +}; + + + +/* android.geometric */ + +struct camera2_geometric_ctl { + enum processing_mode mode; +}; + +struct camera2_geometric_dm { + enum processing_mode mode; +}; + + + +/* android.colorCorrection */ + +enum colorcorrection_mode { + COLORCORRECTION_MODE_FAST = 1, + COLORCORRECTION_MODE_HIGH_QUALITY, + COLORCORRECTION_MODE_TRANSFORM_MATRIX, + COLORCORRECTION_MODE_EFFECT_MONO, + COLORCORRECTION_MODE_EFFECT_NEGATIVE, + COLORCORRECTION_MODE_EFFECT_SOLARIZE, + COLORCORRECTION_MODE_EFFECT_SEPIA, + COLORCORRECTION_MODE_EFFECT_POSTERIZE, + COLORCORRECTION_MODE_EFFECT_WHITEBOARD, + COLORCORRECTION_MODE_EFFECT_BLACKBOARD, + COLORCORRECTION_MODE_EFFECT_AQUA, + COLORCORRECTION_MODE_EFFECT_EMBOSS, + COLORCORRECTION_MODE_EFFECT_EMBOSS_MONO, + COLORCORRECTION_MODE_EFFECT_SKETCH, + COLORCORRECTION_MODE_EFFECT_RED_YELLOW_POINT, + COLORCORRECTION_MODE_EFFECT_GREEN_POINT, + COLORCORRECTION_MODE_EFFECT_BLUE_POINT, + COLORCORRECTION_MODE_EFFECT_MAGENTA_POINT, + COLORCORRECTION_MODE_EFFECT_WARM_VINTAGE, + COLORCORRECTION_MODE_EFFECT_COLD_VINTAGE, + COLORCORRECTION_MODE_EFFECT_WASHED, + COLORCORRECTION_MODE_EFFECT_BEAUTY_FACE, + TOTAOCOUNT_COLORCORRECTION_MODE_EFFECT +}; + + +struct camera2_colorcorrection_ctl { + enum colorcorrection_mode mode; + float transform[9]; + uint32_t hue; + uint32_t saturation; + uint32_t brightness; + uint32_t contrast; +}; + +struct camera2_colorcorrection_dm { + enum colorcorrection_mode mode; + float transform[9]; + uint32_t hue; + uint32_t saturation; + uint32_t brightness; + uint32_t contrast; +}; + +struct camera2_colorcorrection_sm { + /*assuming 10 supported modes*/ + uint8_t availableModes[CAMERA2_MAX_AVAILABLE_MODE]; + uint32_t hueRange[2]; + uint32_t saturationRange[2]; + uint32_t brightnessRange[2]; + uint32_t contrastRange[2]; +}; + + +/* android.tonemap */ + +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 curveRed[64]; + float curveGreen[64]; + float curveBlue[64]; +}; + +struct camera2_tonemap_dm { + enum tonemap_mode mode; + /* assuming maxCurvePoints = 64 */ + float curveRed[64]; + float curveGreen[64]; + float curveBlue[64]; +}; + +struct camera2_tonemap_sm { + uint32_t maxCurvePoints; +}; + +/* android.edge */ + +struct camera2_edge_ctl { + enum processing_mode mode; + uint32_t strength; +}; + +struct camera2_edge_dm { + enum processing_mode mode; + uint32_t strength; +}; + + + +/* android.scaler */ + +enum scaler_availableformats { + SCALER_FORMAT_BAYER_RAW, + SCALER_FORMAT_YV12, + SCALER_FORMAT_NV21, + SCALER_FORMAT_JPEG, + SCALER_FORMAT_UNKNOWN +}; + +struct camera2_scaler_ctl { + uint32_t cropRegion[4]; + uint32_t orientation; +}; + +struct camera2_scaler_dm { + uint32_t cropRegion[4]; + uint32_t orientation; +}; + +struct camera2_scaler_sm { + enum scaler_availableformats availableFormats[4]; + /*assuming # of availableFormats = 4*/ + uint32_t availableRawSizes; + uint64_t availableRawMinDurations; + /* needs check */ + uint32_t availableProcessedSizes[8]; + uint64_t availableProcessedMinDurations[8]; + uint32_t availableJpegSizes[8][2]; + uint64_t availableJpegMinDurations[8]; + uint32_t availableMaxDigitalZoom[8]; +}; + +/* android.jpeg */ +struct camera2_jpeg_ctl { + uint32_t quality; + uint32_t thumbnailSize[2]; + uint32_t thumbnailQuality; + double gpsCoordinates[3]; + uint32_t gpsProcessingMethod; + uint64_t gpsTimestamp; + uint32_t orientation; +}; + +struct camera2_jpeg_dm { + uint32_t quality; + uint32_t thumbnailSize[2]; + uint32_t thumbnailQuality; + double gpsCoordinates[3]; + uint32_t gpsProcessingMethod; + uint64_t gpsTimestamp; + uint32_t orientation; +}; + +struct camera2_jpeg_sm { + uint32_t availableThumbnailSizes[8][2]; + uint32_t maxSize; + /*assuming supported size=8*/ +}; + + + +/* android.statistics */ + +enum facedetect_mode { + FACEDETECT_MODE_OFF = 1, + FACEDETECT_MODE_SIMPLE, + FACEDETECT_MODE_FULL +}; + +enum stats_mode { + STATS_MODE_OFF = 1, + STATS_MODE_ON +}; + +enum stats_lowlightmode { + STATE_LLS_LEVEL_ZSL = 0, + STATE_LLS_LEVEL_LOW = 1, + STATE_LLS_LEVEL_HIGH = 2, + STATE_LLS_LEVEL_SIS = 3, + STATE_LLS_LEVEL_ZSL_LIKE = 4, + STATE_LLS_LEVEL_ZSL_FLASH = 16, +}; + +struct camera2_stats_ctl { + enum facedetect_mode faceDetectMode; + enum stats_mode histogramMode; + enum stats_mode sharpnessMapMode; +}; + + +struct camera2_stats_dm { + enum facedetect_mode faceDetectMode; + uint32_t faceRectangles[CAMERA2_MAX_FACES][4]; + uint8_t faceScores[CAMERA2_MAX_FACES]; + uint32_t faceLandmarks[CAMERA2_MAX_FACES][6]; + uint32_t faceIds[CAMERA2_MAX_FACES]; +/* PAYTON_CHECK_20120712 : histogram_mode -> stats_mode */ + enum stats_mode histogramMode; +/* [hj529.kim, 2012/07/19] androd.statistics.histogram */ + uint32_t histogram[3 * 256]; +/* PAYTON_CHECK_20120712 : sharpnessmap_mode -> stats_mode */ + enum stats_mode sharpnessMapMode; + /*sharpnessMap*/ + enum stats_lowlightmode LowLightMode; + uint32_t lls_tuning_set_index; + uint32_t lls_brightness_index; +}; + + +struct camera2_stats_sm { + uint8_t availableFaceDetectModes[CAMERA2_MAX_AVAILABLE_MODE]; + /*assuming supported modes = 3;*/ + uint32_t maxFaceCount; + uint32_t histogramBucketCount; + uint32_t maxHistogramCount; + uint32_t sharpnessMapSize[2]; + uint32_t maxSharpnessMapValue; +}; + +/* android.control */ + +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, + AA_CAPTURE_INTENT_STILL_CAPTURE_OIS_SINGLE, + AA_CAPTURE_INTENT_STILL_CAPTURE_OIS_MULTI +}; + +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, + AA_SCENE_MODE_ANTISHAKE, + AA_SCENE_MODE_HDR, + AA_SCENE_MODE_LLS, + AA_SCENE_MODE_FDAE, + AA_SCENE_MODE_DUAL, + AA_SCENE_MODE_DRAMA, + AA_SCENE_MODE_ANIMATED, + AA_SCENE_MODE_PANORAMA, + AA_SCENE_MODE_GOLF, + AA_SCENE_MODE_PREVIEW, + AA_SCENE_MODE_VIDEO, + AA_SCENE_MODE_SLOWMOTION_2, + AA_SCENE_MODE_SLOWMOTION_4_8, + AA_SCENE_MODE_DUAL_PREVIEW, + AA_SCENE_MODE_DUAL_VIDEO, + AA_SCENE_MODE_120_PREVIEW, + AA_SCENE_MODE_LIGHT_TRACE +}; + +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_CENTER, + AA_AEMODE_AVERAGE, + AA_AEMODE_MATRIX, + AA_AEMODE_SPOT, + AA_AEMODE_CENTER_TOUCH, + AA_AEMODE_AVERAGE_TOUCH, + AA_AEMODE_MATRIX_TOUCH, + AA_AEMODE_SPOT_TOUCH +}; + +enum aa_ae_flashmode { + /*all flash control stop*/ + AA_FLASHMODE_OFF = 1, + /*flash start*/ + AA_FLASHMODE_START, + /*flash cancle*/ + AA_FLASHMODE_CANCLE, + /*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, + AA_AE_ANTIBANDING_AUTO_50HZ, /*50Hz + Auto*/ + AA_AE_ANTIBANDING_AUTO_60HZ /*60Hz + 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 { + /* These modes are adjusted immediatly */ + AA_AFMODE_OFF = 1, + AA_AFMODE_SLEEP, + AA_AFMODE_INFINITY, + AA_AFMODE_MACRO, + AA_AFMODE_DELAYED_OFF, + + /* Single AF. These modes are adjusted when afTrigger is changed from 0 to 1 */ + AA_AFMODE_AUTO = 11, + AA_AFMODE_AUTO_MACRO, + AA_AFMODE_AUTO_VIDEO, + AA_AFMODE_AUTO_FACE, + + /* Continuous AF. These modes are adjusted when afTrigger is changed from 0 to 1 */ + AA_AFMODE_CONTINUOUS_PICTURE = 21, + AA_AFMODE_CONTINUOUS_VIDEO, + AA_AFMODE_CONTINUOUS_PICTURE_FACE, + + /* Special modes for PDAF */ + AA_AFMODE_PDAF_OUTFOCUSING = 31, + AA_AFMODE_PDAF_OUTFOCUSING_FACE, + AA_AFMODE_PDAF_OUTFOCUSING_CONTINUOUS_PICTURE, + AA_AFMODE_PDAF_OUTFOCUSING_CONTINUOUS_PICTURE_FACE, + + /* Not supported yet */ + AA_AFMODE_EDOF = 41, +}; + +/* camera2_aa_ctl.afRegions[4] */ +enum aa_afmode_ext { + AA_AFMODE_EXT_OFF = 1000, + /* Increase macro range for special app */ + AA_AFMODE_EXT_ADVANCED_MACRO_FOCUS = 1001, + /* Set AF region for OCR */ + AA_AFMODE_EXT_FOCUS_LOCATION = 1002, +}; + +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 captureIntent; + enum aa_mode mode; + /*enum aa_effect_mode effectMode;*/ + enum aa_scene_mode sceneMode; + uint32_t videoStabilizationMode; + enum aa_aemode aeMode; + uint32_t aeRegions[5]; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region.*/ + int32_t aeExpCompensation; + uint32_t aeTargetFpsRange[2]; + enum aa_ae_antibanding_mode aeAntibandingMode; + enum aa_ae_flashmode aeflashMode; + enum aa_awbmode awbMode; + uint32_t awbRegions[5]; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region.*/ + enum aa_afmode afMode; + uint32_t afRegions[5]; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region.*/ + uint32_t afTrigger; + enum aa_isomode isoMode; + uint32_t isoValue; + int32_t awbValue; + uint32_t reserved[10]; +}; + +struct camera2_aa_dm { + enum aa_mode mode; + enum aa_effect_mode effectMode; + enum aa_scene_mode sceneMode; + uint32_t videoStabilizationMode; + enum aa_aemode aeMode; + /*needs check*/ + uint32_t aeRegions[5]; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region.*/ + enum ae_state aeState; + enum aa_ae_flashmode aeflashMode; + /*needs check*/ + enum aa_awbmode awbMode; + uint32_t awbRegions[5]; + enum awb_state awbState; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region.*/ + enum aa_afmode afMode; + uint32_t afRegions[5]; + /*5 per region(x1,y1,x2,y2,weight). currently assuming 1 region*/ + enum aa_afstate afState; + enum aa_isomode isoMode; + uint32_t isoValue; + uint32_t reserved[10]; +}; + +struct camera2_aa_sm { + uint8_t availableSceneModes[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t availableEffects[CAMERA2_MAX_AVAILABLE_MODE]; + /*assuming # of available scene modes = 10*/ + uint32_t maxRegions; + uint8_t aeAvailableModes[CAMERA2_MAX_AVAILABLE_MODE]; + /*assuming # of available ae modes = 8*/ + struct rational aeCompensationStep; + int32_t aeCompensationRange[2]; + uint32_t aeAvailableTargetFpsRanges[CAMERA2_MAX_AVAILABLE_MODE][2]; + uint8_t aeAvailableAntibandingModes[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t awbAvailableModes[CAMERA2_MAX_AVAILABLE_MODE]; + /*assuming # of awbAvailableModes = 10*/ + uint8_t afAvailableModes[CAMERA2_MAX_AVAILABLE_MODE]; + /*assuming # of afAvailableModes = 4*/ + uint8_t availableVideoStabilizationModes[4]; + /*assuming # of availableVideoStabilizationModes = 4*/ + uint32_t isoRange[2]; +}; + +struct camera2_lens_usm { + /** Frame delay between sending command and applying frame data */ + uint32_t focusDistanceFrameDelay; +}; + +struct camera2_sensor_usm { + /** Frame delay between sending command and applying frame data */ + uint32_t exposureTimeFrameDelay; + uint32_t frameDurationFrameDelay; + uint32_t sensitivityFrameDelay; +}; + +struct camera2_flash_usm { + /** Frame delay between sending command and applying frame data */ + uint32_t flashModeFrameDelay; + uint32_t firingPowerFrameDelay; + uint64_t firingTimeFrameDelay; +}; + +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_noisereduction_ctl noise; + struct camera2_shading_ctl shading; + struct camera2_geometric_ctl geometric; + struct camera2_colorcorrection_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_entry_ctl entry; +}; + +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_noisereduction_dm noise; + struct camera2_shading_dm shading; + struct camera2_geometric_dm geometric; + struct camera2_colorcorrection_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_entry_dm entry; +}; + +struct camera2_sm { + struct camera2_lens_sm lens; + struct camera2_sensor_sm sensor; + struct camera2_flash_sm flash; + struct camera2_colorcorrection_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 sensorUd; + struct camera2_flash_usm flashUd; +}; + +/** \brief + User-defined control for lens. +*/ +struct camera2_lens_uctl { + struct camera2_lens_ctl ctl; + + /** It depends by posSize */ + uint32_t pos; + /** It depends by af algorithm(AF pos bit. normally 8 or 9 or 10) */ + uint32_t posSize; + /** It depends by af algorithm */ + uint32_t direction; + /** Some actuator support slew rate control. */ + uint32_t slewRate; +}; + +/** \brief + User-defined metadata for lens. +*/ +struct camera2_lens_udm { + /** It depends by posSize */ + uint32_t pos; + /** It depends by af algorithm(AF pos bit. normally 8 or 9 or 10) */ + uint32_t posSize; + /** It depends by af algorithm */ + uint32_t direction; + /** Some actuator support slew rate control. */ + uint32_t slewRate; +}; + +/** \brief + User-defined metadata for ae. +*/ +struct camera2_ae_udm { + /** vendor specific length */ + uint32_t vsLength; + /** vendor specific data array */ + uint32_t vendorSpecific[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** \brief + User-defined metadata for awb. +*/ +struct camera2_awb_udm { + /** vendor specific length */ + uint32_t vsLength; + /** vendor specific data array */ + uint32_t vendorSpecific[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** \brief + User-defined metadata for af. +*/ +struct camera2_af_udm { + /** vendor specific length */ + uint32_t vsLength; + /** vendor specific data array */ + uint32_t vendorSpecific[CAMERA2_MAX_VENDER_LENGTH]; + int32_t lensPositionInfinity; + int32_t lensPositionMacro; + int32_t lensPositionCurrent; +}; + +/** \brief + User-defined metadata for anti-shading. +*/ +struct camera2_as_udm { + /** vendor specific length */ + uint32_t vsLength; + /** vendor specific data array */ + uint32_t vendorSpecific[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** \brief + User-defined metadata for anti-shading. +*/ +struct camera2_ipc_udm { + /** vendor specific length */ + uint32_t vsLength; + /** vendor specific data array */ + uint32_t vendorSpecific[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** \brief + User-defined metadata for aa. +*/ +struct camera2_internal_udm { + /** vendor specific data array */ + uint32_t vendorSpecific1[CAMERA2_MAX_VENDER_LENGTH]; + uint32_t vendorSpecific2[CAMERA2_MAX_VENDER_LENGTH]; + /* + * vendorSpecific2[0] : info + * vendorSpecific2[100] : 0:sirc 1:cml + * vendorSpecific2[101] : cml exposure + * vendorSpecific2[102] : cml iso(gain) + * vendorSpecific2[103] : cml Bv + */ +}; + +/** \brief + 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 dynamicFrameDuration; + uint32_t analogGain; + uint32_t digitalGain; + uint64_t longExposureTime; /* For supporting WDR */ + uint64_t shortExposureTime; + uint32_t longAnalogGain; + uint32_t shortAnalogGain; + uint32_t longDigitalGain; + uint32_t shortDigitalGain; +}; + +struct camera2_scaler_uctl { + /** \brief + target address for next frame. + \remarks + [0] invalid address, stop + [others] valid address + */ + uint32_t sccTargetAddress[4]; + uint32_t scpTargetAddress[4]; + uint32_t disTargetAddress[4]; + uint32_t taapTargetAddress[4]; /* 3AA preview DMA */ + uint32_t taacTargetAddress[4]; /* 3AA capture DMA */ + uint32_t orientation; +}; + +struct camera2_flash_uctl { + struct camera2_flash_ctl ctl; +}; + +struct camera2_bayer_uctl { + struct camera2_scaler_ctl ctl; +}; + +enum companion_drc_mode { + COMPANION_DRC_OFF = 1, + COMPANION_DRC_ON, +}; + +enum companion_wdr_mode { + COMPANION_WDR_OFF = 1, + COMPANION_WDR_ON, +}; + +enum companion_paf_mode { + COMPANION_PAF_OFF = 1, + COMPANION_PAF_ON, +}; + +enum companion_bypass_mode { + COMPANION_FULL_BYPASS_OFF = 1, + COMPANION_FULL_BYPASS_ON, +}; + +enum companion_lsc_mode { + COMPANION_LSC_OFF = 1, + COMPANION_LSC_ON, +}; + +struct camera2_companion_uctl { + enum companion_drc_mode drc_mode; + enum companion_wdr_mode wdr_mode; + enum companion_paf_mode paf_mode; +}; + +struct camera2_bayer_udm { + uint32_t width; + uint32_t height; +}; + +struct camera2_pdaf_single_result { + uint16_t mode; + uint16_t goalPos; + uint16_t reliability; + uint16_t currentPos; +}; + +struct camera2_pdaf_multi_result { + uint16_t mode; + uint16_t goalPos; + uint16_t reliability; +}; + +struct camera2_pdaf_udm { + uint16_t numCol; /* width of PDAF map, 0 means no multi PDAF data */ + uint16_t numRow; /* height of PDAF map, 0 means no multi PDAF data */ + struct camera2_pdaf_multi_result multiResult[CAMERA2_MAX_PDAF_MULTIROI_COLUMN][CAMERA2_MAX_PDAF_MULTIROI_ROW]; + struct camera2_pdaf_single_result singleResult; + uint16_t lensPosResolution; /* 1023(unsigned 10bit) */ +}; + +struct camera2_companion_udm { + enum companion_drc_mode drc_mode; + enum companion_wdr_mode wdr_mode; + enum companion_paf_mode paf_mode; + struct camera2_pdaf_udm pdaf; +}; + +/** \brief + User-defined control area. + \remarks + sensor, lens, flash category is empty value. + It should be filled by a5 for SET_CAM_CONTROL command. + Other category is filled already from host. +*/ +struct camera2_uctl { + /** \brief + Set sensor, lens, flash control for next frame. + \remarks + This flag can be combined. + [0 bit] lens + [1 bit] sensor + [2 bit] flash + */ + uint32_t uUpdateBitMap; + + /** For debugging */ + uint32_t uFrameNumber; + + /** ispfw specific control(user-defined) of lens. */ + struct camera2_lens_uctl lensUd; + /** ispfw specific control(user-defined) of sensor. */ + struct camera2_sensor_uctl sensorUd; + /** ispfw specific control(user-defined) of flash. */ + struct camera2_flash_uctl flashUd; + + struct camera2_scaler_uctl scalerUd; + /** ispfw specific control(user-defined) of Bcrop1. */ + struct camera2_bayer_uctl bayerUd; + struct camera2_companion_uctl companionUd; + uint32_t reserved[10]; +}; + +struct camera2_udm { + struct camera2_lens_udm lens; + struct camera2_ae_udm ae; + struct camera2_awb_udm awb; + struct camera2_af_udm af; + struct camera2_as_udm as; + struct camera2_ipc_udm ipc; + /* KJ_121129 : Add udm for sirc sdk. */ + struct camera2_internal_udm internal; + /* Add udm for bayer down size. */ + struct camera2_bayer_udm bayer; + struct camera2_companion_udm companion; + uint32_t reserved[10]; +}; + +struct camera2_shot { + /*google 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; +}; + +struct camera2_node_input { + /** \brief + intput crop region + \remarks + [0] x axis + [1] y axie + [2] width + [3] height + */ + uint32_t cropRegion[4]; +}; + +struct camera2_node_output { + /** \brief + output crop region + \remarks + [0] x axis + [1] y axie + [2] width + [3] height + */ + uint32_t cropRegion[4]; +}; + +struct camera2_node { + /** \brief + video node id + \remarks + [x] video node id + */ + uint32_t vid; + + /** \brief + stream control + \remarks + [0] disable stream out + [1] enable stream out + */ + uint32_t request; + + struct camera2_node_input input; + struct camera2_node_output output; +}; + +struct camera2_node_group { + /** \brief + output device node + \remarks + this node can pull in image + */ + struct camera2_node leader; + + /** \brief + capture node list + \remarks + this node can get out image + 3AAC, 3AAP, SCC, SCP, VDISC + */ + struct camera2_node capture[CAPTURE_NODE_MAX]; +}; + +/** \brief + Structure for interfacing between HAL and driver. +*/ +struct camera2_shot_ext { + /* + * --------------------------------------------------------------------- + * HAL Control Part + * --------------------------------------------------------------------- + */ + + /** \brief + setfile change + \remarks + [x] mode for setfile + */ + uint32_t setfile; + + /** \brief + node group control + \remarks + per frame control + */ + struct camera2_node_group node_group; + + /** \brief + post processing control(DRC) + \remarks + [0] bypass off + [1] bypass on + */ + uint32_t drc_bypass; + + /** \brief + post processing control(DIS) + \remarks + [0] bypass off + [1] bypass on + */ + uint32_t dis_bypass; + + /** \brief + post processing control(3DNR) + \remarks + [0] bypass off + [1] bypass on + */ + uint32_t dnr_bypass; + + /** \brief + post processing control(FD) + \remarks + [0] bypass off + [1] bypass on + */ + uint32_t fd_bypass; + + /* + * --------------------------------------------------------------------- + * DRV Control Part + * --------------------------------------------------------------------- + */ + + /** \brief + requested frames state. + driver return the information everytime + when dequeue is requested. + \remarks + [X] count + */ + uint32_t free_cnt; + uint32_t request_cnt; + uint32_t process_cnt; + uint32_t complete_cnt; + + /* reserved for future */ + uint32_t reserved[15]; + + /** \brief + processing time debugging + \remarks + taken time(unit : struct timeval) + [0][x] flite start + [1][x] flite end + [2][x] DRV Shot + [3][x] DRV Shot done + [4][x] DRV Meta done + */ + uint32_t timeZone[10][2]; + + /* + * --------------------------------------------------------------------- + * Camera API + * --------------------------------------------------------------------- + */ + + struct camera2_shot shot; +}; + +/** \brief + stream structure for scaler. +*/ +struct camera2_stream { + /** \brief + this address for verifying conincidence of index and address + \remarks + [X] kernel virtual address for this buffer + */ + uint32_t address; + + /** \brief + this frame count is from FLITE through dm.request.fcount, + this count increases every frame end. initial value is 1. + \remarks + [X] frame count + */ + uint32_t fcount; + + /** \brief + this request count is from HAL through ctl.request.fcount, + this count is the unique. + \remarks + [X] request count + */ + uint32_t rcount; + + /** \brief + frame index of isp framemgr. + this value is for driver internal debugging + \remarks + [X] frame index + */ + uint32_t findex; + + /** \brief + frame validation of isp framemgr. + this value is for driver and HAL internal debugging + \remarks + [X] frame valid + */ + uint32_t fvalid; + + /** \brief + output crop region + this value mean the output image places the axis of memory space + \remarks + [0] crop x axis + [1] crop y axis + [2] width + [3] height + */ + uint32_t input_crop_region[4]; + uint32_t output_crop_region[4]; +}; + +#define CAM_LENS_CMD (0x1 << 0x0) +#define CAM_SENSOR_CMD (0x1 << 0x1) +#define CAM_FLASH_CMD (0x1 << 0x2) + +/* typedefs below are for firmware sources */ + +typedef enum metadata_mode metadata_mode_t; +typedef struct camera2_request_ctl camera2_request_ctl_t; +typedef struct camera2_request_dm camera2_request_dm_t; +typedef enum optical_stabilization_mode optical_stabilization_mode_t; +typedef enum lens_facing lens_facing_t; +typedef struct camera2_entry_ctl camera2_entry_ctl_t; +typedef struct camera2_entry_dm camera2_entry_dm_t; +typedef struct camera2_lens_ctl camera2_lens_ctl_t; +typedef struct camera2_lens_dm camera2_lens_dm_t; +typedef struct camera2_lens_sm camera2_lens_sm_t; +typedef enum sensor_colorfilterarrangement sensor_colorfilterarrangement_t; +typedef enum sensor_ref_illuminant sensor_ref_illuminant_t; +typedef struct camera2_sensor_ctl camera2_sensor_ctl_t; +typedef struct camera2_sensor_dm camera2_sensor_dm_t; +typedef struct camera2_sensor_sm camera2_sensor_sm_t; +typedef enum flash_mode flash_mode_t; +typedef struct camera2_flash_ctl camera2_flash_ctl_t; +typedef struct camera2_flash_dm camera2_flash_dm_t; +typedef struct camera2_flash_sm camera2_flash_sm_t; +typedef enum processing_mode processing_mode_t; +typedef struct camera2_hotpixel_ctl camera2_hotpixel_ctl_t; +typedef struct camera2_hotpixel_dm camera2_hotpixel_dm_t; + +typedef struct camera2_demosaic_ctl camera2_demosaic_ctl_t; +typedef struct camera2_demosaic_dm camera2_demosaic_dm_t; +typedef struct camera2_noisereduction_ctl camera2_noisereduction_ctl_t; +typedef struct camera2_noisereduction_dm camera2_noisereduction_dm_t; +typedef struct camera2_shading_ctl camera2_shading_ctl_t; +typedef struct camera2_shading_dm camera2_shading_dm_t; +typedef struct camera2_geometric_ctl camera2_geometric_ctl_t; +typedef struct camera2_geometric_dm camera2_geometric_dm_t; +typedef enum colorcorrection_mode colorcorrection_mode_t; +typedef struct camera2_colorcorrection_ctl camera2_colorcorrection_ctl_t; +typedef struct camera2_colorcorrection_dm camera2_colorcorrection_dm_t; +typedef struct camera2_colorcorrection_sm camera2_colorcorrection_sm_t; +typedef enum tonemap_mode tonemap_mode_t; +typedef struct camera2_tonemap_ctl camera2_tonemap_ctl_t; +typedef struct camera2_tonemap_dm camera2_tonemap_dm_t; +typedef struct camera2_tonemap_sm camera2_tonemap_sm_t; + +typedef struct camera2_edge_ctl camera2_edge_ctl_t; +typedef struct camera2_edge_dm camera2_edge_dm_t; +typedef enum scaler_availableformats scaler_availableformats_t; +typedef struct camera2_scaler_ctl camera2_scaler_ctl_t; +typedef struct camera2_scaler_dm camera2_scaler_dm_t; +typedef struct camera2_jpeg_ctl camera2_jpeg_ctl_t; +typedef struct camera2_jpeg_dm camera2_jpeg_dm_t; +typedef struct camera2_jpeg_sm camera2_jpeg_sm_t; +typedef enum facedetect_mode facedetect_mode_t; +typedef enum stats_mode stats_mode_t; +typedef struct camera2_stats_ctl camera2_stats_ctl_t; +typedef struct camera2_stats_dm camera2_stats_dm_t; +typedef struct camera2_stats_sm camera2_stats_sm_t; +typedef enum aa_capture_intent aa_capture_intent_t; +typedef enum aa_mode aa_mode_t; +typedef enum aa_scene_mode aa_scene_mode_t; +typedef enum aa_effect_mode aa_effect_mode_t; +typedef enum aa_aemode aa_aemode_t; +typedef enum aa_ae_antibanding_mode aa_ae_antibanding_mode_t; +typedef enum aa_awbmode aa_awbmode_t; +typedef enum aa_afmode aa_afmode_t; +typedef enum aa_afstate aa_afstate_t; +typedef struct camera2_aa_ctl camera2_aa_ctl_t; +typedef struct camera2_aa_dm camera2_aa_dm_t; +typedef struct camera2_aa_sm camera2_aa_sm_t; +typedef struct camera2_lens_usm camera2_lens_usm_t; +typedef struct camera2_sensor_usm camera2_sensor_usm_t; +typedef struct camera2_flash_usm camera2_flash_usm_t; +typedef struct camera2_ctl camera2_ctl_t; +typedef struct camera2_uctl camera2_uctl_t; +typedef struct camera2_dm camera2_dm_t; +typedef struct camera2_sm camera2_sm_t; + +typedef struct camera2_scaler_sm camera2_scaler_sm_t; +typedef struct camera2_scaler_uctl camera2_scaler_uctl_t; + +typedef struct camera2_sensor_uctl camera2_sensor_uctl_t; +typedef struct camera2_lens_uctl camera2_lens_uctl_t; +typedef struct camera2_lens_udm camera2_lens_udm_t; + +typedef struct camera2_ae_udm camera2_ae_udm_t; +typedef struct camera2_awb_udm camera2_awb_udm_t; +typedef struct camera2_af_udm camera2_af_udm_t; +typedef struct camera2_as_udm camera2_as_udm_t; +typedef struct camera2_ipc_udm camera2_ipc_udm_t; +typedef struct camera2_internal_udm camera2_internal_udm_t; + +typedef struct camera2_flash_uctl camera2_flash_uctl_t; + +typedef struct camera2_shot camera2_shot_t; + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.c b/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.c new file mode 100644 index 000000000000..015ba00a24fb --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#include "fimc-is-ncp6335b.h" + +/** + * NCP6335B Vout Tables + */ +static const struct ncp6335b_vout vout_tables[] = { + {0, NCP6335B_VOUT_1P000, "1.000"}, /* defualt voltage */ + {1, NCP6335B_VOUT_0P875, "0.875"}, + {2, NCP6335B_VOUT_0P900, "0.900"}, + {3, NCP6335B_VOUT_0P925, "0.925"}, + {4, NCP6335B_VOUT_0P950, "0.950"}, + {5, NCP6335B_VOUT_0P975, "0.975"}, + {6, NCP6335B_VOUT_1P000, "1.000"}, +}; + +/** + * ncp6335b_get_vout_val: get i2c register value to set vout of dcdc regulator. + */ +int ncp6335b_get_vout_val(int sel) +{ + int i, vout = vout_tables[0].val; + + if (sel < 0) + pr_err("%s: error, invalid sel %d\n", __func__, sel); + + for (i = 0; ARRAY_SIZE(vout_tables); i++) { + if (vout_tables[i].sel == sel) { + return vout_tables[i].val; + } + } + + pr_err("%s: warning, default voltage selected. sel %d\n", __func__, sel); + + return vout; +} + +/** + * ncp6335b_get_vout_name: get voltage name of vout as string. + */ +const char *ncp6335b_get_vout_str(int sel) +{ + const char *vout = vout_tables[0].vout; + int i; + + if (sel < 0) + pr_err("%s: error, invalid sel %d\n", __func__, sel); + + for (i = 0; ARRAY_SIZE(vout_tables); i++) { + if (vout_tables[i].sel == sel) { + return vout_tables[i].vout; + } + } + + pr_err("%s: warning, default voltage selected. sel %d\n", __func__, sel); + + return vout; +} + +/** + * ncp6335b_set_voltage: set dcdc vout with i2c register value. + */ +int ncp6335b_set_voltage(struct i2c_client *client, int vout) +{ + int ret = i2c_smbus_write_byte_data(client, 0x14, 0x00); + if (ret < 0) + pr_err("[%s::%d] Write Error [%d]\n", __func__, __LINE__, ret); + + ret = i2c_smbus_write_byte_data(client, 0x10, vout); /* 1.05V -> 1V (0xC0) */ + if (ret < 0) + pr_err("[%s::%d] Write Error [%d]. vout 0x%X\n", __func__, __LINE__, ret, vout); + + ret = i2c_smbus_write_byte_data(client, 0x11, vout); /* 1.05V -> 1V (0xC0) */ + if (ret < 0) + pr_err("[%s::%d] Write Error [%d]. vout 0x%X\n", __func__, __LINE__, ret, vout); + + /*pr_info("%s: vout 0x%X\n", __func__, vout);*/ + + return ret; +} + +int ncp6335b_read_voltage(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, 0x3); + if (ret < 0) + pr_err("[%s::%d]Read Error [%d]\n", __func__, __LINE__, ret); + pr_err("[%s::%d]NCP6335B PID[%x]\n", __func__, __LINE__, ret); + + ret = i2c_smbus_read_byte_data(client, 0x10); + if (ret < 0) + pr_err("[%s::%d]Read Error [%d]\n", __func__, __LINE__, ret); + pr_err("[%s::%d]NCP6335B [0x10 Read :: %x]\n", __func__, __LINE__, ret); + + ret = i2c_smbus_read_byte_data(client, 0x11); + if (ret < 0) + pr_err("[%s::%d]Read Error [%d]\n", __func__, __LINE__, ret); + pr_err("[%s::%d]NCP6335B [0x11 Read :: %x]\n", __func__, __LINE__, ret); + + ret = i2c_smbus_read_byte_data(client, 0x14); + if (ret < 0) + pr_err("[%s::%d]Read Error [%d]\n", __func__, __LINE__, ret); + pr_err("[%s::%d]NCP6335B [0x14 Read :: %x]\n", __func__, __LINE__, ret); + + return ret; +} + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.h b/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.h new file mode 100644 index 000000000000..eeab7d6d556a --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-ncp6335b.h @@ -0,0 +1,36 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is core functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "fimc-is-core.h" + +enum{ + NCP6335B_VOUT_0P875 = 0xAC, + NCP6335B_VOUT_0P900 = 0xB0, + NCP6335B_VOUT_0P925 = 0xB4, + NCP6335B_VOUT_0P950 = 0xB8, + NCP6335B_VOUT_0P975 = 0xBC, + NCP6335B_VOUT_1P000 = 0xC0, +}; + +struct ncp6335b_vout { + int sel; /* selector, unique value for vout entry and indepedant to dcdc vendor */ + int val; /* dcdc-specific value for vout register */ + char vout[7]; /* voltage level string */ +}; + +int ncp6335b_get_vout_val(int sel); +const char *ncp6335b_get_vout_str(int sel); +int ncp6335b_set_voltage(struct i2c_client *client, int vout); +int ncp6335b_read_voltage(struct i2c_client *client); + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-param.h b/drivers/media/platform/exynos/fimc-is/fimc-is-param.h new file mode 100644 index 000000000000..2ccf086b97ab --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-param.h @@ -0,0 +1,1445 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_PARAMS_H +#define FIMC_IS_PARAMS_H + +#define IS_REGION_VER 150 /* IS REGION VERSION 1.50 */ + +/* MACROs */ +#define IS_SET_PARAM_BIT(dev, num) \ + (num >= 32 ? set_bit((num-32), &dev->p_region_index2) \ + : set_bit(num, &dev->p_region_index1)) +#define IS_INC_PARAM_NUM(dev) atomic_inc(&dev->p_region_num) + +#define IS_PARAM_GLOBAL(dev) (dev->is_p_region->parameter.global) +#define IS_PARAM_ISP(dev) (dev->is_p_region->parameter.isp) +#define IS_PARAM_DRC(dev) (dev->is_p_region->parameter.drc) +#define IS_PARAM_FD(dev) (dev->is_p_region->parameter.fd) +#define IS_HEADER(dev) (dev->is_p_region->header) +#define IS_FACE(dev) (dev->is_p_region->face) +#define IS_SHARED(dev) (dev->is_shared_region) +#define IS_PARAM_SIZE (FIMC_IS_REGION_SIZE + 1) + +#ifndef BIT0 +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 +#define BIT32 0x0000000100000000ULL +#define BIT33 0x0000000200000000ULL +#define BIT34 0x0000000400000000ULL +#define BIT35 0x0000000800000000ULL +#define BIT36 0x0000001000000000ULL +#define BIT37 0x0000002000000000ULL +#define BIT38 0x0000004000000000ULL +#define BIT39 0x0000008000000000ULL +#define BIT40 0x0000010000000000ULL +#define BIT41 0x0000020000000000ULL +#define BIT42 0x0000040000000000ULL +#define BIT43 0x0000080000000000ULL +#define BIT44 0x0000100000000000ULL +#define BIT45 0x0000200000000000ULL +#define BIT46 0x0000400000000000ULL +#define BIT47 0x0000800000000000ULL +#define BIT48 0x0001000000000000ULL +#define BIT49 0x0002000000000000ULL +#define BIT50 0x0004000000000000ULL +#define BIT51 0x0008000000000000ULL +#define BIT52 0x0010000000000000ULL +#define BIT53 0x0020000000000000ULL +#define BIT54 0x0040000000000000ULL +#define BIT55 0x0080000000000000ULL +#define BIT56 0x0100000000000000ULL +#define BIT57 0x0200000000000000ULL +#define BIT58 0x0400000000000000ULL +#define BIT59 0x0800000000000000ULL +#define BIT60 0x1000000000000000ULL +#define BIT61 0x2000000000000000ULL +#define BIT62 0x4000000000000000ULL +#define BIT63 0x8000000000000000ULL +#define INC_BIT(bit) (bit<<1) +#define INC_NUM(bit) (bit + 1) +#endif + +#define MAGIC_NUMBER 0x01020304 + +#define PARAMETER_MAX_SIZE 128 /* in byte */ +#define PARAMETER_MAX_MEMBER (PARAMETER_MAX_SIZE/4) + +enum is_entry { + ENTRY_GLOBAL, + ENTRY_BUFFER, + ENTRY_SENSOR, + ENTRY_3AA, + ENTRY_ISP, + ENTRY_DRC, + ENTRY_SCALERC, + ENTRY_DIS, + ENTRY_TDNR, + ENTRY_SCALERP, + ENTRY_LHFD, + ENTRY_3AAC, + ENTRY_3AAP, + ENTRY_END +}; + +#define ADDRESS_TO_OFFSET(start, end) ((uint32)end - (uint32)start) +#define OFFSET_TO_NUM(offset) ((offset)>>6) +#define IS_OFFSET_LOWBIT(offset) (OFFSET_TO_NUM(offset) >= \ + 32 ? false : true) +#define OFFSET_TO_BIT(offset) \ + {(IS_OFFSET_LOWBIT(offset) ? (1<= 32 ? 0 : BIT0<= 32 ? BIT0<<(num-32) : 0) + +/* 0~31 */ +#define PARAM_GLOBAL_SHOTMODE 0 +#define PARAM_SENSOR_CONTROL INC_NUM(PARAM_GLOBAL_SHOTMODE) +#define PARAM_SENSOR_OTF_INPUT INC_NUM(PARAM_SENSOR_CONTROL) +#define PARAM_SENSOR_OTF_OUTPUT INC_NUM(PARAM_SENSOR_OTF_INPUT) +#define PARAM_SENSOR_CONFIG INC_NUM(PARAM_SENSOR_OTF_OUTPUT) +#define PARAM_SENSOR_DMA_OUTPUT INC_NUM(PARAM_SENSOR_CONFIG) +#define PARAM_BUFFER_CONTROL INC_NUM(PARAM_SENSOR_DMA_OUTPUT) +#define PARAM_BUFFER_OTF_INPUT INC_NUM(PARAM_BUFFER_CONTROL) +#define PARAM_BUFFER_OTF_OUTPUT INC_NUM(PARAM_BUFFER_OTF_INPUT) +#define PARAM_3AA_CONTROL INC_NUM(PARAM_BUFFER_OTF_OUTPUT) +#define PARAM_3AA_OTF_INPUT INC_NUM(PARAM_3AA_CONTROL) +#define PARAM_3AA_VDMA1_INPUT INC_NUM(PARAM_3AA_OTF_INPUT) +#define PARAM_3AA_DDMA_INPUT INC_NUM(PARAM_3AA_VDMA1_INPUT) +#define PARAM_3AA_OTF_OUTPUT INC_NUM(PARAM_3AA_DDMA_INPUT) +#define PARAM_3AA_VDMA4_OUTPUT INC_NUM(PARAM_3AA_OTF_OUTPUT) +#define PARAM_3AA_VDMA2_OUTPUT INC_NUM(PARAM_3AA_VDMA4_OUTPUT) +#define PARAM_3AA_DDMA_OUTPUT INC_NUM(PARAM_3AA_VDMA2_OUTPUT) +#define PARAM_ISP_CONTROL INC_NUM(PARAM_3AA_DDMA_OUTPUT) +#define PARAM_ISP_OTF_INPUT INC_NUM(PARAM_ISP_CONTROL) +#define PARAM_ISP_VDMA1_INPUT INC_NUM(PARAM_ISP_OTF_INPUT) +#define PARAM_ISP_VDMA3_INPUT INC_NUM(PARAM_ISP_VDMA1_INPUT) +#define PARAM_ISP_OTF_OUTPUT INC_NUM(PARAM_ISP_VDMA3_INPUT) +#define PARAM_ISP_VDMA4_OUTPUT INC_NUM(PARAM_ISP_OTF_OUTPUT) +#define PARAM_ISP_VDMA5_OUTPUT INC_NUM(PARAM_ISP_VDMA4_OUTPUT) +#define PARAM_DRC_CONTROL INC_NUM(PARAM_ISP_VDMA5_OUTPUT) +#define PARAM_DRC_OTF_INPUT INC_NUM(PARAM_DRC_CONTROL) +#define PARAM_DRC_DMA_INPUT INC_NUM(PARAM_DRC_OTF_INPUT) +#define PARAM_DRC_OTF_OUTPUT INC_NUM(PARAM_DRC_DMA_INPUT) +#define PARAM_SCALERC_CONTROL INC_NUM(PARAM_DRC_OTF_OUTPUT) +#define PARAM_SCALERC_OTF_INPUT INC_NUM(PARAM_SCALERC_CONTROL) +#define PARAM_SCALERC_IMAGE_EFFECT INC_NUM(PARAM_SCALERC_OTF_INPUT) +#define PARAM_SCALERC_INPUT_CROP INC_NUM(PARAM_SCALERC_IMAGE_EFFECT) +/* 32~63 */ +#define PARAM_SCALERC_OUTPUT_CROP INC_NUM(PARAM_SCALERC_INPUT_CROP) +#define PARAM_SCALERC_OTF_OUTPUT INC_NUM(PARAM_SCALERC_OUTPUT_CROP) +#define PARAM_SCALERC_DMA_OUTPUT INC_NUM(PARAM_SCALERC_OTF_OUTPUT) +#define PARAM_ODC_CONTROL INC_NUM(PARAM_SCALERC_DMA_OUTPUT) +#define PARAM_ODC_OTF_INPUT INC_NUM(PARAM_ODC_CONTROL) +#define PARAM_ODC_OTF_OUTPUT INC_NUM(PARAM_ODC_OTF_INPUT) +#define PARAM_DIS_CONTROL INC_NUM(PARAM_ODC_OTF_OUTPUT) +#define PARAM_DIS_OTF_INPUT INC_NUM(PARAM_DIS_CONTROL) +#define PARAM_DIS_OTF_OUTPUT INC_NUM(PARAM_DIS_OTF_INPUT) +#define PARAM_TDNR_CONTROL INC_NUM(PARAM_DIS_OTF_OUTPUT) +#define PARAM_TDNR_OTF_INPUT INC_NUM(PARAM_TDNR_CONTROL) +#define PARAM_TDNR_1ST_FRAME INC_NUM(PARAM_TDNR_OTF_INPUT) +#define PARAM_TDNR_OTF_OUTPUT INC_NUM(PARAM_TDNR_1ST_FRAME) +#define PARAM_TDNR_DMA_OUTPUT INC_NUM(PARAM_TDNR_OTF_OUTPUT) +#define PARAM_SCALERP_CONTROL INC_NUM(PARAM_TDNR_DMA_OUTPUT) +#define PARAM_SCALERP_OTF_INPUT INC_NUM(PARAM_SCALERP_CONTROL) +#define PARAM_SCALERP_IMAGE_EFFECT INC_NUM(PARAM_SCALERP_OTF_INPUT) +#define PARAM_SCALERP_INPUT_CROP INC_NUM(PARAM_SCALERP_IMAGE_EFFECT) +#define PARAM_SCALERP_OUTPUT_CROP INC_NUM(PARAM_SCALERP_INPUT_CROP) +#define PARAM_SCALERP_ROTATION INC_NUM(PARAM_SCALERP_OUTPUT_CROP) +#define PARAM_SCALERP_FLIP INC_NUM(PARAM_SCALERP_ROTATION) +#define PARAM_SCALERP_OTF_OUTPUT INC_NUM(PARAM_SCALERP_FLIP) +#define PARAM_SCALERP_DMA_OUTPUT INC_NUM(PARAM_SCALERP_OTF_OUTPUT) +#define PARAM_FD_CONTROL INC_NUM(PARAM_SCALERP_DMA_OUTPUT) +#define PARAM_FD_OTF_INPUT INC_NUM(PARAM_FD_CONTROL) +#define PARAM_FD_DMA_INPUT INC_NUM(PARAM_FD_OTF_INPUT) +#define PARAM_FD_CONFIG INC_NUM(PARAM_FD_DMA_INPUT) +#define PARAM_END INC_NUM(PARAM_FD_CONFIG) + +#define PARAM_STRNUM_GLOBAL (PARAM_GLOBAL_SHOTMODE) +#define PARAM_RANGE_GLOBAL 1 +#define PARAM_STRNUM_SENSOR (PARAM_SENSOR_CONTROL) +#define PARAM_RANGE_SENSOR 5 +#define PARAM_STRNUM_BUFFER (PARAM_BUFFER_CONTROL) +#define PARAM_RANGE_BUFFER 3 +#define PARAM_STRNUM_3AA (PARAM_3AA_CONTROL) +#define PARAM_RANGE_3AA 8 +#define PARAM_STRNUM_ISP (PARAM_ISP_CONTROL) +#define PARAM_RANGE_ISP 7 +#define PARAM_STRNUM_DRC (PARAM_DRC_CONTROL) +#define PARAM_RANGE_DRC 4 +#define PARAM_STRNUM_SCALERC (PARAM_SCALERC_CONTROL) +#define PARAM_RANGE_SCALERC 7 +#define PARAM_STRNUM_ODC (PARAM_ODC_CONTROL) +#define PARAM_RANGE_ODC 3 +#define PARAM_STRNUM_DIS (PARAM_DIS_CONTROL) +#define PARAM_RANGE_DIS 3 +#define PARAM_STRNUM_TDNR (PARAM_TDNR_CONTROL) +#define PARAM_RANGE_TDNR 5 +#define PARAM_STRNUM_SCALERP (PARAM_SCALERP_CONTROL) +#define PARAM_RANGE_SCALERP 9 +#define PARAM_STRNUM_LHFD (PARAM_FD_CONTROL) +#define PARAM_RANGE_LHFD 4 + +#define PARAM_LOW_MASK (0xFFFFFFFF) +#define PARAM_HIGH_MASK (0x07FFFFFF) + +/* Enumerations +* +*/ + +/* ---------------------- 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_NO = 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_input_path { + OTF_INPUT_SERIAL_PATH = 0, + OTF_INPUT_PARAL_PATH = 1 +}; + +enum otf_intput_error { + OTF_INPUT_ERROR_NO = 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, + DMA_INPUT_FORMAT_RGB = 4, + DMA_INPUT_FORMAT_BAYER_PACKED12 = 5, +}; + +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_plane { + DMA_INPUT_PLANE_3 = 3, + DMA_INPUT_PLANE_2 = 2, + DMA_INPUT_PLANE_1 = 1 +}; + +enum dma_input_order { + /* (for DMA_INPUT_PLANE_3) */ + DMA_INPUT_ORDER_NO = 0, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CbCr = 1, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_INPUT_ORDER_CrCb = 2, + /* (only valid at DMA_INPUT_PLANE_1 & DMA_INPUT_FORMAT_YUV444) */ + DMA_INPUT_ORDER_YCbCr = 3, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YYCbCr = 4, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCbYCr = 5, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_YCrYCb = 6, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CbYCrY = 7, + /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ + DMA_INPUT_ORDER_CrYCbY = 8, + /* (only valid at DMA_INPUT_FORMAT_BAYER) */ + DMA_INPUT_ORDER_GR_BG = 9 +}; + +enum dma_input_MemoryWidthBits { + DMA_INPUT_MEMORY_WIDTH_16BIT = 16, + DMA_INPUT_MEMORY_WIDTH_12BIT = 12, +}; + +enum dma_input_error { + DMA_INPUT_ERROR_NO = 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, + OTF_OUTPUT_FORMAT_YUV444_TRUNCATED = 5, + OTF_OUTPUT_FORMAT_YUV422_TRUNCATED = 6 +}; + +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_NO = 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, + DMA_OUTPUT_FORMAT_BAYER_PACKED12= 5, +}; + +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_plane { + DMA_OUTPUT_PLANE_3 = 3, + DMA_OUTPUT_PLANE_2 = 2, + DMA_OUTPUT_PLANE_1 = 1 +}; + +enum dma_output_order { + DMA_OUTPUT_ORDER_NO = 0, + /* (for DMA_OUTPUT_PLANE_3) */ + DMA_OUTPUT_ORDER_CbCr = 1, + /* (only valid at DMA_INPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_CrCb = 2, + /* (only valid at DMA_OUTPUT_PLANE_2) */ + DMA_OUTPUT_ORDER_YYCbCr = 3, + /* (only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_YCbYCr = 4, + /* (only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_YCrYCb = 5, + /* (only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CbYCrY = 6, + /* (only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CrYCbY = 7, + /* (only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_YCbCr = 8, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CrYCb = 9, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CrCbY = 10, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CbYCr = 11, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_YCrCb = 12, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_CbCrY = 13, + /* (only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1) */ + DMA_OUTPUT_ORDER_BGR = 14, + /* (only valid at DMA_OUTPUT_FORMAT_RGB) */ + DMA_OUTPUT_ORDER_GB_BG = 15 + /* (only valid at DMA_OUTPUT_FORMAT_BAYER) */ +}; + +enum dma_output_notify_dma_done { + DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE = 0, + DMA_OUTPUT_NOTIFY_DMA_DONE_ENBABLE = 1, +}; + +enum dma_output_error { + DMA_OUTPUT_ERROR_NO = 0 /* DMA output setting is done */ +}; + +/* ---------------------- Global ----------------------------------- */ +enum global_shotmode_error { + GLOBAL_SHOTMODE_ERROR_NO = 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_NO = 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, + ISP_FLASH_COMMAND_START = 8, + ISP_FLASH_COMMAND_CANCLE = 9 +}; + +enum isp_flash_redeye { + ISP_FLASH_REDEYE_DISABLE = 0, + ISP_FLASH_REDEYE_ENABLE = 1 +}; + +enum isp_flash_error { + ISP_FLASH_ERROR_NO = 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_NO = 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_NO = 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_NO = 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_NO = 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_NO = 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_NO = 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 +}; + +enum ISP_BDSCommandEnum { + ISP_BDS_COMMAND_DISABLE = 0, + ISP_BDS_COMMAND_ENABLE = 1 +}; + +/* -------------------------- 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_NO = 0 +}; + +enum scaler_crop_command { + SCALER_CROP_COMMAND_DISABLE = 0, + SCALER_CROP_COMMAND_ENABLE = 1 +}; + +enum scaler_crop_error { + SCALER_CROP_ERROR_NO = 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_NO = 0 +}; + +enum scaler_rotation_command { + SCALER_ROTATION_COMMAND_DISABLE = 0, + SCALER_ROTATION_COMMAND_CLOCKWISE90 = 1 +}; + +enum scaler_rotation_error { + SCALER_ROTATION_ERROR_NO = 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_NO = 0 /* flip setting is done */ +}; + +enum scaler_dma_out_sel { + SCALER_DMA_OUT_IMAGE_EFFECT = 0, + SCALER_DMA_OUT_SCALED = 1, + SCALER_DMA_OUT_UNSCALED = 2 +}; + +enum scaler_output_yuv_range { + SCALER_OUTPUT_YUV_RANGE_FULL = 0, + SCALER_OUTPUT_YUV_RANGE_NARROW = 1, +}; + +/* -------------------------- 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_NO = 0 + /*1st frame setting is done*/ +}; + +/* ---------------------------- FD ------------------------------------- */ +enum fd_config_command { + FD_CONFIG_COMMAND_MAXIMUM_NUMBER = 0x1, + FD_CONFIG_COMMAND_ROLL_ANGLE = 0x2, + FD_CONFIG_COMMAND_YAW_ANGLE = 0x4, + FD_CONFIG_COMMAND_SMILE_MODE = 0x8, + FD_CONFIG_COMMAND_BLINK_MODE = 0x10, + FD_CONFIG_COMMAND_EYES_DETECT = 0x20, + FD_CONFIG_COMMAND_MOUTH_DETECT = 0x40, + FD_CONFIG_COMMAND_ORIENTATION = 0x80, + FD_CONFIG_COMMAND_ORIENTATION_VALUE = 0x100 +}; + +enum fd_config_roll_angle { + FD_CONFIG_ROLL_ANGLE_BASIC = 0, + FD_CONFIG_ROLL_ANGLE_PRECISE_BASIC = 1, + FD_CONFIG_ROLL_ANGLE_SIDES = 2, + FD_CONFIG_ROLL_ANGLE_PRECISE_SIDES = 3, + FD_CONFIG_ROLL_ANGLE_FULL = 4, + FD_CONFIG_ROLL_ANGLE_PRECISE_FULL = 5, +}; + +enum fd_config_yaw_angle { + FD_CONFIG_YAW_ANGLE_0 = 0, + FD_CONFIG_YAW_ANGLE_45 = 1, + FD_CONFIG_YAW_ANGLE_90 = 2, + FD_CONFIG_YAW_ANGLE_45_90 = 3, +}; + +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; /* with margin */ + u32 height; /* with margine */ + u32 format; + u32 bitwidth; + u32 order; + u32 sensor_binning_ratio_x; /* ex(x1: 1000, x0.5: 2000) */ + u32 sensor_binning_ratio_y; /* ex(x1: 1000, x0.5: 2000) */ + u32 bns_binning_enable; + u32 bns_binning_ratio_x; /* ex(x1: 1000, x0.5: 2000) */ + u32 bns_binning_ratio_y; /* ex(x1: 1000, x0.5: 2000) */ + u32 bns_margin_left; + u32 bns_margin_top; + u32 bns_output_width; /* with margin */ + u32 bns_output_height; /* with margin */ + u32 bayer_crop_enable; + u32 bayer_crop_offset_x; + u32 bayer_crop_offset_y; + u32 bayer_crop_width; /* without margin */ + u32 bayer_crop_height; /* without margin */ + u32 bds_out_enable; + u32 bds_out_width; /* without margin */ + u32 bds_out_height; /* without margin */ + u32 frametime_min; + u32 frametime_max; + u32 scaler_path_sel; /* parallel or serial for SCC*/ + u32 reserved[PARAMETER_MAX_MEMBER-27]; + u32 err; +}; + +struct param_dma_input { + u32 cmd; + u32 width; /* with margin */ + u32 height; /* with margin */ + u32 format; + u32 bitwidth; + u32 plane; + u32 order; + u32 buffer_number; + u32 buffer_address; + u32 sensor_binning_ratio_x; /* ex(x1: 1000, x0.5: 2000) */ + u32 sensor_binning_ratio_y; /* ex(x1: 1000, x0.5: 2000) */ + u32 dma_crop_enable; /* should be 0 */ + u32 dma_crop_offset_x; /* not supported yet */ + u32 dma_crop_offset_y; /* not supported yet */ + u32 dma_crop_width; /* not supported yet */ + u32 dma_crop_height; /* not supported yet */ + u32 bayer_crop_enable; + u32 bayer_crop_offset_x; + u32 bayer_crop_offset_y; + u32 bayer_crop_width; /* without margine */ + u32 bayer_crop_height; /* without margine */ + u32 bds_out_enable; + u32 bds_out_width; /* without margine */ + u32 bds_out_height; /* without margine */ + u32 user_min_frame_time; + u32 user_max_frame_time; + u32 reserved[PARAMETER_MAX_MEMBER-27]; + 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_config { + u32 framerate; + u32 width; + u32 height; + u32 mode; + u32 binning_ratio_x; + u32 binning_ratio_y; + u32 min_target_fps; + u32 max_target_fps; + u32 scene_mode; + u32 reserved[PARAMETER_MAX_MEMBER-10]; + 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_api_2p0; + /* For android.control.afRegions in Camera 2.0, + Resolution based on YUV output size*/ + u32 af_region_left; + /* For android.control.afRegions in Camera 2.0, + Resolution based on YUV output size*/ + u32 af_region_top; + /* For android.control.afRegions in Camera 2.0, + Resolution based on YUV output size*/ + u32 af_region_right; + /* For android.control.afRegions in Camera 2.0, + Resolution based on YUV output size*/ + u32 af_region_bottom; + u32 reserved[PARAMETER_MAX_MEMBER-17]; + u32 err; +}; + +struct param_isp_flash { + u32 cmd; + u32 redeye; + u32 flashintensity; + 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 hot_pixel_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; + /* ISP_AdjustSceneIndexEnum */ + u32 user_scene_mode; + u32 min_frame_time; + u32 max_frame_time; + 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_api_2p0; + 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 arbitrary_cb; + u32 arbitrary_cr; + u32 yuv_range; + u32 reserved[PARAMETER_MAX_MEMBER-5]; + 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; /* 0 */ +}; + +/* To be added */ +struct sensor_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_otf_output otf_output; + struct param_sensor_config config; + 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 taa_param { + struct param_control control; + struct param_otf_input otf_input; /* otf_input */ + struct param_dma_input vdma1_input; /* dma1_input */ + struct param_dma_input ddma_input; /* not use */ + struct param_otf_output otf_output; /* not use */ + struct param_dma_output vdma4_output; /* Before BDS */ + struct param_dma_output vdma2_output; /* After BDS */ + struct param_dma_output ddma_output; /* not use */ +}; + +struct isp_param { + struct param_control control; + struct param_otf_input otf_input; /* not use */ + struct param_dma_input vdma1_input; /* dma1_input */ + struct param_dma_input vdma3_input; /* not use */ + struct param_otf_output otf_output; /* otf_out */ + struct param_dma_output vdma4_output; /* not use */ + struct param_dma_output vdma5_output; /* not use */ +}; + +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 taa_param taa; + 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; + /* (32*4 + 9)*4 = 548 bytes */ +}; + +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 + +enum apex_aperture_value { + F1_0 = 0, + F1_4 = 1, + F2_0 = 2, + F2_8 = 3, + F4_0 = 4, + F5_6 = 5, + F8_9 = 6, + F11_0 = 7, + F16_0 = 8, + F22_0 = 9, + F32_0 = 10, +}; + +struct exif_attribute { + struct rational_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_fd_rect { + u32 offset_x; + u32 offset_y; + u32 width; + u32 height; +}; + +struct is_face_marker { + u32 frame_number; + struct is_fd_rect face; + struct is_fd_rect left_eye; + struct is_fd_rect right_eye; + struct is_fd_rect mouth; + u32 roll_angle; + u32 yaw_angle; + u32 confidence; + u32 is_tracked; + u32 tracked_face_id; + u32 smile_level; + u32 blink_level; +}; + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) || defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS4415) +struct is_debug_region { + u32 frame_count; + u32 reserved[PARAMETER_MAX_MEMBER-1]; +}; +#endif + +#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]; +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) || defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS4415) + struct is_debug_region debug; +#endif + 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 Inovked, 1:Invoked*/ + u32 pabort_flag; /* 0:Not Inovked, 1:Invoked*/ + u32 dabort_flag; /* 0:Not Inovked, 1:Invoked*/ + u32 pd_Ready_flag; /* 0:Normal, 1:EnterIdle(Ready to power down)*/ + u32 isp_frameErr; /* Frame Error Count.*/ + u32 drc_frame_err; /* Frame Error Count.*/ + u32 scc_frame_err; /* Frame Error Count.*/ + u32 odc_frame_err; /* Frame Error Count.*/ + u32 dis_frame_err; /* Frame Error Count.*/ + u32 tdnr_frame_err; /* Frame Error Count.*/ + u32 scp_frame_err; /* Frame Error Count.*/ + u32 fd_frame_err; /* Frame Error Count.*/ + u32 isp_frame_drop; /* Frame Drop Count.*/ + u32 drc_frame_drop; /* Frame Drop Count.*/ + u32 dis_frame_drop; /* Frame Drop Count.*/ + u32 fd_frame_drop; +}; +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-regs.h b/drivers/media/platform/exynos/fimc-is/fimc-is-regs.h new file mode 100644 index 000000000000..c15485ab2a90 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-regs.h @@ -0,0 +1,437 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_REGS_H +#define FIMC_IS_REGS_H + +#include + +/* MCUCTL register */ +#define MCUCTL 0x0 +/* 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_INTGC9 (1 << 25) +#define INTGR0_INTGC8 (1 << 24) +#define INTGR0_INTGC7 (1 << 23) +#define INTGR0_INTGC6 (1 << 22) +#define INTGR0_INTGC5 (1 << 21) +#define INTGR0_INTGC4 (1 << 20) +#define INTGR0_INTGC3 (1 << 19) +#define INTGR0_INTGC2 (1 << 18) +#define INTGR0_INTGC1 (1 << 17) +#define INTGR0_INTGC0 (1 << 16) +#define INTGR0_INTGD5 (1 << 5) +#define INTGR0_INTGD4 (1 << 4) +#define INTGR0_INTGD3 (1 << 3) +#define INTGR0_INTGD2 (1 << 2) +#define INTGR0_INTGD1 (1 << 1) +#define INTGR0_INTGD0 (1 << 0) +/* Interrupt Clear Register 0 from Host CPU to VIC */ +#define INTCR0 (MCUCTL+0x0c) +#define INTCR0_INTCC9 (1 << 25) +#define INTCR0_INTCC8 (1 << 24) +#define INTCR0_INTCC7 (1 << 23) +#define INTCR0_INTCC6 (1 << 22) +#define INTCR0_INTCC5 (1 << 21) +#define INTCR0_INTCC4 (1 << 20) +#define INTCR0_INTCC3 (1 << 19) +#define INTCR0_INTCC2 (1 << 18) +#define INTCR0_INTCC1 (1 << 17) +#define INTCR0_INTCC0 (1 << 16) +#define INTCR0_INTCD5 (1 << 5) +#define INTCR0_INTCD4 (1 << 4) +#define INTCR0_INTCD3 (1 << 3) +#define INTCR0_INTCD2 (1 << 2) +#define INTCR0_INTCD1 (1 << 1) +#define INTCR0_INTCD0 (1 << 0) +/* Interrupt Mask Register 0 from Host CPU to VIC */ +#define INTMR0 (MCUCTL+0x10) +#define INTMR0_INTMC9 (1 << 25) +#define INTMR0_INTMC8 (1 << 24) +#define INTMR0_INTMC7 (1 << 23) +#define INTMR0_INTMC6 (1 << 22) +#define INTMR0_INTMC5 (1 << 21) +#define INTMR0_INTMC4 (1 << 20) +#define INTMR0_INTMC3 (1 << 19) +#define INTMR0_INTMC2 (1 << 18) +#define INTMR0_INTMC1 (1 << 17) +#define INTMR0_INTMC0 (1 << 16) +#define INTMR0_INTMD5 (1 << 5) +#define INTMR0_INTMD4 (1 << 4) +#define INTMR0_INTMD3 (1 << 3) +#define INTMR0_INTMD2 (1 << 2) +#define INTMR0_INTMD1 (1 << 1) +#define INTMR0_INTMD0 (1 << 0) +/* Interrupt Status Register 0 from Host CPU to VIC */ +#define INTSR0 (MCUCTL+0x14) +#define INTSR0_GET_INTSD0(x) (((x) >> 0) & 0x1) +#define INTSR0_GET_INTSD1(x) (((x) >> 1) & 0x1) +#define INTSR0_GET_INTSD2(x) (((x) >> 2) & 0x1) +#define INTSR0_GET_INTSD3(x) (((x) >> 3) & 0x1) +#define INTSR0_GET_INTSD4(x) (((x) >> 4) & 0x1) +#define INTSR0_GET_INTSC0(x) (((x) >> 16) & 0x1) +#define INTSR0_GET_INTSC1(x) (((x) >> 17) & 0x1) +#define INTSR0_GET_INTSC2(x) (((x) >> 18) & 0x1) +#define INTSR0_GET_INTSC3(x) (((x) >> 19) & 0x1) +#define INTSR0_GET_INTSC4(x) (((x) >> 20) & 0x1) +#define INTSR0_GET_INTSC5(x) (((x) >> 21) & 0x1) +#define INTSR0_GET_INTSC6(x) (((x) >> 22) & 0x1) +#define INTSR0_GET_INTSC7(x) (((x) >> 23) & 0x1) +#define INTSR0_GET_INTSC8(x) (((x) >> 24) & 0x1) +#define INTSR0_GET_INTSC9(x) (((x) >> 25) & 0x1) +/* Interrupt Mask Status Register 0 from Host CPU to VIC */ +#define INTMSR0 (MCUCTL+0x18) +#define INTMSR0_GET_INTMSD0(x) (((x) >> 0) & 0x1) +#define INTMSR0_GET_INTMSD1(x) (((x) >> 1) & 0x1) +#define INTMSR0_GET_INTMSD2(x) (((x) >> 2) & 0x1) +#define INTMSR0_GET_INTMSD3(x) (((x) >> 3) & 0x1) +#define INTMSR0_GET_INTMSD4(x) (((x) >> 4) & 0x1) +#define INTMSR0_GET_INTMSC0(x) (((x) >> 16) & 0x1) +#define INTMSR0_GET_INTMSC1(x) (((x) >> 17) & 0x1) +#define INTMSR0_GET_INTMSC2(x) (((x) >> 18) & 0x1) +#define INTMSR0_GET_INTMSC3(x) (((x) >> 19) & 0x1) +#define INTMSR0_GET_INTMSC4(x) (((x) >> 20) & 0x1) +#define INTMSR0_GET_INTMSC5(x) (((x) >> 21) & 0x1) +#define INTMSR0_GET_INTMSC6(x) (((x) >> 22) & 0x1) +#define INTMSR0_GET_INTMSC7(x) (((x) >> 23) & 0x1) +#define INTMSR0_GET_INTMSC8(x) (((x) >> 24) & 0x1) +#define INTMSR0_GET_INTMSC9(x) (((x) >> 25) & 0x1) +/* Interrupt Generation Register 1 from ISP CPU to Host IC */ +#define INTGR1 (MCUCTL+0x1c) +#define INTGR1_INTGC9 (1 << 9) +#define INTGR1_INTGC8 (1 << 8) +#define INTGR1_INTGC7 (1 << 7) +#define INTGR1_INTGC6 (1 << 6) +#define INTGR1_INTGC5 (1 << 5) +#define INTGR1_INTGC4 (1 << 4) +#define INTGR1_INTGC3 (1 << 3) +#define INTGR1_INTGC2 (1 << 2) +#define INTGR1_INTGC1 (1 << 1) +#define INTGR1_INTGC0 (1 << 0) +/* Interrupt Clear Register 1 from ISP CPU to Host IC */ +#define INTCR1 (MCUCTL+0x20) +#define INTCR1_INTCC9 (1 << 9) +#define INTCR1_INTCC8 (1 << 8) +#define INTCR1_INTCC7 (1 << 7) +#define INTCR1_INTCC6 (1 << 6) +#define INTCR1_INTCC5 (1 << 5) +#define INTCR1_INTCC4 (1 << 4) +#define INTCR1_INTCC3 (1 << 3) +#define INTCR1_INTCC2 (1 << 2) +#define INTCR1_INTCC1 (1 << 1) +#define INTCR1_INTCC0 (1 << 0) +/* Interrupt Mask Register 1 from ISP CPU to Host IC */ +#define INTMR1 (MCUCTL+0x24) +#define INTMR1_INTMC9 (1 << 9) +#define INTMR1_INTMC8 (1 << 8) +#define INTMR1_INTMC7 (1 << 7) +#define INTMR1_INTMC6 (1 << 6) +#define INTMR1_INTMC5 (1 << 5) +#define INTMR1_INTMC4 (1 << 4) +#define INTMR1_INTMC3 (1 << 3) +#define INTMR1_INTMC2 (1 << 2) +#define INTMR1_INTMC1 (1 << 1) +#define INTMR1_INTMC0 (1 << 0) +/* 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_INTCC21 (1 << 21) +#define INTCR2_INTCC20 (1 << 20) +#define INTCR2_INTCC19 (1 << 19) +#define INTCR2_INTCC18 (1 << 18) +#define INTCR2_INTCC17 (1 << 17) +#define INTCR2_INTCC16 (1 << 16) +/* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */ +#define INTMR2 (MCUCTL+0x34) +#define INTMR2_INTMCIS25 (1 << 25) +#define INTMR2_INTMCIS24 (1 << 24) +#define INTMR2_INTMCIS23 (1 << 23) +#define INTMR2_INTMCIS22 (1 << 22) +#define INTMR2_INTMCIS21 (1 << 21) +#define INTMR2_INTMCIS20 (1 << 20) +#define INTMR2_INTMCIS19 (1 << 19) +#define INTMR2_INTMCIS18 (1 << 18) +#define INTMR2_INTMCIS17 (1 << 17) +#define INTMR2_INTMCIS16 (1 << 16) +#define INTMR2_INTMCIS15 (1 << 15) +#define INTMR2_INTMCIS14 (1 << 14) +#define INTMR2_INTMCIS13 (1 << 13) +#define INTMR2_INTMCIS12 (1 << 12) +#define INTMR2_INTMCIS11 (1 << 11) +#define INTMR2_INTMCIS10 (1 << 10) +#define INTMR2_INTMCIS9 (1 << 9) +#define INTMR2_INTMCIS8 (1 << 8) +#define INTMR2_INTMCIS7 (1 << 7) +#define INTMR2_INTMCIS6 (1 << 6) +#define INTMR2_INTMCIS5 (1 << 5) +#define INTMR2_INTMCIS4 (1 << 4) +#define INTMR2_INTMCIS3 (1 << 3) +#define INTMR2_INTMCIS2 (1 << 2) +#define INTMR2_INTMCIS1 (1 << 1) +#define INTMR2_INTMCIS0 (1 << 0) +/* 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_GPOG17(x) ((x) << 17) +#define GPOCTLR_GPOG16(x) ((x) << 16) +#define GPOCTLR_GPOG15(x) ((x) << 15) +#define GPOCTLR_GPOG14(x) ((x) << 14) +#define GPOCTLR_GPOG13(x) ((x) << 13) +#define GPOCTLR_GPOG12(x) ((x) << 12) +#define GPOCTLR_GPOG11(x) ((x) << 11) +#define GPOCTLR_GPOG10(x) ((x) << 10) +#define GPOCTLR_GPOG9(x) ((x) << 9) +#define GPOCTLR_GPOG8(x) ((x) << 8) +#define GPOCTLR_GPOG7(x) ((x) << 7) +#define GPOCTLR_GPOG6(x) ((x) << 6) +#define GPOCTLR_GPOG5(x) ((x) << 5) +#define GPOCTLR_GPOG4(x) ((x) << 4) +#define GPOCTLR_GPOG3(x) ((x) << 3) +#define GPOCTLR_GPOG2(x) ((x) << 2) +#define GPOCTLR_GPOG1(x) ((x) << 1) +#define GPOCTLR_GPOG0(x) ((x) << 0) +/* General Purpose Pad Output Enable Register (0~17) */ +#define GPOENCTLR (MCUCTL+0x44) +#define GPOENCTLR_GPOEN17(x) ((x) << 17) +#define GPOENCTLR_GPOEN16(x) ((x) << 16) +#define GPOENCTLR_GPOEN15(x) ((x) << 15) +#define GPOENCTLR_GPOEN14(x) ((x) << 14) +#define GPOENCTLR_GPOEN13(x) ((x) << 13) +#define GPOENCTLR_GPOEN12(x) ((x) << 12) +#define GPOENCTLR_GPOEN11(x) ((x) << 11) +#define GPOENCTLR_GPOEN10(x) ((x) << 10) +#define GPOENCTLR_GPOEN9(x) ((x) << 9) +#define GPOENCTLR_GPOEN8(x) ((x) << 8) +#define GPOENCTLR_GPOEN7(x) ((x) << 7) +#define GPOENCTLR_GPOEN6(x) ((x) << 6) +#define GPOENCTLR_GPOEN5(x) ((x) << 5) +#define GPOENCTLR_GPOEN4(x) ((x) << 4) +#define GPOENCTLR_GPOEN3(x) ((x) << 3) +#define GPOENCTLR_GPOEN2(x) ((x) << 2) +#define GPOENCTLR_GPOEN1(x) ((x) << 1) +#define GPOENCTLR_GPOEN0(x) ((x) << 0) +/* General Purpose Input Control Register (0~17) */ +#define GPICTLR (MCUCTL+0x48) +/* IS Shared Register 0 between ISP CPU and HOST CPU */ +#define ISSR0 (MCUCTL+0x80) +/* Command Host -> IS */ +/* IS Shared Register 1 between ISP CPU and HOST CPU */ +/* Sensor ID for Command */ +#define ISSR1 (MCUCTL+0x84) +/* IS Shared Register 2 between ISP CPU and HOST CPU */ +/* Parameter 1 */ +#define ISSR2 (MCUCTL+0x88) +/* IS Shared Register 3 between ISP CPU and HOST CPU */ +/* Parameter 2 */ +#define ISSR3 (MCUCTL+0x8c) +/* IS Shared Register 4 between ISP CPU and HOST CPU */ +/* Parameter 3 */ +#define ISSR4 (MCUCTL+0x90) +/* IS Shared Register 5 between ISP CPU and HOST CPU */ +/* Parameter 4 */ +#define ISSR5 (MCUCTL+0x94) +#define ISSR6 (MCUCTL+0x98) +#define ISSR7 (MCUCTL+0x9c) +#define ISSR8 (MCUCTL+0xa0) +#define ISSR9 (MCUCTL+0xa4) +/* IS Shared Register 10 between ISP CPU and HOST CPU */ +/* Command IS -> Host */ +#define ISSR10 (MCUCTL+0xa8) +/* IS Shared Register 11 between ISP CPU and HOST CPU */ +/* Sensor ID for Command */ +#define ISSR11 (MCUCTL+0xac) +/* IS Shared Register 12 between ISP CPU and HOST CPU */ +/* Parameter 1 */ +#define ISSR12 (MCUCTL+0xb0) +/* IS Shared Register 13 between ISP CPU and HOST CPU */ +/* Parameter 2 */ +#define ISSR13 (MCUCTL+0xb4) +/* IS Shared Register 14 between ISP CPU and HOST CPU */ +/* Parameter 3 */ +#define ISSR14 (MCUCTL+0xb8) +/* IS Shared Register 15 between ISP CPU and HOST CPU */ +/* Parameter 4 */ +#define ISSR15 (MCUCTL+0xbc) +#define ISSR16 (MCUCTL+0xc0) +#define ISSR17 (MCUCTL+0xc4) +#define ISSR18 (MCUCTL+0xc8) +#define ISSR19 (MCUCTL+0xcc) +/* IS Shared Register 20 between ISP CPU and HOST CPU */ +/* ISP_FRAME_DONE : SENSOR ID */ +#define ISSR20 (MCUCTL+0xd0) +/* IS Shared Register 21 between ISP CPU and HOST CPU */ +/* ISP_FRAME_DONE : PARAMETER 1 */ +#define ISSR21 (MCUCTL+0xd4) +#define ISSR22 (MCUCTL+0xd8) +#define ISSR23 (MCUCTL+0xdc) +/* IS Shared Register 24 between ISP CPU and HOST CPU */ +/* SCALERC_FRAME_DONE : SENSOR ID */ +#define ISSR24 (MCUCTL+0xe0) +/* IS Shared Register 25 between ISP CPU and HOST CPU */ +/* SCALERC_FRAME_DONE : PARAMETER 1 */ +#define ISSR25 (MCUCTL+0xe4) +#define ISSR26 (MCUCTL+0xe8) +#define ISSR27 (MCUCTL+0xec) +/* IS Shared Register 28 between ISP CPU and HOST CPU */ +/* 3DNR_FRAME_DONE : SENSOR ID */ +#define ISSR28 (MCUCTL+0xf0) +/* IS Shared Register 29 between ISP CPU and HOST CPU */ +/* 3DNR_FRAME_DONE : PARAMETER 1 */ +#define ISSR29 (MCUCTL+0xf4) +#define ISSR30 (MCUCTL+0xf8) +#define ISSR31 (MCUCTL+0xfc) +/* IS Shared Register 32 between ISP CPU and HOST CPU */ +/* SCALERP_FRAME_DONE : SENSOR ID */ +#define ISSR32 (MCUCTL+0x100) +/* IS Shared Register 33 between ISP CPU and HOST CPU */ +/* SCALERP_FRAME_DONE : PARAMETER 1 */ +#define ISSR33 (MCUCTL+0x104) +#define ISSR34 (MCUCTL+0x108) +#define ISSR35 (MCUCTL+0x10c) +#define ISSR36 (MCUCTL+0x110) +#define ISSR37 (MCUCTL+0x114) +#define ISSR38 (MCUCTL+0x118) +#define ISSR39 (MCUCTL+0x11c) +#define ISSR40 (MCUCTL+0x120) +#define ISSR41 (MCUCTL+0x124) +#define ISSR42 (MCUCTL+0x128) +#define ISSR43 (MCUCTL+0x12c) +#define ISSR44 (MCUCTL+0x130) +#define ISSR45 (MCUCTL+0x134) +#define ISSR46 (MCUCTL+0x138) +#define ISSR47 (MCUCTL+0x13c) +#define ISSR48 (MCUCTL+0x140) +#define ISSR49 (MCUCTL+0x144) +#define ISSR50 (MCUCTL+0x148) +#define ISSR51 (MCUCTL+0x14c) +#define ISSR52 (MCUCTL+0x150) +#define ISSR53 (MCUCTL+0x154) +#define ISSR54 (MCUCTL+0x158) +#define ISSR55 (MCUCTL+0x15c) +#define ISSR56 (MCUCTL+0x160) +#define ISSR57 (MCUCTL+0x164) +#define ISSR58 (MCUCTL+0x168) +#define ISSR59 (MCUCTL+0x16c) +#define ISSR60 (MCUCTL+0x170) +#define ISSR61 (MCUCTL+0x174) +#define ISSR62 (MCUCTL+0x178) +#define ISSR63 (MCUCTL+0x17c) + +/* PMU for FIMC-IS*/ +#if defined(CONFIG_SOC_EXYNOS4415) +#define PMUREG_CMU_RESET_ISP0_SYS_PWR (S5P_VA_PMU + 0x1594) +#define PMUREG_ISP0_CONFIGURATION (S5P_VA_PMU + 0x40A0) +#define PMUREG_ISP0_STATUS (S5P_VA_PMU + 0x40A4) +#define PMUREG_ISP0_OPTION (S5P_VA_PMU + 0x40A8) + +#define PMUREG_CMU_RESET_ISP1_SYS_PWR (S5P_VA_PMU + 0x159C) +#define PMUREG_ISP1_CONFIGURATION (S5P_VA_PMU + 0x40E0) +#define PMUREG_ISP1_STATUS (S5P_VA_PMU + 0x40E4) +#define PMUREG_ISP1_OPTION (S5P_VA_PMU + 0x40E8) + +#define PMUREG_CENTRAL_SEQ_OPTION (S5P_VA_PMU + 0x0208) +#define PMUREG_ISP_ARM_SYS_PWR_REG (S5P_VA_PMU + 0x1050) +#define PMUREG_ISP_ARM_CONFIGURATION (S5P_VA_PMU + 0x2280) +#define PMUREG_ISP_ARM_STATUS (S5P_VA_PMU + 0x2284) +#define PMUREG_ISP_ARM_OPTION (S5P_VA_PMU + 0x2288) + +#define PMUREG_ISP_LOW_POWER_OFF (S5P_VA_PMU + 0x0004) + +#define PMUREG_ISP0_STATUS (S5P_VA_PMU + 0x40A4) +#define PMUREG_ISP1_STATUS (S5P_VA_PMU + 0x40E4) + +#define MIPICSI0_REG_BASE (S5P_VA_MIPICSI0) /* phy : 0x120C_0000 */ +#define MIPICSI1_REG_BASE (S5P_VA_MIPICSI1) /* phy : 0x120D_0000 */ +#define MIPICSI2_REG_BASE 0 + +#define FIMCLITE0_REG_BASE (S5P_VA_FIMCLITE0) /* phy : 0x120A0000 */ +#define FIMCLITE1_REG_BASE (S5P_VA_FIMCLITE1) /* phy : 0x120B0000 */ +#define FIMCLITE2_REG_BASE (S5P_VA_FIMCLITE2) /* phy : 0x122A0000 */ + +#define PA_FIMC_IS_GIC_C (0x121E0000) +#define PA_FIMC_IS_GIC_D (0x121F0000) +#else +#define MIPICSI0_REG_BASE (S5P_VA_MIPICSI0) +#define MIPICSI1_REG_BASE (S5P_VA_MIPICSI1) +#define MIPICSI2_REG_BASE (S5P_VA_MIPICSI2) + +#define FIMCLITE0_REG_BASE (S5P_VA_FIMCLITE0) +#define FIMCLITE1_REG_BASE (S5P_VA_FIMCLITE1) +#define FIMCLITE2_REG_BASE (S5P_VA_FIMCLITE2) + +#if defined(CONFIG_SOC_EXYNOS3470) +#define PMUREG_ISP_ARM_CONFIGURATION (S5P_VA_PMU + 0x2280) +#define PMUREG_ISP_ARM_STATUS (S5P_VA_PMU + 0x2284) +#define PMUREG_ISP_ARM_OPTION (S5P_VA_PMU + 0x2288) +#define PMUREG_ISP_LOW_POWER_OFF (S5P_VA_PMU + 0x0004) +#define PMUREG_ISP_CONFIGURATION (S5P_VA_PMU + 0x3CA0) +#define PMUREG_ISP_STATUS (S5P_VA_PMU + 0x3CA4) +#define PMUREG_ISP_ARM_SYS_PWR_REG (S5P_VA_PMU + 0x1050) +#elif defined(CONFIG_SOC_EXYNOS5422) +#define PMUREG_ISP_ARM_CONFIGURATION (S5P_VA_PMU + 0x2480) +#define PMUREG_ISP_ARM_STATUS (S5P_VA_PMU + 0x2484) +#define PMUREG_ISP_ARM_OPTION (S5P_VA_PMU + 0x2488) +#define PMUREG_ISP_LOW_POWER_OFF (S5P_VA_PMU + 0x0004) +#define PMUREG_ISP_CONFIGURATION (S5P_VA_PMU + 0x4020) +#define PMUREG_ISP_STATUS (S5P_VA_PMU + 0x4024) +#define PMUREG_CMU_RESET_ISP_SYS_PWR_REG (S5P_VA_PMU + 0x1584) +#define PMUREG_CMU_SYSCLK_ISP_SYS_PWR_REG (S5P_VA_PMU + 0x14C4) +#define PMUREG_ISP_ARM_SYS_PWR_REG (S5P_VA_PMU + 0x1090) +#define PMUREG_CAM_CONFIGURATION (S5P_VA_PMU + 0x5100) +#define PMUREG_CAM_STATUS (S5P_VA_PMU + 0x5104) +#else +#define PMUREG_ISP_ARM_CONFIGURATION (S5P_VA_PMU + 0x2580) +#define PMUREG_ISP_ARM_STATUS (S5P_VA_PMU + 0x2584) +#define PMUREG_ISP_ARM_OPTION (S5P_VA_PMU + 0x2588) +#define PMUREG_ISP_LOW_POWER_OFF (S5P_VA_PMU + 0x0004) +#define PMUREG_ISP_CONFIGURATION (S5P_VA_PMU + 0x4140) +#define PMUREG_ISP_STATUS (S5P_VA_PMU + 0x4144) +#endif + +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#define PMUREG_CAM0_STATUS (S5P_VA_PMU + 0x4024) +#define PMUREG_CAM1_STATUS (S5P_VA_PMU + 0x40A4) +#endif + +#define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224) +#define SYSREG_ISPBLK_CFG (S3C_VA_SYS + 0x022C) + +/* GIC for FIMC-IS*/ +#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +#define PA_FIMC_IS_GIC_C (0x141E0000) +#define PA_FIMC_IS_GIC_D (0x141F0000) +#elif defined(CONFIG_SOC_EXYNOS3470) +#define PA_FIMC_IS_GIC_C (0x121E0000) +#define PA_FIMC_IS_GIC_D (0x121F0000) +#else +#define PA_FIMC_IS_GIC_C (0x131E0000) +#define PA_FIMC_IS_GIC_D (0x131F0000) +#endif +#endif + +/* PWM for FIMC-IS*/ +#define FIMC_IS_PWM_TCNTB0 (0xC) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.c b/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.c new file mode 100644 index 000000000000..ad2518f669e1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-resourcemgr.h" +#include "fimc-is-core.h" +#include "fimc-is-dvfs.h" +#include "fimc-is-clk-gate.h" +#include "fimc-is-hw.h" + +struct pm_qos_request exynos_isp_qos_cpu_min; +struct pm_qos_request exynos_isp_qos_cpu_max; +struct pm_qos_request exynos_isp_qos_int; +struct pm_qos_request exynos_isp_qos_mem; +struct pm_qos_request exynos_isp_qos_cam; +struct pm_qos_request exynos_isp_qos_disp; +#if defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433) +struct pm_qos_request max_cpu_qos; +#endif + +extern struct fimc_is_sysfs_debug sysfs_debug; + +int fimc_is_resource_probe(struct fimc_is_resourcemgr *resourcemgr, + void *private_data) +{ + int ret = 0; + + BUG_ON(!resourcemgr); + BUG_ON(!private_data); + + resourcemgr->private_data = private_data; + + atomic_set(&resourcemgr->rsccount, 0); + atomic_set(&resourcemgr->resource_sensor0.rsccount, 0); + atomic_set(&resourcemgr->resource_sensor1.rsccount, 0); + atomic_set(&resourcemgr->resource_ischain.rsccount, 0); + +#ifdef ENABLE_DVFS + /* dvfs controller init */ + ret = fimc_is_dvfs_init(resourcemgr); + if (ret) + err("%s: fimc_is_dvfs_init failed!\n", __func__); +#endif + + info("%s\n", __func__); + return ret; +} + +int fimc_is_resource_get(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type) +{ + int ret = 0; + u32 rsccount; + struct fimc_is_resource *resource; + struct fimc_is_core *core; + + BUG_ON(!resourcemgr); + BUG_ON(!resourcemgr->private_data); + BUG_ON(rsc_type >= RESOURCE_TYPE_MAX); + + resource = GET_RESOURCE(resourcemgr, rsc_type); + core = (struct fimc_is_core *)resourcemgr->private_data; + rsccount = atomic_read(&core->rsccount); + + if (rsccount >= 5) { + err("[RSC] Invalid rsccount(%d)", rsccount); + ret = -EMFILE; + goto p_err; + } + + if (rsccount == 0) { +#ifdef ENABLE_DVFS + /* dvfs controller init */ + ret = fimc_is_dvfs_init(resourcemgr); + if (ret) { + err("%s: fimc_is_dvfs_init failed!\n", __func__); + goto p_err; + } +#endif + } + + if (atomic_read(&resource->rsccount) == 0) { + switch (rsc_type) { + case RESOURCE_TYPE_SENSOR0: + break; + case RESOURCE_TYPE_SENSOR1: + break; + case RESOURCE_TYPE_ISCHAIN: + core->debug_cnt = 0; + + ret = fimc_is_interface_open(&core->interface); + if (ret) { + err("fimc_is_interface_open is fail(%d)", ret); + goto p_err; + } + + ret = fimc_is_ischain_power(&core->ischain[0], 1); + if (ret) { + err("fimc_is_ischain_power is fail (%d)", ret); + goto p_err; + } + + /* W/A for a lower version MCUCTL */ + fimc_is_interface_reset(&core->interface); + +#ifdef ENABLE_CLOCK_GATE + if (sysfs_debug.en_clk_gate && + sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) + fimc_is_clk_gate_init(core); +#endif + break; + default: + err("[RSC] resource type(%d) is invalid", rsc_type); + BUG(); + break; + } + } + + atomic_inc(&resource->rsccount); + atomic_inc(&core->rsccount); + +p_err: + info("[RSC] rsctype : %d, rsccount : %d\n", rsc_type, rsccount); + return ret; +} + +int fimc_is_resource_put(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type) +{ + int ret = 0; + u32 rsccount; + struct fimc_is_resource *resource; + struct fimc_is_core *core; + + BUG_ON(!resourcemgr); + BUG_ON(!resourcemgr->private_data); + BUG_ON(rsc_type >= RESOURCE_TYPE_MAX); + + resource = GET_RESOURCE(resourcemgr, rsc_type); + core = (struct fimc_is_core *)resourcemgr->private_data; + rsccount = atomic_read(&core->rsccount); + + if (rsccount == 0) { + err("[RSC] Invalid rsccount(%d)\n", rsccount); + ret = -EMFILE; + goto p_err; + } + + if (atomic_read(&resource->rsccount) == 1) { + switch (rsc_type) { + case RESOURCE_TYPE_SENSOR0: + break; + case RESOURCE_TYPE_SENSOR1: + break; + case RESOURCE_TYPE_ISCHAIN: + ret = fimc_is_itf_power_down(&core->interface); + if (ret) + err("power down cmd is fail(%d)", ret); + + ret = fimc_is_ischain_power(&core->ischain[0], 0); + if (ret) + err("fimc_is_ischain_power is fail(%d)", ret); + + ret = fimc_is_interface_close(&core->interface); + if (ret) + err("fimc_is_interface_close is fail(%d)", ret); +#ifndef RESERVED_MEM + /* 5. Dealloc memroy */ + ret = fimc_is_ishcain_deinitmem(&core->ischain[0]); + if (ret) + err("fimc_is_ishcain_deinitmem is fail(%d)", ret); +#endif + break; + + default: + err("[RSC] resource type(%d) is invalid", rsc_type); + BUG(); + break; + } + } + + /* global update */ + if (atomic_read(&core->rsccount) == 1) { + ret = fimc_is_runtime_suspend_post(NULL); + if (ret) + err("fimc_is_runtime_suspend_post is fail(%d)", ret); + } + + atomic_dec(&resource->rsccount); + atomic_dec(&core->rsccount); + +p_err: + info("[RSC] rsctype : %d, rsccount : %d\n", rsc_type, rsccount); + return ret; +} + +int fimc_is_logsync(struct fimc_is_interface *itf, u32 sync_id, u32 msg_test_id) +{ + int ret = 0; + + /* print kernel sync log */ + log_sync(sync_id); + +#ifdef ENABLE_FW_SYNC_LOG + ret = fimc_is_hw_msg_test(itf, sync_id, msg_test_id); + if (ret) + err("fimc_is_hw_msg_test(%d)", ret); +#endif + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.h b/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.h new file mode 100644 index 000000000000..51c9e98f8cbd --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-resourcemgr.h @@ -0,0 +1,84 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_RESOURCE_MGR_H +#define FIMC_IS_RESOURCE_MGR_H + +#include "fimc-is-groupmgr.h" +#include "fimc-is-interface.h" + +#define RESOURCE_TYPE_SENSOR0 0 +#define RESOURCE_TYPE_SENSOR1 1 +#define RESOURCE_TYPE_ISCHAIN 2 +#define RESOURCE_TYPE_MAX 3 + +struct fimc_is_dvfs_ctrl { + struct mutex lock; + int cur_cpu_min_qos; + int cur_cpu_max_qos; + int cur_int_qos; + int cur_mif_qos; + int cur_cam_qos; + int cur_i2c_qos; + int cur_disp_qos; + + struct fimc_is_dvfs_scenario_ctrl *static_ctrl; + struct fimc_is_dvfs_scenario_ctrl *dynamic_ctrl; +}; + +struct fimc_is_clk_gate_ctrl { + spinlock_t lock; + unsigned long msk_state; + int msk_cnt[GROUP_ID_MAX]; + u32 msk_lock_by_ischain[FIMC_IS_MAX_NODES]; + struct exynos_fimc_is_clk_gate_info *gate_info; + u32 msk_clk_on_off_state; /* on/off(1/0) state per ip */ + /* + * For check that there's too long clock-on period. + * This var will increase when clock on, + * And will decrease when clock off. + */ + unsigned long chk_on_off_cnt[GROUP_ID_MAX]; +}; + +struct fimc_is_resource { + struct platform_device *pdev; + void __iomem *regs; + atomic_t rsccount; + u32 private_data; +}; + +struct fimc_is_resourcemgr { + atomic_t rsccount; + atomic_t rsccount_module; /* sensor module */ + struct fimc_is_resource resource_sensor0; + struct fimc_is_resource resource_sensor1; + struct fimc_is_resource resource_ischain; + + struct fimc_is_dvfs_ctrl dvfs_ctrl; + struct fimc_is_clk_gate_ctrl clk_gate_ctrl; + + void *private_data; +}; + +int fimc_is_resource_probe(struct fimc_is_resourcemgr *resourcemgr, + void *private_data); +int fimc_is_resource_get(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type); +int fimc_is_resource_put(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type); +int fimc_is_logsync(struct fimc_is_interface *itf, u32 sync_id, u32 msg_test_id); + + +#define GET_RESOURCE(resourcemgr, type) \ + ((type == RESOURCE_TYPE_SENSOR0) ? &resourcemgr->resource_sensor0 : \ + ((type == RESOURCE_TYPE_SENSOR1) ? &resourcemgr->resource_sensor1 : \ + &resourcemgr->resource_ischain)) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.c b/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.c new file mode 100644 index 000000000000..4e52cf0889d8 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.c @@ -0,0 +1,2626 @@ +#include "fimc-is-sec-define.h" +#include + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +#include +#endif + +bool crc32_fw_check = true; +bool crc32_check = true; +bool crc32_check_factory = true; +bool crc32_header_check = true; +bool crc32_check_front = true; +bool crc32_header_check_front = true; +bool crc32_check_factory_front = true; +bool fw_version_crc_check = true; +bool is_latest_cam_module = false; +bool is_final_cam_module = false; +#if defined(CONFIG_SOC_EXYNOS5433) +bool is_right_prj_name = true; +#endif +#ifdef CONFIG_COMPANION_USE +bool crc32_c1_fw_check = true; +bool crc32_c1_check = true; +bool crc32_c1_check_factory = true; +bool companion_lsc_isvalid = false; +bool companion_coef_isvalid = false; +#endif +u8 cal_map_version[4] = {0,}; +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +#define FIMC_IS_MAX_CAL_SIZE (8 * 1024) +#else +#define FIMC_IS_MAX_CAL_SIZE (64 * 1024) +#endif +#define FIMC_IS_MAX_CAL_SIZE_FRONT (8 * 1024) + +#define FIMC_IS_DEFAULT_CAL_SIZE (20 * 1024) +#define FIMC_IS_DUMP_CAL_SIZE (172 * 1024) +#define FIMC_IS_LATEST_FROM_VERSION_B 'B' +#define FIMC_IS_LATEST_FROM_VERSION_C 'C' +#define FIMC_IS_LATEST_FROM_VERSION_D 'D' +#define FIMC_IS_LATEST_FROM_VERSION_M 'M' + + +//static bool is_caldata_read = false; +//static bool is_c1_caldata_read = false; +static bool force_caldata_dump = false; +static int cam_id = CAMERA_SINGLE_REAR; +bool is_dumped_fw_loading_needed = false; +bool is_dumped_c1_fw_loading_needed = false; +char fw_core_version; +//struct class *camera_class; +//struct device *camera_front_dev; /*sys/class/camera/front*/ +//struct device *camera_rear_dev; /*sys/class/camera/rear*/ +static struct fimc_is_from_info sysfs_finfo; +static struct fimc_is_from_info sysfs_pinfo; +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +static struct fimc_is_from_info sysfs_finfo_front; +static struct fimc_is_from_info sysfs_pinfo_front; +static char cal_buf_front[FIMC_IS_MAX_CAL_SIZE_FRONT]; +#endif + +static char cal_buf[FIMC_IS_MAX_CAL_SIZE]; +char loaded_fw[12] = {0, }; +char loaded_companion_fw[30] = {0, }; + +int fimc_is_sec_set_force_caldata_dump(bool fcd) +{ + force_caldata_dump = fcd; + if (fcd) + pr_info("forced caldata dump enabled!!\n"); + return 0; +} + +int fimc_is_sec_get_sysfs_finfo(struct fimc_is_from_info **finfo) +{ + *finfo = &sysfs_finfo; + return 0; +} + +int fimc_is_sec_get_sysfs_pinfo(struct fimc_is_from_info **pinfo) +{ + *pinfo = &sysfs_pinfo; + return 0; +} + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +int fimc_is_sec_get_sysfs_finfo_front(struct fimc_is_from_info **finfo) +{ + *finfo = &sysfs_finfo_front; + return 0; +} + +int fimc_is_sec_get_sysfs_pinfo_front(struct fimc_is_from_info **pinfo) +{ + *pinfo = &sysfs_pinfo_front; + return 0; +} + +int fimc_is_sec_get_front_cal_buf(char **buf) +{ + *buf = &cal_buf_front[0]; + return 0; +} +#endif + +int fimc_is_sec_get_cal_buf(char **buf) +{ + *buf = &cal_buf[0]; + return 0; +} + +int fimc_is_sec_get_loaded_fw(char **buf) +{ + *buf = &loaded_fw[0]; + return 0; +} + +int fimc_is_sec_get_loaded_c1_fw(char **buf) +{ + *buf = &loaded_companion_fw[0]; + return 0; +} + +int fimc_is_sec_set_camid(int id) +{ + cam_id = id; + return 0; +} + +int fimc_is_sec_get_camid(void) +{ + return cam_id; +} + +int fimc_is_sec_get_camid_from_hal(char *fw_name, char *setf_name) +{ +#if 0 + char buf[1]; + loff_t pos = 0; + int pixelSize; + + read_data_from_file("/data/CameraID.txt", buf, 1, &pos); + if (buf[0] == '0') + cam_id = CAMERA_SINGLE_REAR; + else if (buf[0] == '1') + cam_id = CAMERA_SINGLE_FRONT; + else if (buf[0] == '2') + cam_id = CAMERA_DUAL_REAR; + else if (buf[0] == '3') + cam_id = CAMERA_DUAL_FRONT; + + if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_3L2)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_3L2), "%s", FIMC_IS_FW_3L2); + snprintf(setf_name, sizeof(FIMC_IS_3L2_SETF), "%s", FIMC_IS_3L2_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_IMX135)) { + snprintf(fw_name, sizeof(FIMC_IS_FW), "%s", FIMC_IS_FW); + snprintf(setf_name, sizeof(FIMC_IS_IMX135_SETF), "%s", FIMC_IS_IMX135_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_IMX134)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX134), "%s", FIMC_IS_FW_IMX134); + snprintf(setf_name, sizeof(FIMC_IS_IMX134_SETF), "%s", FIMC_IS_IMX134_SETF); + } else { + pixelSize = fimc_is_sec_get_pixel_size(sysfs_finfo.header_ver); + if (pixelSize == 13) { + snprintf(fw_name, sizeof(FIMC_IS_FW), "%s", FIMC_IS_FW); + snprintf(setf_name, sizeof(FIMC_IS_IMX135_SETF), "%s", FIMC_IS_IMX135_SETF); + } else if (pixelSize == 8) { + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX134), "%s", FIMC_IS_FW_IMX134); + snprintf(setf_name, sizeof(FIMC_IS_IMX134_SETF), "%s", FIMC_IS_IMX134_SETF); + } else { + snprintf(fw_name, sizeof(FIMC_IS_FW), "%s", FIMC_IS_FW); + snprintf(setf_name, sizeof(FIMC_IS_IMX135_SETF), "%s", FIMC_IS_IMX135_SETF); + } + } + + if (cam_id == CAMERA_SINGLE_FRONT || + cam_id == CAMERA_DUAL_FRONT) { + snprintf(setf_name, sizeof(FIMC_IS_6B2_SETF), "%s", FIMC_IS_6B2_SETF); + } +#else + pr_err("%s: waring, you're calling the disabled func!\n\n", __func__); +#endif + return 0; +} + +int fimc_is_sec_fw_revision(char *fw_ver) +{ + int revision = 0; + revision = revision + ((int)fw_ver[FW_PUB_YEAR] - 58) * 10000; + revision = revision + ((int)fw_ver[FW_PUB_MON] - 64) * 100; + revision = revision + ((int)fw_ver[FW_PUB_NUM] - 48) * 10; + revision = revision + (int)fw_ver[FW_PUB_NUM + 1] - 48; + + return revision; +} + +bool fimc_is_sec_fw_module_compare(char *fw_ver1, char *fw_ver2) +{ + if (fw_ver1[FW_CORE_VER] != fw_ver2[FW_CORE_VER] + || fw_ver1[FW_PIXEL_SIZE] != fw_ver2[FW_PIXEL_SIZE] + || fw_ver1[FW_PIXEL_SIZE + 1] != fw_ver2[FW_PIXEL_SIZE + 1] + || fw_ver1[FW_ISP_COMPANY] != fw_ver2[FW_ISP_COMPANY] + || fw_ver1[FW_SENSOR_MAKER] != fw_ver2[FW_SENSOR_MAKER]) { + return false; + } + + return true; +} + +bool fimc_is_sec_check_cal_crc32(char *buf, int id) +{ + u32 *buf32 = NULL; + u32 checksum; + u32 check_base; + u32 check_length; + u32 checksum_base; + bool crc32_temp, crc32_header_temp; + struct fimc_is_from_info *finfo = NULL; + + buf32 = (u32 *)buf; + + printk(KERN_INFO "+++ %s\n", __func__); + + + crc32_temp = true; +#ifdef CONFIG_COMPANION_USE + crc32_c1_check = true; +#endif + + /* Header data */ + check_base = 0; + checksum = 0; +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + finfo = &sysfs_finfo_front; + checksum_base = ((check_base & 0xffffff00) + 0xfc) / 4; + } else +#endif + { + finfo = &sysfs_finfo; +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + checksum_base = ((check_base & 0xffffff00) + 0xfc) / 4; +#else + checksum_base = ((check_base & 0xfffff000) + 0xffc) / 4; +#endif + } + + checksum = getCRC((u16 *)&buf32[check_base], HEADER_CRC32_LEN, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the header (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_temp = false; + crc32_header_temp = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: Header checksum address has error(0x%08X)", checksum_base * 4); + } else { + crc32_header_temp = true; + } + + /* OEM */ + check_base = finfo->oem_start_addr / 4; + checksum = 0; + check_length = (finfo->oem_end_addr - finfo->oem_start_addr + 1) / 2; + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + checksum_base = ((finfo->oem_end_addr & 0xffffff00) + 0xfc) / 4; + } else +#endif + { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + checksum_base = ((finfo->oem_end_addr & 0xffffff00) + 0xfc) / 4; +#else + checksum_base = ((finfo->oem_end_addr & 0xfffff000) + 0xffc) / 4; +#endif + } + + checksum = getCRC((u16 *)&buf32[check_base], + check_length, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the OEM (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_temp = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: OEM checksum address has error(0x%08X)", checksum_base * 4); + } + + /* AWB */ + check_base = finfo->awb_start_addr / 4; + checksum = 0; + check_length = (finfo->awb_end_addr - finfo->awb_start_addr + 1) / 2; + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + checksum_base = ((finfo->awb_end_addr & 0xffffff00) + 0xfc) / 4; + } else +#endif + { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + checksum_base = ((finfo->awb_end_addr & 0xffffff00) + 0xfc) / 4; +#else + checksum_base = ((finfo->awb_end_addr & 0xfffff000) + 0xffc) / 4; +#endif + } + + checksum = getCRC((u16 *)&buf32[check_base], + check_length, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the AWB (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_temp = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: AWB checksum address has error(0x%08X)", checksum_base * 4); + } + + /* Shading */ + check_base = finfo->shading_start_addr / 4; + checksum = 0; + check_length = (finfo->shading_end_addr - finfo->shading_start_addr + 1) / 2; + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + checksum_base = 0x1ffc / 4; + } else +#endif + { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + checksum_base = 0x1ffc / 4; +#else + checksum_base = ((finfo->shading_end_addr & 0xfffff000) + 0xffc) / 4; +#endif + } + + checksum = getCRC((u16 *)&buf32[check_base], + check_length, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the Shading (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_temp = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: Shading checksum address has error(0x%08X)", checksum_base * 4); + } + +#ifdef CONFIG_COMPANION_USE + /* pdaf cal */ + check_base = finfo->pdaf_cal_start_addr / 4; + checksum = 0; + check_length = (finfo->pdaf_cal_end_addr - finfo->pdaf_cal_start_addr + 1) / 2; + checksum_base = ((0x8FFF & 0xfffff000) + 0xffc) / 4; + + checksum = getCRC((u16 *)&buf32[check_base], + check_length, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the pdaf cal (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_temp = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: pdaf cal checksum address has error(0x%08X)", checksum_base * 4); + } + + /* concord cal */ + check_base = finfo->concord_cal_start_addr / 4; + checksum = 0; + check_length = (finfo->concord_cal_end_addr - finfo->concord_cal_start_addr + 1) / 2; + checksum_base = ((finfo->concord_cal_end_addr & 0xfffff000) + 0xffc) / 4; + + checksum = getCRC((u16 *)&buf32[check_base], + check_length, NULL, NULL); + if (checksum_base < 0x80000 && checksum_base > 0 && checksum != buf32[checksum_base]) { + err("Camera: CRC32 error at the concord cal (0x%08X != 0x%08X)", + checksum, buf32[checksum_base]); + crc32_c1_check = false; + } else if (checksum_base > 0x80000 || checksum_base < 0) { + err("Camera: concord cal checksum address has error(0x%08X)", checksum_base * 4); + } +#endif + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + crc32_check_front = crc32_temp; + crc32_header_check_front = crc32_header_temp; + return crc32_check_front; + } else +#endif + { + crc32_check = crc32_temp; + crc32_header_check = crc32_header_temp; +#ifdef CONFIG_COMPANION_USE + return crc32_check && crc32_c1_check; +#else + return crc32_check; +#endif + } +} + +bool fimc_is_sec_check_fw_crc32(char *buf) +{ + u32 *buf32 = NULL; + u32 checksum; + + buf32 = (u32 *)buf; + + pr_info("Camera: Start checking CRC32 FW\n\n"); + + crc32_fw_check = true; + + checksum = 0; + + checksum = getCRC((u16 *)&buf32[0], + (sysfs_finfo.setfile_end_addr - sysfs_finfo.bin_start_addr + 1)/2, NULL, NULL); + if (checksum != buf32[(0x3FFFFC - 0x80000)/4]) { + err("Camera: CRC32 error at the binary section (0x%08X != 0x%08X)", + checksum, buf32[(0x3FFFFC - 0x80000)/4]); + crc32_fw_check = false; + } + + pr_info("Camera: End checking CRC32 FW\n\n"); + + return crc32_fw_check; +} + +#ifdef CONFIG_COMPANION_USE +bool fimc_is_sec_check_companion_fw_crc32(char *buf) +{ + u32 *buf32 = NULL; + u32 checksum; + + buf32 = (u32 *)buf; + + pr_info("Camera: Start checking CRC32 Companion FW\n\n"); + + crc32_c1_fw_check = true; + + checksum = 0; + + checksum = getCRC((u16 *)&buf32[0], (sysfs_finfo.concord_mode_setfile_end_addr - sysfs_finfo.concord_bin_start_addr + 1)/2, NULL, NULL); + if (checksum != buf32[(0x6AFFC - 0x2B000)/4]) { + err("Camera: CRC32 error at the binary section (0x%08X != 0x%08X)", + checksum, buf32[(0x6AFFC - 0x2B000)/4]); + crc32_c1_fw_check = false; + } + + pr_info("Camera: End checking CRC32 Companion FW\n\n"); + + return crc32_c1_fw_check; +} +#endif + +ssize_t write_data_to_file(char *name, char *buf, size_t count, loff_t *pos) +{ + struct file *fp; + mm_segment_t old_fs; + ssize_t tx; + int fd, old_mask; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + old_mask = sys_umask(0); + + fd = sys_open(name, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0664); + if (fd < 0) { + err("open file error!!\n"); + sys_umask(old_mask); + set_fs(old_fs); + return -EINVAL; + } + + fp = fget(fd); + if (fp) { + tx = vfs_write(fp, buf, count, pos); + vfs_fsync(fp, 0); + fput(fp); + } + + sys_close(fd); + sys_umask(old_mask); + set_fs(old_fs); + + return count; +} + +ssize_t read_data_from_file(char *name, char *buf, size_t count, loff_t *pos) +{ + struct file *fp; + mm_segment_t old_fs; + ssize_t tx; + int fd; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = sys_open(name, O_RDONLY, 0664); + if (fd < 0) { + if (-ENOENT == fd) + pr_info("%s: file(%s) not exist!\n", __func__, name); + else + pr_info("%s: error %d, failed to open %s\n", __func__, fd, name); + + set_fs(old_fs); + return -EINVAL; + } + fp = fget(fd); + if (fp) { + tx = vfs_read(fp, buf, count, pos); + fput(fp); + } + sys_close(fd); + set_fs(old_fs); + + return count; +} + +void fimc_is_sec_make_crc32_table(u32 *table, u32 id) +{ + u32 i, j, k; + + for (i = 0; i < 256; ++i) { + k = i; + for (j = 0; j < 8; ++j) { + if (k & 1) + k = (k >> 1) ^ id; + else + k >>= 1; + } + table[i] = k; + } +} + +#if 0 /* unused */ +static void fimc_is_read_sensor_version(void) +{ + int ret; + char buf[0x50]; + + memset(buf, 0x0, 0x50); + + printk(KERN_INFO "+++ %s\n", __func__); + + ret = fimc_is_spi_read(buf, 0x0, 0x50); + + printk(KERN_INFO "--- %s\n", __func__); + + if (ret) { + err("fimc_is_spi_read - can't read sensor version\n"); + } + + err("Manufacturer ID(0x40): 0x%02x\n", buf[0x40]); + err("Pixel Number(0x41): 0x%02x\n", buf[0x41]); +} + +static void fimc_is_read_sensor_version2(void) +{ + char *buf; + char *cal_data; + u32 cur; + u32 count = SETFILE_SIZE/READ_SIZE; + u32 extra = SETFILE_SIZE%READ_SIZE; + + printk(KERN_ERR "%s\n\n\n\n", __func__); + + + buf = (char *)kmalloc(READ_SIZE, GFP_KERNEL); + cal_data = (char *)kmalloc(SETFILE_SIZE, GFP_KERNEL); + + memset(buf, 0x0, READ_SIZE); + memset(cal_data, 0x0, SETFILE_SIZE); + + + for (cur = 0; cur < SETFILE_SIZE; cur += READ_SIZE) { + fimc_is_spi_read(buf, cur, READ_SIZE); + memcpy(cal_data+cur, buf, READ_SIZE); + memset(buf, 0x0, READ_SIZE); + } + + if (extra != 0) { + fimc_is_spi_read(buf, cur, extra); + memcpy(cal_data+cur, buf, extra); + memset(buf, 0x0, extra); + } + + pr_info("Manufacturer ID(0x40): 0x%02x\n", cal_data[0x40]); + pr_info("Pixel Number(0x41): 0x%02x\n", cal_data[0x41]); + + + pr_info("Manufacturer ID(0x4FE7): 0x%02x\n", cal_data[0x4FE7]); + pr_info("Pixel Number(0x4FE8): 0x%02x\n", cal_data[0x4FE8]); + pr_info("Manufacturer ID(0x4FE9): 0x%02x\n", cal_data[0x4FE9]); + pr_info("Pixel Number(0x4FEA): 0x%02x\n", cal_data[0x4FEA]); + + kfree(buf); + kfree(cal_data); + +} + +static int fimc_is_get_cal_data(void) +{ + int err = 0; + struct file *fp = NULL; + mm_segment_t old_fs; + long ret = 0; + u8 mem0 = 0, mem1 = 0; + u32 CRC = 0; + u32 DataCRC = 0; + u32 IntOriginalCRC = 0; + u32 crc_index = 0; + int retryCnt = 2; + u32 header_crc32 = 0x1000; + u32 oem_crc32 = 0x2000; + u32 awb_crc32 = 0x3000; + u32 shading_crc32 = 0x6000; + u32 shading_header = 0x22C0; + + char *cal_data; + + crc32_check = true; + printk(KERN_INFO "%s\n\n\n\n", __func__); + printk(KERN_INFO "+++ %s\n", __func__); + + fimc_is_spi_read(cal_map_version, 0x60, 0x4); + printk(KERN_INFO "cal_map_version = %.4s\n", cal_map_version); + + if (cal_map_version[3] == '5') { + shading_crc32 = 0x6000; + shading_header = 0x22C0; + } else if (cal_map_version[3] == '6') { + shading_crc32 = 0x4000; + shading_header = 0x920; + } else { + shading_crc32 = 0x5000; + shading_header = 0x22C0; + } + + /* Make CRC Table */ + fimc_is_sec_make_crc32_table((u32 *)&crc_table, 0xEDB88320); + + + retry: + cal_data = (char *)kmalloc(SETFILE_SIZE, GFP_KERNEL); + + memset(cal_data, 0x0, SETFILE_SIZE); + + mem0 = 0, mem1 = 0; + CRC = 0; + DataCRC = 0; + IntOriginalCRC = 0; + crc_index = 0; + + fimc_is_spi_read(cal_data, 0, SETFILE_SIZE); + + CRC = ~CRC; + for (crc_index = 0; crc_index < (0x80)/2; crc_index++) { + /*low byte*/ + mem0 = (unsigned char)(cal_data[crc_index*2] & 0x00ff); + /*high byte*/ + mem1 = (unsigned char)((cal_data[crc_index*2+1]) & 0x00ff); + CRC = crc_table[(CRC ^ (mem0)) & 0xFF] ^ (CRC >> 8); + CRC = crc_table[(CRC ^ (mem1)) & 0xFF] ^ (CRC >> 8); + } + CRC = ~CRC; + + + DataCRC = (CRC&0x000000ff)<<24; + DataCRC += (CRC&0x0000ff00)<<8; + DataCRC += (CRC&0x00ff0000)>>8; + DataCRC += (CRC&0xff000000)>>24; + printk(KERN_INFO "made HEADER CSC value by S/W = 0x%x\n", DataCRC); + + IntOriginalCRC = (cal_data[header_crc32-4]&0x00ff)<<24; + IntOriginalCRC += (cal_data[header_crc32-3]&0x00ff)<<16; + IntOriginalCRC += (cal_data[header_crc32-2]&0x00ff)<<8; + IntOriginalCRC += (cal_data[header_crc32-1]&0x00ff); + printk(KERN_INFO "Original HEADER CRC Int = 0x%x\n", IntOriginalCRC); + + if (IntOriginalCRC != DataCRC) + crc32_check = false; + + CRC = 0; + CRC = ~CRC; + for (crc_index = 0; crc_index < (0xC0)/2; crc_index++) { + /*low byte*/ + mem0 = (unsigned char)(cal_data[0x1000 + crc_index*2] & 0x00ff); + /*high byte*/ + mem1 = (unsigned char)((cal_data[0x1000 + crc_index*2+1]) & 0x00ff); + CRC = crc_table[(CRC ^ (mem0)) & 0xFF] ^ (CRC >> 8); + CRC = crc_table[(CRC ^ (mem1)) & 0xFF] ^ (CRC >> 8); + } + CRC = ~CRC; + + + DataCRC = (CRC&0x000000ff)<<24; + DataCRC += (CRC&0x0000ff00)<<8; + DataCRC += (CRC&0x00ff0000)>>8; + DataCRC += (CRC&0xff000000)>>24; + printk(KERN_INFO "made OEM CSC value by S/W = 0x%x\n", DataCRC); + + IntOriginalCRC = (cal_data[oem_crc32-4]&0x00ff)<<24; + IntOriginalCRC += (cal_data[oem_crc32-3]&0x00ff)<<16; + IntOriginalCRC += (cal_data[oem_crc32-2]&0x00ff)<<8; + IntOriginalCRC += (cal_data[oem_crc32-1]&0x00ff); + printk(KERN_INFO "Original OEM CRC Int = 0x%x\n", IntOriginalCRC); + + if (IntOriginalCRC != DataCRC) + crc32_check = false; + + + CRC = 0; + CRC = ~CRC; + for (crc_index = 0; crc_index < (0x20)/2; crc_index++) { + /*low byte*/ + mem0 = (unsigned char)(cal_data[0x2000 + crc_index*2] & 0x00ff); + /*high byte*/ + mem1 = (unsigned char)((cal_data[0x2000 + crc_index*2+1]) & 0x00ff); + CRC = crc_table[(CRC ^ (mem0)) & 0xFF] ^ (CRC >> 8); + CRC = crc_table[(CRC ^ (mem1)) & 0xFF] ^ (CRC >> 8); + } + CRC = ~CRC; + + + DataCRC = (CRC&0x000000ff)<<24; + DataCRC += (CRC&0x0000ff00)<<8; + DataCRC += (CRC&0x00ff0000)>>8; + DataCRC += (CRC&0xff000000)>>24; + printk(KERN_INFO "made AWB CSC value by S/W = 0x%x\n", DataCRC); + + IntOriginalCRC = (cal_data[awb_crc32-4]&0x00ff)<<24; + IntOriginalCRC += (cal_data[awb_crc32-3]&0x00ff)<<16; + IntOriginalCRC += (cal_data[awb_crc32-2]&0x00ff)<<8; + IntOriginalCRC += (cal_data[awb_crc32-1]&0x00ff); + printk(KERN_INFO "Original AWB CRC Int = 0x%x\n", IntOriginalCRC); + + if (IntOriginalCRC != DataCRC) + crc32_check = false; + + + CRC = 0; + CRC = ~CRC; + for (crc_index = 0; crc_index < (shading_header)/2; crc_index++) { + + /*low byte*/ + mem0 = (unsigned char)(cal_data[0x3000 + crc_index*2] & 0x00ff); + /*high byte*/ + mem1 = (unsigned char)((cal_data[0x3000 + crc_index*2+1]) & 0x00ff); + CRC = crc_table[(CRC ^ (mem0)) & 0xFF] ^ (CRC >> 8); + CRC = crc_table[(CRC ^ (mem1)) & 0xFF] ^ (CRC >> 8); + } + CRC = ~CRC; + + + DataCRC = (CRC&0x000000ff)<<24; + DataCRC += (CRC&0x0000ff00)<<8; + DataCRC += (CRC&0x00ff0000)>>8; + DataCRC += (CRC&0xff000000)>>24; + printk(KERN_INFO "made SHADING CSC value by S/W = 0x%x\n", DataCRC); + + IntOriginalCRC = (cal_data[shading_crc32-4]&0x00ff)<<24; + IntOriginalCRC += (cal_data[shading_crc32-3]&0x00ff)<<16; + IntOriginalCRC += (cal_data[shading_crc32-2]&0x00ff)<<8; + IntOriginalCRC += (cal_data[shading_crc32-1]&0x00ff); + printk(KERN_INFO "Original SHADING CRC Int = 0x%x\n", IntOriginalCRC); + + if (IntOriginalCRC != DataCRC) + crc32_check = false; + + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (crc32_check == true) { + printk(KERN_INFO "make cal_data.bin~~~~ \n"); + fp = filp_open(FIMC_IS_CAL_SDCARD, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (IS_ERR(fp) || fp == NULL) { + printk(KERN_INFO "failed to open %s, err %ld\n", + FIMC_IS_CAL_SDCARD, PTR_ERR(fp)); + err = -EINVAL; + goto out; + } + + ret = vfs_write(fp, (char __user *)cal_data, + SETFILE_SIZE, &fp->f_pos); + + } else { + if (retryCnt > 0) { + set_fs(old_fs); + retryCnt--; + goto retry; + } + } + +/* + { + fp = filp_open(FIMC_IS_CAL_SDCARD, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (IS_ERR(fp) || fp == NULL) { + printk(KERN_INFO "failed to open %s, err %ld\n", + FIMC_IS_CAL_SDCARD, PTR_ERR(fp)); + err = -EINVAL; + goto out; + } + + ret = vfs_write(fp, (char __user *)cal_data, + SETFILE_SIZE, &fp->f_pos); + + } +*/ + + if (fp != NULL) + filp_close(fp, current->files); + + out: + set_fs(old_fs); + kfree(cal_data); + return err; + +} + +#endif + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +int fimc_is_i2c_read(struct i2c_client *client, void *buf, u32 addr, size_t size) +{ + const u32 addr_size = 2, max_retry = 5; + u8 addr_buf[addr_size]; + int retries = max_retry; + int ret = 0; + + if (!client) { + pr_info("%s: client is null\n", __func__); + return -ENODEV; + } + + /* pr_info("%s %s: fimc_is_i2c_read\n", + dev_driver_string(&client->dev), dev_name(&client->dev));*/ + + /* Send addr */ + addr_buf[0] = ((u16)addr) >> 8; + addr_buf[1] = (u8)addr; + + for (retries = max_retry; retries > 0; retries--) { + ret = i2c_master_send(client, addr_buf, addr_size); + if (likely(addr_size == ret)) + break; + + pr_info("%s: i2c_master_send failed(%d), try %d\n", __func__, ret, retries); + usleep_range(1000, 1000); + } + + if (unlikely(ret <= 0)) { + pr_err("%s: error %d, fail to write 0x%04X\n", __func__, ret, addr); + return ret ? ret : -ETIMEDOUT; + } + + /* Receive data */ + for (retries = max_retry; retries > 0; retries--) { + ret = i2c_master_recv(client, buf, size); + if (likely(ret == size)) + break; + + pr_info("%s: i2c_master_recv failed(%d), try %d\n", __func__, ret, retries); + usleep_range(1000, 1000); + } + + if (unlikely(ret <= 0)) { + pr_err("%s: error %d, fail to read 0x%04X\n", __func__, ret, addr); + return ret ? ret : -ETIMEDOUT; + } + + return 0; +} + +int fimc_is_i2c_write(struct i2c_client *client, void *buf, u32 addr, size_t size) +{ + pr_info("%s: do nothing\n", __func__); + return 0; +} + +int fimc_is_sec_read_eeprom_header(struct device *dev) +{ + int ret = 0; + struct fimc_is_core *core = dev_get_drvdata(dev); + u8 header_version[12] = {0, }; + struct i2c_client *client; + client = core->eeprom_client0; + + ret = fimc_is_i2c_read(client, header_version, 0x20, 0x0B); + if (unlikely(ret)) { + err("failed to fimc_is_i2c_read for header version (%d)\n", ret); + ret = -EINVAL; + } + + memcpy(sysfs_finfo.header_ver, header_version, 11); + sysfs_finfo.header_ver[11] = '\0'; + + return ret; +} + +int fimc_is_sec_readcal_eeprom(struct device *dev, int id) +{ + int ret = 0; + char *buf = NULL; + int retry = FIMC_IS_CAL_RETRY_CNT; + struct fimc_is_core *core = dev_get_drvdata(dev); + struct fimc_is_from_info *finfo = NULL; + int cal_size = 0; + struct i2c_client *client = NULL; + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + finfo = &sysfs_finfo_front; + fimc_is_sec_get_front_cal_buf(&buf); + cal_size = FIMC_IS_MAX_CAL_SIZE_FRONT; + client = core->eeprom_client1; + } else +#endif + { + finfo = &sysfs_finfo; + fimc_is_sec_get_cal_buf(&buf); + cal_size = FIMC_IS_MAX_CAL_SIZE; + client = core->eeprom_client0; + } + + ret = fimc_is_i2c_read(client, cal_map_version, 0x30, 0x4); + if (unlikely(ret)) { + err("failed to fimc_is_i2c_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + printk(KERN_INFO "Camera: Cal map_version = %c%c%c%c\n", cal_map_version[0], + cal_map_version[1], cal_map_version[2], cal_map_version[3]); + +crc_retry: + + /* read cal data */ + pr_info("Camera: I2C read cal data\n\n"); + fimc_is_i2c_read(client, buf, 0x0, cal_size); + + finfo->oem_start_addr = *((u32 *)&buf[0x0]); + finfo->oem_end_addr = *((u32 *)&buf[0x04]); + pr_info("OEM start = 0x%08x, end = 0x%08x\n", + (finfo->oem_start_addr), (finfo->oem_end_addr)); + finfo->awb_start_addr = *((u32 *)&buf[0x08]); + finfo->awb_end_addr = *((u32 *)&buf[0x0C]); + pr_info("AWB start = 0x%08x, end = 0x%08x\n", + (finfo->awb_start_addr), (finfo->awb_end_addr)); + finfo->shading_start_addr = *((u32 *)&buf[0x10]); + finfo->shading_end_addr = *((u32 *)&buf[0x14]); + if (finfo->shading_end_addr > 0x1fff) { + err("Shading end_addr has error!! 0x%08x", finfo->shading_end_addr); + finfo->setfile_end_addr = 0x1fff; + } + pr_info("Shading start = 0x%08x, end = 0x%08x\n", + (finfo->shading_start_addr), (finfo->shading_end_addr)); + + /* HEARDER Data : Module/Manufacturer Information */ + memcpy(finfo->header_ver, &buf[0x20], 11); + finfo->header_ver[11] = '\0'; + /* HEARDER Data : Cal Map Version */ + memcpy(finfo->cal_map_ver, &buf[0x30], 4); + + memcpy(finfo->project_name, &buf[0x38], 8); + finfo->project_name[8] = '\0'; + + /* OEM Data : Module/Manufacturer Information */ + memcpy(finfo->oem_ver, &buf[0x150], 11); + finfo->oem_ver[11] = '\0'; + + /* AWB Data : Module/Manufacturer Information */ + memcpy(finfo->awb_ver, &buf[0x220], 11); + finfo->awb_ver[11] = '\0'; + + /* SHADING Data : Module/Manufacturer Information */ + memcpy(finfo->shading_ver, &buf[0x1CE0], 11); + finfo->shading_ver[11] = '\0'; + + /* debug info dump */ +#if defined(EEPROM_DEBUG) + pr_info("++++ EEPROM data info\n"); + pr_info("1. Header info\n"); + pr_info("Module info : %s\n", finfo->header_ver); + pr_info(" ID : %c\n", finfo->header_ver[0]); + pr_info(" Pixel num : %c%c\n", finfo->header_ver[1], + finfo->header_ver[2]); + pr_info(" ISP ID : %c\n", finfo->header_ver[3]); + pr_info(" Sensor Maker : %c\n", finfo->header_ver[4]); + pr_info(" Module ver : %c\n", finfo->header_ver[6]); + pr_info(" Year : %c\n", finfo->header_ver[7]); + pr_info(" Month : %c\n", finfo->header_ver[8]); + pr_info(" Release num : %c%c\n", finfo->header_ver[9], + finfo->header_ver[10]); + pr_info("project_name : %s\n", finfo->project_name); + pr_info("Cal data map ver : %s\n", finfo->cal_map_ver); + pr_info("2. OEM info\n"); + pr_info("Module info : %s\n", finfo->oem_ver); + pr_info("3. AWB info\n"); + pr_info("Module info : %s\n", finfo->awb_ver); + pr_info("4. Shading info\n"); + pr_info("Module info : %s\n", finfo->shading_ver); + pr_info("---- EEPROM data info\n"); +#endif + + /* CRC check */ + if (id == SENSOR_POSITION_FRONT) { + ret = fimc_is_sec_check_cal_crc32(buf, SENSOR_POSITION_FRONT); + } else { + ret = fimc_is_sec_check_cal_crc32(buf, SENSOR_POSITION_REAR); + } + if (!ret && (retry > 0)) { + retry--; + goto crc_retry; + } + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + if (finfo->header_ver[3] == 'L') { + crc32_check_factory_front = crc32_check_front; + } else { + crc32_check_factory_front = false; + } + } else +#endif + { + if (finfo->header_ver[3] == 'L') { + crc32_check_factory = crc32_check; + } else { + crc32_check_factory = false; + } + + if (finfo->project_name[6] == 'C' && finfo->header_ver[0] == 'E' && finfo->header_ver[1] == '0' && + finfo->header_ver[2] == '8') { + pr_info("This camera module use IMX134+EEPROM - project_name: %c-%c\n" + , finfo->project_name[6], finfo->project_name[7]); + + if (!core->use_module_check) { + is_right_prj_name = true; + is_latest_cam_module = true; + is_final_cam_module = true; + } + } + } + +exit: + return ret; +} +#endif + +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +extern int fimc_is_spi_reset_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size); +extern int fimc_is_spi_read_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size); +extern int fimc_is_spi_read_module_id(struct spi_device *spi, void *buf, u16 rx_addr, size_t size); + +int fimc_is_sec_read_from_header(struct device *dev) +{ + int ret = 0; + struct fimc_is_core *core = dev_get_drvdata(dev); + u8 header_version[12] = {0, }; + + ret = fimc_is_spi_read_by_core(core->spi0, header_version, 0x40, 0x0B); + if (ret < 0) { + printk(KERN_ERR "failed to fimc_is_spi_read for header version (%d)\n", ret); + ret = -EINVAL; + } + + memcpy(sysfs_finfo.header_ver, header_version, 11); + sysfs_finfo.header_ver[11] = '\0'; + + return ret; +} + +int fimc_is_sec_readcal(struct fimc_is_core *core) +{ + int ret = 0; + int retry = FIMC_IS_CAL_RETRY_CNT; + char spi_buf[0x50] = {0, }; + struct file *key_fp = NULL; + struct file *dump_fp = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + u16 id; + + /* reset spi */ + if (!core->spi0) { + pr_err("spi0 device is not available"); + goto exit; + } + + ret = fimc_is_spi_reset_by_core(core->spi0, spi_buf, 0x0, FIMC_IS_MAX_CAL_SIZE); + if (ret < 0) { + err("failed to fimc_is_spi_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + + ret = fimc_is_spi_read_module_id(core->spi0, &id, 0x0, 0x2); + pr_info("Camera: FROM Module ID = 0x%04x\n", id); + + ret = fimc_is_spi_read_by_core(core->spi0, cal_map_version, 0x60, 0x4); + if (ret < 0) { + printk(KERN_ERR "failed to fimc_is_spi_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + pr_info("Camera: Cal map_version = %c%c%c%c\n", cal_map_version[0], + cal_map_version[1], cal_map_version[2], cal_map_version[3]); +crc_retry: + /* read cal data */ + pr_info("Camera: SPI read cal data\n\n"); + ret = fimc_is_spi_read_by_core(core->spi0, cal_buf, 0x0, FIMC_IS_MAX_CAL_SIZE); + if (ret < 0) { + err("failed to fimc_is_spi_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + + sysfs_finfo.bin_start_addr = *((u32 *)&cal_buf[0]); + sysfs_finfo.bin_end_addr = *((u32 *)&cal_buf[4]); + pr_info("Binary start = 0x%08x, end = 0x%08x\n", + (sysfs_finfo.bin_start_addr), (sysfs_finfo.bin_end_addr)); + sysfs_finfo.oem_start_addr = *((u32 *)&cal_buf[8]); + sysfs_finfo.oem_end_addr = 0x113F;//*((u32 *)&cal_buf[12]); + pr_info("OEM start = 0x%08x, end = 0x%08x\n", + (sysfs_finfo.oem_start_addr), (sysfs_finfo.oem_end_addr)); + sysfs_finfo.awb_start_addr = *((u32 *)&cal_buf[16]); + sysfs_finfo.awb_end_addr = *((u32 *)&cal_buf[20]); + pr_info("AWB start = 0x%08x, end = 0x%08x\n", + (sysfs_finfo.awb_start_addr), (sysfs_finfo.awb_end_addr)); + sysfs_finfo.shading_start_addr = *((u32 *)&cal_buf[24]); + sysfs_finfo.shading_end_addr = *((u32 *)&cal_buf[28]); + pr_info("Shading start = 0x%08x, end = 0x%08x\n", + (sysfs_finfo.shading_start_addr), (sysfs_finfo.shading_end_addr)); + sysfs_finfo.setfile_start_addr = *((u32 *)&cal_buf[32]); + sysfs_finfo.setfile_end_addr = *((u32 *)&cal_buf[36]); + /*if (sysfs_finfo.setfile_start_addr == 0xffffffff) { + sysfs_finfo.setfile_start_addr = *((u32 *)&cal_buf[40]); + sysfs_finfo.setfile_end_addr = *((u32 *)&cal_buf[44]); + }*/ +#ifdef CONFIG_COMPANION_USE + sysfs_finfo.concord_cal_start_addr = *((u32 *)&cal_buf[40]); + sysfs_finfo.concord_cal_end_addr = 0xF23F;//*((u32 *)&cal_buf[44]); + pr_info("concord cal start = 0x%08x, end = 0x%08x\n", + sysfs_finfo.concord_cal_start_addr, sysfs_finfo.concord_cal_end_addr); + sysfs_finfo.concord_bin_start_addr = *((u32 *)&cal_buf[48]); + sysfs_finfo.concord_bin_end_addr = *((u32 *)&cal_buf[52]); + pr_info("concord bin start = 0x%08x, end = 0x%08x\n", + sysfs_finfo.concord_bin_start_addr, sysfs_finfo.concord_bin_end_addr); + sysfs_finfo.concord_master_setfile_start_addr = *((u32 *)&cal_buf[168]); + sysfs_finfo.concord_master_setfile_end_addr = sysfs_finfo.concord_master_setfile_start_addr + 16064; + sysfs_finfo.concord_mode_setfile_start_addr = sysfs_finfo.concord_master_setfile_end_addr + 1; + sysfs_finfo.concord_mode_setfile_end_addr = *((u32 *)&cal_buf[172]); + sysfs_finfo.pdaf_cal_start_addr = 0x5000; + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V005) + sysfs_finfo.pdaf_cal_end_addr = 0x540F; + else + sysfs_finfo.pdaf_cal_end_addr = 0x521F; + pr_info("pdaf start = 0x%08x, end = 0x%08x\n", + sysfs_finfo.pdaf_cal_start_addr, sysfs_finfo.pdaf_cal_end_addr); + + sysfs_finfo.lsc_i0_gain_addr = 0x3006; + pr_info("Shading lsc_i0 start = 0x%08x\n", sysfs_finfo.lsc_i0_gain_addr); + sysfs_finfo.lsc_j0_gain_addr = sysfs_finfo.lsc_i0_gain_addr + 0x2; + pr_info("Shading lsc_j0 start = 0x%08x\n", sysfs_finfo.lsc_j0_gain_addr); + sysfs_finfo.lsc_a_gain_addr = sysfs_finfo.lsc_j0_gain_addr + 0x2; + pr_info("Shading lsc_a start = 0x%08x\n", sysfs_finfo.lsc_a_gain_addr); + sysfs_finfo.lsc_k4_gain_addr = sysfs_finfo.lsc_a_gain_addr + 0x4; + pr_info("Shading lsc_k4 start = 0x%08x\n", sysfs_finfo.lsc_k4_gain_addr); + sysfs_finfo.lsc_scale_gain_addr = sysfs_finfo.lsc_k4_gain_addr + 0x4; + pr_info("Shading lsc_scale start = 0x%08x\n", sysfs_finfo.lsc_scale_gain_addr); + + sysfs_finfo.lsc_gain_start_addr = sysfs_finfo.shading_start_addr + 0x14; + sysfs_finfo.lsc_gain_end_addr = sysfs_finfo.lsc_gain_start_addr + 6600 -1; + pr_info("LSC start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.lsc_gain_start_addr, sysfs_finfo.lsc_gain_end_addr); + sysfs_finfo.pdaf_start_addr = sysfs_finfo.concord_cal_start_addr; + sysfs_finfo.pdaf_end_addr = sysfs_finfo.pdaf_start_addr + 512 -1; + pr_info("pdaf_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.pdaf_start_addr, sysfs_finfo.pdaf_end_addr); + /*sysfs_finfo.coefficient_cal_start_addr = sysfs_finfo.pdaf_start_addr + 512 + 16; + sysfs_finfo.coefficient_cal_end_addr = sysfs_finfo.coefficient_cal_start_addr + 24576 -1; + pr_info("coefficient_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coefficient_cal_start_addr, sysfs_finfo.coefficient_cal_end_addr);*/ + sysfs_finfo.coef1_start = sysfs_finfo.pdaf_start_addr + 512 + 16; + sysfs_finfo.coef1_end = sysfs_finfo.coef1_start + 4032 -1; + pr_info("coefficient1_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef1_start, sysfs_finfo.coef1_end); + sysfs_finfo.coef2_start = sysfs_finfo.coef1_end + 64 + 1; + sysfs_finfo.coef2_end = sysfs_finfo.coef2_start + 4032 -1; + pr_info("coefficient2_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef2_start, sysfs_finfo.coef2_end); + sysfs_finfo.coef3_start = sysfs_finfo.coef2_end + 64 + 1; + sysfs_finfo.coef3_end = sysfs_finfo.coef3_start + 4032 -1; + pr_info("coefficient3_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef3_start, sysfs_finfo.coef3_end); + sysfs_finfo.coef4_start = sysfs_finfo.coef3_end + 64 + 1; + sysfs_finfo.coef4_end = sysfs_finfo.coef4_start + 4032 -1; + pr_info("coefficient4_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef4_start, sysfs_finfo.coef4_end); + sysfs_finfo.coef5_start = sysfs_finfo.coef4_end + 64 + 1; + sysfs_finfo.coef5_end = sysfs_finfo.coef5_start + 4032 -1; + pr_info("coefficient5_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef5_start, sysfs_finfo.coef5_end); + sysfs_finfo.coef6_start = sysfs_finfo.coef5_end + 64 + 1; + sysfs_finfo.coef6_end = sysfs_finfo.coef6_start + 4032 -1; + pr_info("coefficient6_cal_addr start = 0x%04x, end = 0x%04x\n", + sysfs_finfo.coef6_start, sysfs_finfo.coef6_end); + sysfs_finfo.wcoefficient1_addr = 0xF210; + pr_info("Shading wcoefficient1 start = 0x%04x\n", sysfs_finfo.wcoefficient1_addr); + memcpy(sysfs_finfo.concord_header_ver, &cal_buf[80], 11); + sysfs_finfo.concord_header_ver[11] = '\0'; + sysfs_finfo.af_inf_addr = 0x1000; + sysfs_finfo.af_macro_addr = 0x1008; + sysfs_finfo.lsc_gain_crc_addr = 0x49DC; + sysfs_finfo.pdaf_crc_addr= 0x9200; + sysfs_finfo.coef1_crc_addr= 0xF21A; + sysfs_finfo.coef2_crc_addr = 0xF21E; + sysfs_finfo.coef3_crc_addr = 0xF222; + sysfs_finfo.coef4_crc_addr = 0xF226; + sysfs_finfo.coef5_crc_addr = 0xF22A; + sysfs_finfo.coef6_crc_addr = 0xF22E; +#endif + + if (sysfs_finfo.setfile_end_addr < 0x8000 || sysfs_finfo.setfile_end_addr > 0x3fffff) { + err("setfile end_addr has error!! 0x%08x", sysfs_finfo.setfile_end_addr); + sysfs_finfo.setfile_end_addr = 0x1fffff; + } + + pr_info("Setfile start = 0x%08x, end = 0x%08x\n", + (sysfs_finfo.setfile_start_addr), (sysfs_finfo.setfile_end_addr)); + + memcpy(sysfs_finfo.header_ver, &cal_buf[64], 11); + sysfs_finfo.header_ver[11] = '\0'; + memcpy(sysfs_finfo.cal_map_ver, &cal_buf[96], 4); + memcpy(sysfs_finfo.setfile_ver, &cal_buf[100], 6); + sysfs_finfo.setfile_ver[6] = '\0'; + memcpy(sysfs_finfo.oem_ver, &cal_buf[8160], 11); + sysfs_finfo.oem_ver[11] = '\0'; + memcpy(sysfs_finfo.awb_ver, &cal_buf[12256], 11); + sysfs_finfo.awb_ver[11] = '\0'; + memcpy(sysfs_finfo.shading_ver, &cal_buf[16352], 11); + sysfs_finfo.shading_ver[11] = '\0'; + memcpy(sysfs_finfo.project_name, &cal_buf[110], 8); + sysfs_finfo.project_name[8] = '\0'; + + fw_core_version = sysfs_finfo.header_ver[0]; + /* debug info dump */ +//#if defined(FROM_DEBUG) +#if 1 + pr_err("++++ FROM data info\n"); + pr_err("1. Header info\n"); + pr_err("Module info : %s\n", sysfs_finfo.header_ver); +#ifdef CONFIG_COMPANION_USE + pr_err("Companion version info : %s\n", sysfs_finfo.concord_header_ver); +#endif + pr_err(" ID : %c\n", sysfs_finfo.header_ver[0]); + pr_err(" Pixel num : %c%c\n", sysfs_finfo.header_ver[1], + sysfs_finfo.header_ver[2]); + pr_err(" ISP ID : %c\n", sysfs_finfo.header_ver[3]); + pr_err(" Sensor Maker : %c\n", sysfs_finfo.header_ver[5]); + pr_err(" Module ver : %c\n", sysfs_finfo.header_ver[6]); + pr_err(" Year : %c\n", sysfs_finfo.header_ver[7]); + pr_err(" Month : %c\n", sysfs_finfo.header_ver[8]); + pr_err(" Release num : %c%c\n", sysfs_finfo.header_ver[9], + sysfs_finfo.header_ver[10]); + pr_err("Cal data map ver : %s\n", sysfs_finfo.cal_map_ver); + pr_err("Setfile ver : %s\n", sysfs_finfo.setfile_ver); + pr_err("Project name : %s\n", sysfs_finfo.project_name); + pr_err("2. OEM info\n"); + pr_err("Module info : %s\n", sysfs_finfo.oem_ver); + pr_err("3. AWB info\n"); + pr_err("Module info : %s\n", sysfs_finfo.awb_ver); + pr_err("4. Shading info\n"); + pr_err("Module info : %s\n", sysfs_finfo.shading_ver); + pr_err("---- FROM data info\n"); +#endif + + /* CRC check */ +#ifdef CONFIG_COMPANION_USE + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + if (!fimc_is_sec_check_cal_crc32(cal_buf, SENSOR_POSITION_REAR) && (retry > 0)) { + retry--; + goto crc_retry; + } + } else { + fw_version_crc_check = false; + crc32_check = false; + crc32_c1_check = false; + } +#else + if (!fimc_is_sec_check_cal_crc32(cal_buf, SENSOR_POSITION_REAR) && (retry > 0)) { + retry--; + goto crc_retry; + } +#endif + + if (sysfs_finfo.header_ver[3] == 'L') { + crc32_check_factory = crc32_check; +#ifdef CONFIG_COMPANION_USE + crc32_c1_check_factory = crc32_c1_check; +#endif + } else { + crc32_check_factory = false; +#ifdef CONFIG_COMPANION_USE + crc32_c1_check_factory = false; +#endif + } + +#ifdef CONFIG_COMPANION_USE + if (fimc_is_comp_is_compare_ver(core) >= FROM_VERSION_V004) { + if (crc32_check && crc32_c1_check) { + /* If FROM LSC value is not valid, loading default lsc data */ + if (*((u32 *)&cal_buf[sysfs_finfo.lsc_gain_start_addr]) == 0x00000000) { + companion_lsc_isvalid = false; + } else { + companion_lsc_isvalid = true; + } + if (*((u32 *)&cal_buf[sysfs_finfo.coef1_start]) == 0x00000000) { + companion_coef_isvalid = false; + } else { + companion_coef_isvalid = true; + } + } else { + companion_lsc_isvalid = false; + companion_coef_isvalid = false; + } + } else { + companion_lsc_isvalid = true; + companion_coef_isvalid = true; + } +#endif + + if (!core->use_module_check) { + is_latest_cam_module = true; + } else { + if (sysfs_finfo.header_ver[10] >= FIMC_IS_LATEST_FROM_VERSION_B) { + is_latest_cam_module = true; + } else { + is_latest_cam_module = false; + } + } + + if (core->use_module_check) { + if (sysfs_finfo.header_ver[10] == FIMC_IS_LATEST_FROM_VERSION_M) { + is_final_cam_module = true; + } else { + is_final_cam_module = false; + } + } else { + is_final_cam_module = true; + } + +#if defined(CONFIG_SOC_EXYNOS5433) + if (sysfs_finfo.project_name[6] != 'T' && sysfs_finfo.header_ver[0] == 'H' && sysfs_finfo.header_ver[1] == '1' && + sysfs_finfo.header_ver[2] == '6') { + pr_info("FROM has abnormal project name : %c-%c\n", sysfs_finfo.project_name[6], sysfs_finfo.project_name[7]); + is_right_prj_name = false; + } else { + is_right_prj_name = true; + } +#endif + + old_fs = get_fs(); + set_fs(KERNEL_DS); + key_fp = filp_open("/data/media/0/1q2w3e4r.key", O_RDONLY, 0); + if (IS_ERR(key_fp)) { + pr_info("KEY does not exist.\n"); + key_fp = NULL; + goto key_err; + } else { + dump_fp = filp_open("/data/media/0/dump", O_RDONLY, 0); + if (IS_ERR(dump_fp)) { + pr_info("dump folder does not exist.\n"); + dump_fp = NULL; + goto key_err; + } else { + pr_info("dump folder exist, Dump FROM cal data.\n"); + if (write_data_to_file("/data/media/0/dump/from_cal.bin", cal_buf, FIMC_IS_DUMP_CAL_SIZE, &pos) < 0) { + pr_info("Failedl to dump cal data.\n"); + goto dump_err; + } + } + } +dump_err: + if (dump_fp) + filp_close(dump_fp, current->files); +key_err: + if (key_fp) + filp_close(key_fp, current->files); + set_fs(old_fs); +exit: + return ret; +} + +int fimc_is_sec_readfw(struct fimc_is_core *core) +{ + int ret = 0; + char *buf = NULL; + loff_t pos = 0; + char fw_path[100]; + char setfile_path[100]; + char setf_name[50]; + int retry = FIMC_IS_FW_RETRY_CNT; + int pixelSize; +#ifdef USE_ION_ALLOC + struct ion_handle *handle = NULL; +#endif + + pr_info("Camera: FW, Setfile need to be dumped\n"); +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)FIMC_IS_MAX_FW_SIZE, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("(%s):failed to ioc_alloc\n",__func__); + ret = -ENOMEM; + goto exit; + } + + buf = (char *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("(%s)fail to ion_map_kernle\n",__func__); + ret = -ENOMEM; + goto exit; + } +#else + buf = vmalloc(FIMC_IS_MAX_FW_SIZE); + if (!buf) { + err("vmalloc fail"); + ret = -ENOMEM; + goto exit; + } +#endif + crc_retry: + + /* read fw data */ + pr_info("Camera: Start SPI read fw data\n\n"); + ret = fimc_is_spi_read_by_core(core->spi0, buf, 0x80000, FIMC_IS_MAX_FW_SIZE); + if (ret < 0) { + err("failed to fimc_is_spi_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + pr_info("Camera: End SPI read fw data\n\n"); + + /* CRC check */ + if (!fimc_is_sec_check_fw_crc32(buf) && (retry > 0)) { + retry--; + goto crc_retry; + } else if (!retry) { + ret = -EINVAL; + goto exit; + } + + snprintf(fw_path, sizeof(fw_path), "%s%s", FIMC_IS_FW_DUMP_PATH, FIMC_IS_FW); + if (write_data_to_file(fw_path, buf, + sysfs_finfo.bin_end_addr - sysfs_finfo.bin_start_addr + 1, &pos) < 0) { + ret = -EIO; + goto exit; + } + + pr_info("Camera: FW Data has dumped successfully\n"); + + if (sysfs_finfo.header_ver[FW_SENSOR_MAKER] == FW_SENSOR_MAKER_SLSI) { + snprintf(setf_name, sizeof(setf_name), "%s", FIMC_IS_2P2_SETF); + } else if (sysfs_finfo.header_ver[FW_SENSOR_MAKER] == FW_SENSOR_MAKER_SONY) { + pixelSize = fimc_is_sec_get_pixel_size(sysfs_finfo.header_ver); + if (pixelSize == 13) { + snprintf(setf_name, sizeof(setf_name), "%s", FIMC_IS_IMX135_SETF); + } else if (pixelSize == 8) { + snprintf(setf_name, sizeof(setf_name), "%s", FIMC_IS_IMX134_SETF); + } else { + snprintf(setf_name, sizeof(setf_name), "%s", FIMC_IS_IMX135_SETF); + } + } else { + snprintf(setf_name, sizeof(setf_name), "%s", FIMC_IS_2P2_SETF); + } + + snprintf(setfile_path, sizeof(setfile_path), "%s%s", FIMC_IS_FW_DUMP_PATH, setf_name); + pos = 0; + + if (write_data_to_file(setfile_path, + buf+(sysfs_finfo.setfile_start_addr - sysfs_finfo.bin_start_addr), + sysfs_finfo.setfile_end_addr - sysfs_finfo.setfile_start_addr + 1, &pos) < 0) { + ret = -EIO; + goto exit; + } + + pr_info("Camera: Setfile has dumped successfully\n"); + pr_info("Camera: FW, Setfile were dumped successfully\n"); + +exit: +#ifdef USE_ION_ALLOC + if (!IS_ERR_OR_NULL(buf)) { + ion_unmap_kernel(core->fimc_ion_client, handle); + } + + if (!IS_ERR_OR_NULL(handle)) { + ion_free(core->fimc_ion_client, handle); + } +#else + if (buf) { + vfree(buf); + } +#endif + return ret; +} +#endif + +#ifdef CONFIG_COMPANION_USE +int fimc_is_sec_read_companion_fw(struct fimc_is_core *core) +{ + int ret = 0; + char *buf = NULL; + loff_t pos = 0; + char fw_path[100]; + char master_setfile_path[100]; + char mode_setfile_path[100]; + char master_setf_name[50]; + char mode_setf_name[50]; + int retry = FIMC_IS_FW_RETRY_CNT; +#ifdef USE_ION_ALLOC + struct ion_handle *handle = NULL; +#endif + + pr_info("Camera: Companion FW, Setfile need to be dumped\n"); +#ifdef USE_ION_ALLOC + handle = ion_alloc(core->fimc_ion_client, (size_t)FIMC_IS_MAX_COMPANION_FW_SIZE, 0, + EXYNOS_ION_HEAP_SYSTEM_MASK, 0); + if (IS_ERR_OR_NULL(handle)) { + err("(%s)failed to ioc_alloc\n",__func__); + ret = -ENOMEM; + goto exit; + } + + buf = (char *)ion_map_kernel(core->fimc_ion_client, handle); + if (IS_ERR_OR_NULL(buf)) { + err("(%s)fail to ion_map_kernle\n",__func__); + ret = -ENOMEM; + goto exit; + } +#else + buf = vmalloc(FIMC_IS_MAX_COMPANION_FW_SIZE); + if (!buf) { + err("vmalloc fail"); + ret = -ENOMEM; + goto exit; + } +#endif + crc_retry: + + /* read companion fw data */ + pr_info("Camera: Start SPI read companion fw data\n\n"); + ret = fimc_is_spi_read_by_core(core->spi0, buf, 0x2B000, FIMC_IS_MAX_COMPANION_FW_SIZE); + if (ret < 0) { + err("failed to fimc_is_spi_read (%d)\n", ret); + ret = -EINVAL; + goto exit; + } + pr_info("Camera: End SPI read companion fw data\n\n"); + + /* CRC check */ + if (!fimc_is_sec_check_companion_fw_crc32(buf) && (retry > 0)) { + retry--; + goto crc_retry; + } else if (!retry) { + ret = -EINVAL; + goto exit; + } + + if (sysfs_finfo.concord_header_ver[9] == 0) { + snprintf(fw_path, sizeof(fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == 10) { + snprintf(fw_path, sizeof(fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, FIMC_IS_FW_COMPANION_EVT1); + } + + if (write_data_to_file(fw_path, buf, + sysfs_finfo.concord_bin_end_addr - sysfs_finfo.concord_bin_start_addr + 1, &pos) < 0) { + ret = -EIO; + goto exit; + } + + pr_info("Camera: Companion FW Data has dumped successfully\n"); + + if (sysfs_finfo.concord_header_ver[FW_SENSOR_MAKER] == FW_SENSOR_MAKER_SLSI) { + snprintf(master_setf_name, sizeof(master_setf_name), "%s", FIMC_IS_COMPANION_MASTER_SETF); + snprintf(mode_setf_name, sizeof(mode_setf_name), "%s", FIMC_IS_COMPANION_MODE_SETF); + } else { + snprintf(master_setf_name, sizeof(master_setf_name), "%s", FIMC_IS_COMPANION_MASTER_SETF); + snprintf(mode_setf_name, sizeof(mode_setf_name), "%s", FIMC_IS_COMPANION_MODE_SETF); + } + + snprintf(master_setfile_path, sizeof(master_setfile_path), "%s%s", FIMC_IS_FW_DUMP_PATH, master_setf_name); + snprintf(mode_setfile_path, sizeof(mode_setfile_path), "%s%s", FIMC_IS_FW_DUMP_PATH, mode_setf_name); + pos = 0; + + if (write_data_to_file(master_setfile_path, + buf + sysfs_finfo.concord_master_setfile_start_addr, + sysfs_finfo.concord_master_setfile_end_addr - sysfs_finfo.concord_master_setfile_start_addr + 1, &pos) < 0) { + ret = -EIO; + goto exit; + } + pos = 0; + if (write_data_to_file(mode_setfile_path, + buf + sysfs_finfo.concord_mode_setfile_start_addr, + sysfs_finfo.concord_mode_setfile_end_addr - sysfs_finfo.concord_mode_setfile_start_addr + 1, &pos) < 0) { + ret = -EIO; + goto exit; + } + + pr_info("Camera: Companion Setfile has dumped successfully\n"); + pr_info("Camera: Companion FW, Setfile were dumped successfully\n"); + +exit: +#ifdef USE_ION_ALLOC + if (!IS_ERR_OR_NULL(buf)) { + ion_unmap_kernel(core->fimc_ion_client, handle); + } + + if (!IS_ERR_OR_NULL(handle)) { + ion_free(core->fimc_ion_client, handle); + } +#else + if (buf) { + vfree(buf); + } +#endif + + return ret; +} +#endif + +#if 0 +int fimc_is_sec_gpio_enable(struct exynos_platform_fimc_is *pdata, char *name, bool on) +{ + struct gpio_set *gpio; + int ret = 0; + int i = 0; + + for (i = 0; i < FIMC_IS_MAX_GPIO_NUM; i++) { + gpio = &pdata->gpio_info->cfg[i]; + if (strcmp(gpio->name, name) == 0) + break; + else + continue; + } + + if (i == FIMC_IS_MAX_GPIO_NUM) { + pr_err("GPIO %s is not found!!\n", name); + ret = -EINVAL; + goto exit; + } + + ret = gpio_request(gpio->pin, gpio->name); + if (ret) { + pr_err("Request GPIO error(%s)\n", gpio->name); + goto exit; + } + + if (on) { + switch (gpio->act) { + case GPIO_PULL_NONE: + s3c_gpio_cfgpin(gpio->pin, gpio->value); + s3c_gpio_setpull(gpio->pin, S3C_GPIO_PULL_NONE); + break; + case GPIO_OUTPUT: + s3c_gpio_cfgpin(gpio->pin, S3C_GPIO_OUTPUT); + s3c_gpio_setpull(gpio->pin, S3C_GPIO_PULL_NONE); + gpio_set_value(gpio->pin, gpio->value); + break; + case GPIO_INPUT: + s3c_gpio_cfgpin(gpio->pin, S3C_GPIO_INPUT); + s3c_gpio_setpull(gpio->pin, S3C_GPIO_PULL_NONE); + gpio_set_value(gpio->pin, gpio->value); + break; + case GPIO_RESET: + s3c_gpio_setpull(gpio->pin, S3C_GPIO_PULL_NONE); + gpio_direction_output(gpio->pin, 0); + gpio_direction_output(gpio->pin, 1); + break; + default: + pr_err("unknown act for gpio\n"); + break; + } + } else { + s3c_gpio_cfgpin(gpio->pin, S3C_GPIO_INPUT); + s3c_gpio_setpull(gpio->pin, S3C_GPIO_PULL_DOWN); + } + + gpio_free(gpio->pin); + +exit: + return ret; +} +#endif + +int fimc_is_sec_get_pixel_size(char *header_ver) +{ + int pixelsize = 0; + + pixelsize += (int) (header_ver[FW_PIXEL_SIZE] - 0x30) * 10; + pixelsize += (int) (header_ver[FW_PIXEL_SIZE + 1] - 0x30); + + return pixelsize; +} + +int fimc_is_sec_core_voltage_select(struct device *dev, char *header_ver) +{ + struct regulator *regulator = NULL; + int ret = 0; + int minV, maxV; + int pixelSize = 0; + + regulator = regulator_get(dev, "cam_sensor_core_1.2v"); + if (IS_ERR_OR_NULL(regulator)) { + pr_err("%s : regulator_get fail\n", + __func__); + return -EINVAL; + } + pixelSize = fimc_is_sec_get_pixel_size(header_ver); + + if (header_ver[FW_SENSOR_MAKER] == FW_SENSOR_MAKER_SONY) { + if (pixelSize == 13) { + minV = 1050000; + maxV = 1050000; + } else if (pixelSize == 8) { + minV = 1100000; + maxV = 1100000; + } else { + minV = 1050000; + maxV = 1050000; + } + } else if (header_ver[FW_SENSOR_MAKER] == FW_SENSOR_MAKER_SLSI) { + minV = 1200000; + maxV = 1200000; + } else { + minV = 1050000; + maxV = 1050000; + } + + ret = regulator_set_voltage(regulator, minV, maxV); + + if (ret >= 0) + pr_info("%s : set_core_voltage %d, %d successfully\n", + __func__, minV, maxV); + regulator_put(regulator); + + return ret; +} + +/** + * fimc_is_sec_ldo_enabled: check whether the ldo has already been enabled. + * + * @ return: true, false or error value + */ +static int fimc_is_sec_ldo_enabled(struct device *dev, char *name) { + struct regulator *regulator = NULL; + int enabled = 0; + + regulator = regulator_get(dev, name); + if (IS_ERR_OR_NULL(regulator)) { + pr_err("%s : regulator_get(%s) fail\n", __func__, name); + return -EINVAL; + } + + enabled = regulator_is_enabled(regulator); + + regulator_put(regulator); + + return enabled; +} + +int fimc_is_sec_ldo_enable(struct device *dev, char *name, bool on) +{ + struct regulator *regulator = NULL; + int ret = 0; + + regulator = regulator_get(dev, name); + if (IS_ERR_OR_NULL(regulator)) { + pr_err("%s : regulator_get(%s) fail\n", __func__, name); + return -EINVAL; + } + + if (on) { + if (regulator_is_enabled(regulator)) { + pr_warning("%s: regulator is already enabled\n", name); + goto exit; + } + + ret = regulator_enable(regulator); + if (ret) { + pr_err("%s : regulator_enable(%s) fail\n", __func__, name); + goto exit; + } + } else { + if (!regulator_is_enabled(regulator)) { + pr_warning("%s: regulator is already disabled\n", name); + goto exit; + } + + ret = regulator_disable(regulator); + if (ret) { + pr_err("%s : regulator_disable(%s) fail\n", __func__, name); + goto exit; + } + } + +exit: + if (regulator) + regulator_put(regulator); + + return ret; +} + +int fimc_is_sec_fw_find(struct fimc_is_core *core, char *fw_name, char *setf_name) +{ + int pixelSize = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_device_sensor *sensor_device = &core->sensor[0]; + + BUG_ON(!sensor_device); + BUG_ON(!sensor_device->pdata); + BUG_ON(!sensor_device->pdata->sensor_id); + + pdata = sensor_device->pdata; + + if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_2P2_F) || + fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_2P2_I)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2), "%s", FIMC_IS_FW_2P2); + snprintf(setf_name, sizeof(FIMC_IS_2P2_SETF), "%s", FIMC_IS_2P2_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_2P2_12M)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2_12M), "%s", FIMC_IS_FW_2P2_12M); + snprintf(setf_name, sizeof(FIMC_IS_2P2_12M_SETF), "%s", FIMC_IS_2P2_12M_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_2P3)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P3), "%s", FIMC_IS_FW_2P3); + snprintf(setf_name, sizeof(FIMC_IS_2P3_SETF), "%s", FIMC_IS_2P3_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_4H5)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_4H5), "%s", FIMC_IS_FW_4H5); + snprintf(setf_name, sizeof(FIMC_IS_4H5_SETF), "%s", FIMC_IS_4H5_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_IMX240) || + fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, FW_IMX240_Q)) { + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX240), "%s", FIMC_IS_FW_IMX240); + snprintf(setf_name, sizeof(FIMC_IS_IMX240_SETF), "%s", FIMC_IS_IMX240_SETF); + } else { + /* Use the PixelSize information */ + pixelSize = fimc_is_sec_get_pixel_size(sysfs_finfo.header_ver); + if (pixelSize == 16) { + if (pdata->sensor_id == SENSOR_NAME_S5K2P3) { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P3), "%s", FIMC_IS_FW_2P3); + snprintf(setf_name, sizeof(FIMC_IS_2P3_SETF), "%s", FIMC_IS_2P3_SETF); + } else { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2), "%s", FIMC_IS_FW_2P2); + snprintf(setf_name, sizeof(FIMC_IS_2P2_SETF), "%s", FIMC_IS_2P2_SETF); + } + } else if (pixelSize == 13) { + snprintf(fw_name, sizeof(FIMC_IS_FW), "%s", FIMC_IS_FW); + snprintf(setf_name, sizeof(FIMC_IS_IMX135_SETF), "%s", FIMC_IS_IMX135_SETF); + } else if (pixelSize == 12) { + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2_12M), "%s", FIMC_IS_FW_2P2_12M); + snprintf(setf_name, sizeof(FIMC_IS_2P2_12M_SETF), "%s", FIMC_IS_2P2_12M_SETF); + } else if (pixelSize == 8) { + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX134), "%s", FIMC_IS_FW_IMX134); + snprintf(setf_name, sizeof(FIMC_IS_IMX134_SETF), "%s", FIMC_IS_IMX134_SETF); + } else { + /* default firmware and setfile */ + if ( pdata->sensor_id == SENSOR_NAME_IMX240 ) { + /* IMX240 */ + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX240), "%s", FIMC_IS_FW_IMX240); + snprintf(setf_name, sizeof(FIMC_IS_IMX240_SETF), "%s", FIMC_IS_IMX240_SETF); + } else if ( pdata->sensor_id == SENSOR_NAME_IMX134 ) { + /* IMX134 */ + snprintf(fw_name, sizeof(FIMC_IS_FW_IMX134), "%s", FIMC_IS_FW_IMX134); + snprintf(setf_name, sizeof(FIMC_IS_IMX134_SETF), "%s", FIMC_IS_IMX134_SETF); + } else if ( pdata->sensor_id == SENSOR_NAME_S5K2P2_12M ) { + /* 2P2_12M */ + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2_12M), "%s", FIMC_IS_FW_2P2_12M); + snprintf(setf_name, sizeof(FIMC_IS_2P2_12M_SETF), "%s", FIMC_IS_2P2_12M_SETF); + } else if ( pdata->sensor_id == SENSOR_NAME_S5K2P2 ) { + /* 2P2 */ + snprintf(fw_name, sizeof(FIMC_IS_FW_2P2), "%s", FIMC_IS_FW_2P2); + snprintf(setf_name, sizeof(FIMC_IS_2P2_SETF), "%s", FIMC_IS_2P2_SETF); + } else if ( pdata->sensor_id == SENSOR_NAME_S5K2P3 ) { + /* 2P3 */ + snprintf(fw_name, sizeof(FIMC_IS_FW_2P3), "%s", FIMC_IS_FW_2P3); + snprintf(setf_name, sizeof(FIMC_IS_2P3_SETF), "%s", FIMC_IS_2P3_SETF); + } else if ( pdata->sensor_id == SENSOR_NAME_S5K4H5 ) { + /* 4H5 */ + snprintf(fw_name, sizeof(FIMC_IS_FW_4H5), "%s", FIMC_IS_FW_4H5); + snprintf(setf_name, sizeof(FIMC_IS_4H5_SETF), "%s", FIMC_IS_4H5_SETF); + } else { + snprintf(fw_name, sizeof(FIMC_IS_FW), "%s", FIMC_IS_FW); + snprintf(setf_name, sizeof(FIMC_IS_IMX135_SETF), "%s", FIMC_IS_IMX135_SETF); + } + } + } + + strcpy(sysfs_finfo.load_fw_name, fw_name); + strcpy(sysfs_finfo.load_setfile_name, setf_name); + + return 0; +} + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +int fimc_is_sec_fw_sel_eeprom(struct device *dev, + char *fw_name, char *setf_name, int id, bool headerOnly) +{ + int ret = 0; + char fw_path[100]; + char phone_fw_version[12] = {0, }; + + struct file *fp = NULL; + mm_segment_t old_fs; + long fsize, nread; + u8 *fw_buf = NULL; + bool is_ldo_enabled = false; + struct platform_device *pdev = to_platform_device(dev); + struct fimc_is_core *core = (struct fimc_is_core *)platform_get_drvdata(pdev); + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_device_sensor *sensor_device = &core->sensor[0]; + + BUG_ON(!sensor_device); + BUG_ON(!sensor_device->pdata); + BUG_ON(!sensor_device->pdata->sensor_id); + + pdata = sensor_device->pdata; + + /* Use mutex for i2c read */ + mutex_lock(&core->spi_lock); + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + if (!sysfs_finfo_front.is_caldata_read || force_caldata_dump) { + if (force_caldata_dump) + pr_info("forced caldata dump!!\n"); + + if (!fimc_is_sec_ldo_enabled(dev, "VT_CAM_1.8V")) { + ret = fimc_is_sec_ldo_enable(dev, "VT_CAM_1.8V", true); + if (ret) { + pr_err("fimc_is_sec_fw_sel_eeprom: error, failed to cam_io(on)"); + goto exit; + } + + is_ldo_enabled = true; + } + + pr_info("Camera: read cal data from Front EEPROM\n"); + if ((fimc_is_sec_readcal_eeprom(dev, SENSOR_POSITION_FRONT) != -EIO) && + crc32_header_check_front) { + sysfs_finfo_front.is_caldata_read = true; + } + } + goto exit; + } else +#endif + { + if (!sysfs_finfo.is_caldata_read || force_caldata_dump) { + is_dumped_fw_loading_needed = false; + if (force_caldata_dump) + pr_info("forced caldata dump!!\n"); + + if (!fimc_is_sec_ldo_enabled(dev, "CAM_IO_1.8V_AP")) { + ret = fimc_is_sec_ldo_enable(dev, "CAM_IO_1.8V_AP", true); + if (ret) { + pr_err("fimc_is_sec_fw_sel_eeprom: error, failed to cam_io(on)"); + goto exit; + } + + is_ldo_enabled = true; + } + + pr_info("Camera: read cal data from Rear EEPROM\n"); + if (headerOnly) { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) + fimc_is_sec_read_eeprom_header(dev); +#endif + } else { + if ((fimc_is_sec_readcal_eeprom(dev, SENSOR_POSITION_REAR) != -EIO) && + crc32_header_check) { + sysfs_finfo.is_caldata_read = true; + } + } + } + } + + fimc_is_sec_fw_find(core, fw_name, setf_name); + if (headerOnly) { + goto exit; + } + + snprintf(fw_path, sizeof(fw_path), "%s%s", FIMC_IS_FW_PATH, fw_name); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("Camera: Failed open phone firmware\n"); + ret = -EIO; + fp = NULL; + goto read_phone_fw_exit; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + fw_path, fsize); + fw_buf = vmalloc(fsize); + if (!fw_buf) { + pr_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto read_phone_fw_exit; + } + nread = vfs_read(fp, (char __user *)fw_buf, fsize, &fp->f_pos); + if (nread != fsize) { + pr_err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto read_phone_fw_exit; + } + + strncpy(phone_fw_version, fw_buf + nread - 11, 11); + strncpy(sysfs_pinfo.header_ver, fw_buf + nread - 11, 11); + pr_info("Camera: phone fw version: %s\n", phone_fw_version); +read_phone_fw_exit: + if (fw_buf) { + vfree(fw_buf); + fw_buf = NULL; + } + + if (fp) { + filp_close(fp, current->files); + fp = NULL; + } + + set_fs(old_fs); + +exit: + if (is_ldo_enabled) { +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) + if (id == SENSOR_POSITION_FRONT) { + ret = fimc_is_sec_ldo_enable(dev, "VT_CAM_1.8V", false); + } else +#endif + { + ret = fimc_is_sec_ldo_enable(dev, "CAM_IO_1.8V_AP", false); + } + if (ret) + pr_err("fimc_is_sec_fw_sel_eeprom: error, failed to cam_io(off)"); + } + + mutex_unlock(&core->spi_lock); + + return ret; +} +#endif + +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +int fimc_is_sec_fw_sel(struct fimc_is_core *core, struct device *dev, + char *fw_name, char *setf_name, bool headerOnly) +{ + int ret = 0; +#if 1 + char fw_path[100]; + char dump_fw_path[100]; + char dump_fw_version[12] = {0, }; + char phone_fw_version[12] = {0, }; + int from_fw_revision = 0; + int dump_fw_revision = 0; + int phone_fw_revision = 0; + + struct file *fp = NULL; + mm_segment_t old_fs; + long fsize, nread; + u8 *fw_buf = NULL; + bool is_dump_existed = false; + bool is_dump_needed = true; +#endif +#ifdef CONFIG_COMPANION_USE + struct fimc_is_spi_gpio *spi_gpio = &core->spi_gpio; +#endif + bool is_ldo_enabled = false; + struct exynos_platform_fimc_is_sensor *pdata; + struct fimc_is_device_sensor *sensor_device = &core->sensor[0]; + + BUG_ON(!sensor_device); + BUG_ON(!sensor_device->pdata); + BUG_ON(!sensor_device->pdata->sensor_id); + + pdata = sensor_device->pdata; + + /* Use mutex for spi read */ + mutex_lock(&core->spi_lock); + if (!sysfs_finfo.is_caldata_read || force_caldata_dump) { + is_dumped_fw_loading_needed = false; + if (force_caldata_dump) + pr_info("forced caldata dump!!\n"); + if (!fimc_is_sec_ldo_enabled(dev, "CAM_IO_1.8V_AP")) { + pr_info("enable %s in the %s\n", "CAM_IO_1.8V_AP", __func__); + ret = fimc_is_sec_ldo_enable(dev, "CAM_IO_1.8V_AP", true); + if (ret) { + pr_err("fimc_is_sec_fw_sel: error, failed to cam_io(on)"); + goto exit; + } + + is_ldo_enabled = true; + } + pr_info("read cal data from FROM\n"); +#ifdef CONFIG_COMPANION_USE + fimc_is_set_spi_config(spi_gpio, FIMC_IS_SPI_FUNC, false); +#endif + + if (headerOnly) { + fimc_is_sec_read_from_header(dev); + } else { + if ((fimc_is_sec_readcal(core) != -EIO) && + crc32_header_check) { + sysfs_finfo.is_caldata_read = true; + } + } + +#ifdef CONFIG_COMPANION_USE + fimc_is_set_spi_config(spi_gpio, FIMC_IS_SPI_OUTPUT, false); +#endif + /*select AF actuator*/ + if (!crc32_header_check) { + pr_info("Camera : CRC32 error for all section.\n"); + //ret = -EIO; + //goto exit; + } + + /*ret = fimc_is_sec_core_voltage_select(dev, sysfs_finfo.header_ver); + if (ret < 0) { + err("failed to fimc_is_sec_core_voltage_select (%d)\n", ret); + ret = -EINVAL; + goto exit; + }*/ + + fimc_is_sec_fw_find(core, fw_name, setf_name); + if (headerOnly) { + goto exit; + } + + snprintf(fw_path, sizeof(fw_path), "%s%s", FIMC_IS_FW_PATH, fw_name); + +#if 1 + snprintf(dump_fw_path, sizeof(dump_fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, fw_name); + pr_info("Camera: f-rom fw version: %s\n", sysfs_finfo.header_ver); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(dump_fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_info("Camera: There is no dumped firmware\n"); + is_dump_existed = false; + goto read_phone_fw; + } else { + is_dump_existed = true; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + dump_fw_path, fsize); + fw_buf = vmalloc(fsize); + if (!fw_buf) { + pr_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto read_phone_fw; + } + nread = vfs_read(fp, (char __user *)fw_buf, fsize, &fp->f_pos); + if (nread != fsize) { + pr_err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto read_phone_fw; + } + + strncpy(dump_fw_version, fw_buf+nread-11, 11); + pr_info("Camera: dumped fw version: %s\n", dump_fw_version); + +read_phone_fw: + if (fw_buf) { + vfree(fw_buf); + fw_buf = NULL; + } + + if (fp && is_dump_existed) { + filp_close(fp, current->files); + fp = NULL; + } + + set_fs(old_fs); + + if (ret < 0) + goto exit; +#endif + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("Camera: Failed open phone firmware\n"); + ret = -EIO; + fp = NULL; + goto read_phone_fw_exit; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + fw_path, fsize); + fw_buf = vmalloc(fsize); + if (!fw_buf) { + pr_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto read_phone_fw_exit; + } + nread = vfs_read(fp, (char __user *)fw_buf, fsize, &fp->f_pos); + if (nread != fsize) { + pr_err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto read_phone_fw_exit; + } + + strncpy(phone_fw_version, fw_buf + nread - 11, 11); + strncpy(sysfs_pinfo.header_ver, fw_buf + nread - 11, 11); + pr_info("Camera: phone fw version: %s\n", phone_fw_version); +read_phone_fw_exit: + if (fw_buf) { + vfree(fw_buf); + fw_buf = NULL; + } + + if (fp) { + filp_close(fp, current->files); + fp = NULL; + } + + set_fs(old_fs); + + if (ret < 0) + goto exit; + + from_fw_revision = fimc_is_sec_fw_revision(sysfs_finfo.header_ver); + phone_fw_revision = fimc_is_sec_fw_revision(phone_fw_version); + if (is_dump_existed) + dump_fw_revision = fimc_is_sec_fw_revision(dump_fw_version); + + if ((!fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, phone_fw_version)) || + (from_fw_revision > phone_fw_revision)) { + is_dumped_fw_loading_needed = true; + if (is_dump_existed) { + if (!fimc_is_sec_fw_module_compare(sysfs_finfo.header_ver, + dump_fw_version)) { + is_dump_needed = true; + } else if (from_fw_revision > dump_fw_revision) { + is_dump_needed = true; + } else { + is_dump_needed = false; + } + } else { + is_dump_needed = true; + } + } else { + is_dump_needed = false; + if (is_dump_existed) { + if (!fimc_is_sec_fw_module_compare(phone_fw_version, + dump_fw_version)) { + is_dumped_fw_loading_needed = false; + } else if (phone_fw_revision > dump_fw_revision) { + is_dumped_fw_loading_needed = false; + } else { + is_dumped_fw_loading_needed = true; + } + } else { + is_dumped_fw_loading_needed = false; + } + } +#if 0 + if (sysfs_finfo.header_ver[0] == 'O') { + /* hack: gumi module using phone fw */ + is_dumped_fw_loading_needed = false; + is_dump_needed = false; + } else if (sysfs_finfo.header_ver[FW_ISP_COMPANY] != FW_ISP_COMPANY_LSI) { + ret = -EINVAL; + goto exit; + } + + if (is_dump_needed) { + ret = fimc_is_sec_readfw(core); + if (ret < 0) { + if (!crc32_fw_check) { + is_dumped_fw_loading_needed = false; + ret = 0; + } else + goto exit; + } + } +#endif + if (is_dump_needed && is_dumped_fw_loading_needed) { + strncpy(loaded_fw, sysfs_finfo.header_ver, 11); + } else if (!is_dump_needed && is_dumped_fw_loading_needed) { + strncpy(loaded_fw, dump_fw_version, 11); + } else + strncpy(loaded_fw, phone_fw_version, 11); + + } else { + pr_info("Did not read cal data from FROM, FW version = %s\n", sysfs_finfo.header_ver); + strcpy(fw_name, sysfs_finfo.load_fw_name); + strcpy(setf_name, sysfs_finfo.load_setfile_name); + } + +exit: + if (is_ldo_enabled && !core->running_rear_camera) { + pr_info("disable %s in the %s\n", "CAM_IO_1.8V_AP", __func__); + ret = fimc_is_sec_ldo_enable(dev, "CAM_IO_1.8V_AP", false); + if (ret) + pr_err("fimc_is_sec_fw_sel: error, failed to cam_io(off)"); + } + + mutex_unlock(&core->spi_lock); + + if (core->use_module_check) { + if (sysfs_finfo.header_ver[3] != 'L') { + pr_err("Not supported module. Module ver = %s", sysfs_finfo.header_ver); + return -EIO; + } + } + + return ret; +} +#endif + +#ifdef CONFIG_COMPANION_USE +void fimc_is_set_spi_config(struct fimc_is_spi_gpio *spi_gpio, int func, bool ssn) { + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_sclk, + PINCFG_PACK(PINCFG_TYPE_FUNC, func)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_miso, + PINCFG_PACK(PINCFG_TYPE_FUNC, func)); + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_mois, + PINCFG_PACK(PINCFG_TYPE_FUNC, func)); + if(ssn == true) { + pin_config_set(FIMC_IS_SPI_PINNAME, spi_gpio->spi_ssn, + PINCFG_PACK(PINCFG_TYPE_FUNC, func)); + } +} + +int fimc_is_sec_concord_fw_sel(struct fimc_is_core *core, struct device *dev, + char *fw_name, char *master_setf_name, char *mode_setf_name) +{ + int ret = 0; + char c1_fw_path[100]; + char dump_c1_fw_path[100]; + char dump_c1_fw_version[12] = {0, }; + char phone_c1_fw_version[12] = {0, }; + int from_c1_fw_revision = 0; + int dump_c1_fw_revision = 0; + int phone_c1_fw_revision = 0; + + struct file *fp = NULL; + mm_segment_t old_fs; + long fsize, nread; + u8 *c1_fw_buf = NULL; + bool is_dump_existed = false; + bool is_dump_needed = true; + int pixelSize = 0; + + if ((!sysfs_finfo.is_c1_caldata_read && + (cam_id == CAMERA_SINGLE_REAR /* || cam_id == CAMERA_DUAL_FRONT*/)) || + force_caldata_dump) { + is_dumped_c1_fw_loading_needed = false; + if (force_caldata_dump) + pr_info("forced caldata dump!!\n"); + + pr_info("Load companion fw from FROM\n"); + sysfs_finfo.is_c1_caldata_read = true; + + /*ret = fimc_is_sec_core_voltage_select(dev, sysfs_finfo.header_ver); + if (ret < 0) { + err("failed to fimc_is_sec_core_voltage_select (%d)\n", ret); + ret = -EINVAL; + goto exit; + }*/ + + if (fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_2P2_F) || + fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_2P2_I)) { + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_EVT0); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_EVT0), "%s", FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_2P2_EVT1); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_2P2_EVT1), "%s", FIMC_IS_FW_COMPANION_2P2_EVT1); + } else { + pr_info("Camera : Wrong companion module version.\n"); + } + sysfs_finfo.sensor_id = COMPANION_SENSOR_2P2; + snprintf(master_setf_name, sizeof(FIMC_IS_COMPANION_2P2_MASTER_SETF), "%s", FIMC_IS_COMPANION_2P2_MASTER_SETF); + snprintf(mode_setf_name, sizeof(FIMC_IS_COMPANION_2P2_MODE_SETF), "%s", FIMC_IS_COMPANION_2P2_MODE_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_IMX240) || + fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_IMX240_Q_C1) || + fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_IMX240_Q)) { + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_EVT0); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_EVT0), "%s", FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_IMX240_EVT1); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_IMX240_EVT1), "%s", FIMC_IS_FW_COMPANION_IMX240_EVT1); + } else { + pr_info("Camera : Wrong companion module version.\n"); + } + sysfs_finfo.sensor_id = COMPANION_SENSOR_IMX240; + snprintf(master_setf_name, sizeof(FIMC_IS_COMPANION_IMX240_MASTER_SETF), "%s", FIMC_IS_COMPANION_IMX240_MASTER_SETF); + snprintf(mode_setf_name, sizeof(FIMC_IS_COMPANION_IMX240_MODE_SETF), "%s", FIMC_IS_COMPANION_IMX240_MODE_SETF); + } else if (fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, FW_2P2_12M)) { + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_EVT0); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_EVT0), "%s", FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_2P2_12M_EVT1); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_2P2_12M_EVT1), "%s", FIMC_IS_FW_COMPANION_2P2_12M_EVT1); + } else { + pr_info("Camera : Wrong companion module version.\n"); + } + sysfs_finfo.sensor_id = COMPANION_SENSOR_2P2; + snprintf(master_setf_name, sizeof(FIMC_IS_COMPANION_2P2_12M_MASTER_SETF), "%s", FIMC_IS_COMPANION_2P2_12M_MASTER_SETF); + snprintf(mode_setf_name, sizeof(FIMC_IS_COMPANION_2P2_12M_MODE_SETF), "%s", FIMC_IS_COMPANION_2P2_12M_MODE_SETF); + } else { + pixelSize = fimc_is_sec_get_pixel_size(sysfs_finfo.concord_header_ver); + if (pixelSize == 16) { + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_EVT0); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_EVT0), "%s", FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_2P2_EVT1); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_2P2_EVT1), "%s", FIMC_IS_FW_COMPANION_2P2_EVT1); + } else { + pr_info("Camera : Wrong companion module version.\n"); + } + sysfs_finfo.sensor_id = COMPANION_SENSOR_2P2; + snprintf(master_setf_name, sizeof(FIMC_IS_COMPANION_2P2_MASTER_SETF), "%s", FIMC_IS_COMPANION_2P2_MASTER_SETF); + snprintf(mode_setf_name, sizeof(FIMC_IS_COMPANION_2P2_MODE_SETF), "%s", FIMC_IS_COMPANION_2P2_MODE_SETF); + } else if (pixelSize == 12) { + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_EVT0); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_EVT0), "%s", FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(c1_fw_path, sizeof(c1_fw_path), "%s%s", + FIMC_IS_FW_PATH, FIMC_IS_FW_COMPANION_2P2_12M_EVT1); + snprintf(fw_name, sizeof(FIMC_IS_FW_COMPANION_2P2_12M_EVT1), "%s", FIMC_IS_FW_COMPANION_2P2_12M_EVT1); + } else { + pr_info("Camera : Wrong companion module version.\n"); + } + sysfs_finfo.sensor_id = COMPANION_SENSOR_2P2; + snprintf(master_setf_name, sizeof(FIMC_IS_COMPANION_2P2_12M_MASTER_SETF), "%s", FIMC_IS_COMPANION_2P2_12M_MASTER_SETF); + snprintf(mode_setf_name, sizeof(FIMC_IS_COMPANION_2P2_12M_MODE_SETF), "%s", FIMC_IS_COMPANION_2P2_12M_MODE_SETF); + } + } + + strcpy(sysfs_finfo.load_c1_fw_name, fw_name); + strcpy(sysfs_finfo.load_c1_mastersetf_name, master_setf_name); + strcpy(sysfs_finfo.load_c1_modesetf_name, mode_setf_name); + + if (sysfs_finfo.concord_header_ver[9] == '0') { + snprintf(dump_c1_fw_path, sizeof(dump_c1_fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, FIMC_IS_FW_COMPANION_EVT0); + } else if (sysfs_finfo.concord_header_ver[9] == '1') { + snprintf(dump_c1_fw_path, sizeof(dump_c1_fw_path), "%s%s", + FIMC_IS_FW_DUMP_PATH, sysfs_finfo.load_c1_fw_name); + } + pr_info("Camera: f-rom fw version: %s\n", sysfs_finfo.concord_header_ver); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = 0; + fp = filp_open(dump_c1_fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_info("Camera: There is no dumped Companion firmware\n"); + is_dump_existed = false; + goto read_phone_fw; + } else { + is_dump_existed = true; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + dump_c1_fw_path, fsize); + c1_fw_buf = vmalloc(fsize); + if (!c1_fw_buf) { + pr_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto read_phone_fw; + } + nread = vfs_read(fp, (char __user *)c1_fw_buf, fsize, &fp->f_pos); + if (nread != fsize) { + pr_err("failed to read firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto read_phone_fw; + } + + strncpy(dump_c1_fw_version, c1_fw_buf+nread - 16, 11); + pr_info("Camera: dumped companion fw version: %s\n", dump_c1_fw_version); +read_phone_fw: + if (c1_fw_buf) { + vfree(c1_fw_buf); + c1_fw_buf = NULL; + } + + if (fp && is_dump_existed) { + filp_close(fp, current->files); + fp = NULL; + } + + set_fs(old_fs); + if (ret < 0) + goto exit; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(c1_fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("Camera: Failed open phone companion firmware\n"); + ret = -EIO; + fp = NULL; + goto read_phone_fw_exit; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + pr_info("start, file path %s, size %ld Bytes\n", + c1_fw_path, fsize); + c1_fw_buf = vmalloc(fsize); + if (!c1_fw_buf) { + pr_err("failed to allocate memory\n"); + ret = -ENOMEM; + goto read_phone_fw_exit; + } + nread = vfs_read(fp, (char __user *)c1_fw_buf, fsize, &fp->f_pos); + if (nread != fsize) { + pr_err("failed to read companion firmware file, %ld Bytes\n", nread); + ret = -EIO; + goto read_phone_fw_exit; + } + + strncpy(phone_c1_fw_version, c1_fw_buf + nread - 16, 11); + strncpy(sysfs_pinfo.concord_header_ver, c1_fw_buf + nread - 16, 11); + pr_info("Camera: phone companion fw version: %s\n", phone_c1_fw_version); +read_phone_fw_exit: + if (c1_fw_buf) { + vfree(c1_fw_buf); + c1_fw_buf = NULL; + } + + if (fp) { + filp_close(fp, current->files); + fp = NULL; + } + + set_fs(old_fs); + + if (ret < 0) + goto exit; + + from_c1_fw_revision = fimc_is_sec_fw_revision(sysfs_finfo.concord_header_ver); + phone_c1_fw_revision = fimc_is_sec_fw_revision(phone_c1_fw_version); + if (is_dump_existed) + dump_c1_fw_revision = fimc_is_sec_fw_revision(dump_c1_fw_version); + + if ((!fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, phone_c1_fw_version)) || + (from_c1_fw_revision > phone_c1_fw_revision)) { + is_dumped_c1_fw_loading_needed = true; + if (is_dump_existed) { + if (!fimc_is_sec_fw_module_compare(sysfs_finfo.concord_header_ver, + dump_c1_fw_version)) { + is_dump_needed = true; + } else if (from_c1_fw_revision > dump_c1_fw_revision) { + is_dump_needed = true; + } else { + is_dump_needed = false; + } + } else { + is_dump_needed = true; + } + } else { + is_dump_needed = false; + if (is_dump_existed) { + if (!fimc_is_sec_fw_module_compare(phone_c1_fw_version, + dump_c1_fw_version)) { + is_dumped_c1_fw_loading_needed = false; + } else if (phone_c1_fw_revision > dump_c1_fw_revision) { + is_dumped_c1_fw_loading_needed = false; + } else { + is_dumped_c1_fw_loading_needed = true; + } + } else { + is_dumped_c1_fw_loading_needed = false; + } + } +#if 0 + if (is_dump_needed) { + ret = fimc_is_sec_read_companion_fw(core); + if (ret < 0) { + if (!crc32_c1_fw_check) { + is_dumped_c1_fw_loading_needed = false; + ret = 0; + } else + goto exit; + } + } +#endif + if (is_dump_needed && is_dumped_c1_fw_loading_needed) { + strncpy(loaded_companion_fw, sysfs_finfo.concord_header_ver, 11); + } else if (!is_dump_needed && is_dumped_c1_fw_loading_needed) { + strncpy(loaded_companion_fw, dump_c1_fw_version, 11); + } else + strncpy(loaded_companion_fw, phone_c1_fw_version, 11); + } else { + pr_info("Did not Load companion fw from FROM, Companion version = %s\n", sysfs_finfo.concord_header_ver); + strcpy(fw_name, sysfs_finfo.load_c1_fw_name); + strcpy(master_setf_name, sysfs_finfo.load_c1_mastersetf_name); + strcpy(mode_setf_name, sysfs_finfo.load_c1_modesetf_name); + } + +exit: + return ret; +} +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.h b/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.h new file mode 100644 index 000000000000..a9a170c4c1de --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-sec-define.h @@ -0,0 +1,226 @@ +#ifndef FIMC_IS_SEC_DEFINE_H +#define FIMC_IS_SEC_DEFINE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +#include "fimc-is-device-sensor.h" +#include "fimc-is-device-ischain.h" +#include "crc32.h" +#include "fimc-is-companion.h" + +#define FW_CORE_VER 0 +#define FW_PIXEL_SIZE 1 +#define FW_ISP_COMPANY 3 +#define FW_SENSOR_MAKER 4 +#define FW_PUB_YEAR 5 +#define FW_PUB_MON 6 +#define FW_PUB_NUM 7 +#define FW_MODULE_COMPANY 9 +#define FW_VERSION_INFO 10 + +#define FW_ISP_COMPANY_BROADCOMM 'B' +#define FW_ISP_COMPANY_TN 'C' +#define FW_ISP_COMPANY_FUJITSU 'F' +#define FW_ISP_COMPANY_INTEL 'I' +#define FW_ISP_COMPANY_LSI 'L' +#define FW_ISP_COMPANY_MARVELL 'M' +#define FW_ISP_COMPANY_QUALCOMM 'Q' +#define FW_ISP_COMPANY_RENESAS 'R' +#define FW_ISP_COMPANY_STE 'S' +#define FW_ISP_COMPANY_TI 'T' +#define FW_ISP_COMPANY_DMC 'D' + +#define FW_SENSOR_MAKER_SF 'F' +#define FW_SENSOR_MAKER_SLSI 'L' +#define FW_SENSOR_MAKER_SONY 'S' + +#define FW_MODULE_COMPANY_SEMCO 'S' +#define FW_MODULE_COMPANY_GUMI 'O' +#define FW_MODULE_COMPANY_CAMSYS 'C' +#define FW_MODULE_COMPANY_PATRON 'P' +#define FW_MODULE_COMPANY_MCNEX 'M' +#define FW_MODULE_COMPANY_LITEON 'L' +#define FW_MODULE_COMPANY_VIETNAM 'V' +#define FW_MODULE_COMPANY_SHARP 'J' +#define FW_MODULE_COMPANY_NAMUGA 'N' +#define FW_MODULE_COMPANY_POWER_LOGIX 'A' +#define FW_MODULE_COMPANY_DI 'D' + +#define FW_2P2_F "F16LL" +#define FW_2P2_I "I16LL" +#define FW_3L2 "C13LL" +#define FW_IMX135 "C13LS" +#define FW_IMX134 "D08LS" +#define FW_IMX240 "H16LS" +#define FW_IMX240_Q "H16US" +#define FW_IMX240_Q_C1 "H16UL" +#define FW_2P2_12M "G16LL" +#define FW_4H5 "F08LL" +#define FW_2P3 "J16LL" + +#define SDCARD_FW +#define FIMC_IS_SETFILE_SDCARD_PATH "/data/media/0/" +#define FIMC_IS_FW "fimc_is_fw2.bin" +#define FIMC_IS_FW_2P2 "fimc_is_fw2_2p2.bin" +#define FIMC_IS_FW_2P2_12M "fimc_is_fw2_2p2_12m.bin" +#define FIMC_IS_FW_2P3 "fimc_is_fw2_2p3.bin" +#define FIMC_IS_FW_3L2 "fimc_is_fw2_3l2.bin" +#define FIMC_IS_FW_4H5 "fimc_is_fw2_4h5.bin" +#define FIMC_IS_FW_IMX134 "fimc_is_fw2_imx134.bin" +#define FIMC_IS_FW_IMX240 "fimc_is_fw2_imx240.bin" +#define FIMC_IS_FW_COMPANION_EVT0 "companion_fw_evt0.bin" +#define FIMC_IS_FW_COMPANION_EVT1 "companion_fw_evt1.bin" +#define FIMC_IS_FW_COMPANION_2P2_EVT1 "companion_fw_2p2_evt1.bin" +#define FIMC_IS_FW_COMPANION_2P2_12M_EVT1 "companion_fw_2p2_12m_evt1.bin" +#define FIMC_IS_FW_COMPANION_IMX240_EVT1 "companion_fw_imx240_evt1.bin" +#define FIMC_IS_FW_SDCARD "/data/media/0/fimc_is_fw2.bin" +#define FIMC_IS_IMX240_SETF "setfile_imx240.bin" +#define FIMC_IS_IMX135_SETF "setfile_imx135.bin" +#define FIMC_IS_IMX134_SETF "setfile_imx134.bin" +#define FIMC_IS_4H5_SETF "setfile_4h5.bin" +#define FIMC_IS_3L2_SETF "setfile_3l2.bin" +#define FIMC_IS_6B2_SETF "setfile_6b2.bin" +#define FIMC_IS_8B1_SETF "setfile_8b1.bin" +#define FIMC_IS_6D1_SETF "setfile_6d1.bin" +#define FIMC_IS_2P2_SETF "setfile_2p2.bin" +#define FIMC_IS_2P2_12M_SETF "setfile_2p2_12m.bin" +#define FIMC_IS_2P3_SETF "setfile_2p3.bin" +#define FIMC_IS_COMPANION_MASTER_SETF "companion_master_setfile.bin" +#define FIMC_IS_COMPANION_MODE_SETF "companion_mode_setfile.bin" +#define FIMC_IS_COMPANION_2P2_MASTER_SETF "companion_2p2_master_setfile.bin" +#define FIMC_IS_COMPANION_2P2_MODE_SETF "companion_2p2_mode_setfile.bin" +#define FIMC_IS_COMPANION_IMX240_MASTER_SETF "companion_imx240_master_setfile.bin" +#define FIMC_IS_COMPANION_IMX240_MODE_SETF "companion_imx240_mode_setfile.bin" +#define FIMC_IS_COMPANION_2P2_12M_MASTER_SETF "companion_2p2_12m_master_setfile.bin" +#define FIMC_IS_COMPANION_2P2_12M_MODE_SETF "companion_2p2_12m_mode_setfile.bin" +#define FIMC_IS_FW_PATH "/system/vendor/firmware/" +#define FIMC_IS_FW_DUMP_PATH "/data/" + +#define FIMC_IS_FW_BASE_MASK ((1 << 26) - 1) +#define FIMC_IS_VERSION_SIZE 42 +#define FIMC_IS_SETFILE_VER_OFFSET 0x40 +#define FIMC_IS_SETFILE_VER_SIZE 52 + +#define FIMC_IS_CAL_SDCARD "/data/cal_data.bin" +#define FIMC_IS_CAL_SDCARD_FRONT "/data/cal_data_front.bin" + +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +#define FIMC_IS_MAX_FW_SIZE (8 * 1024) +#define HEADER_CRC32_LEN (80 / 2) +#define OEM_CRC32_LEN (64 / 2) +#define AWB_CRC32_LEN (32 / 2) +#define SHADING_CRC32_LEN (6623 / 2) +#else +/*#define FIMC_IS_MAX_CAL_SIZE (20 * 1024)*/ +#define FIMC_IS_MAX_FW_SIZE (2048 * 1024) +#define HEADER_CRC32_LEN (224 / 2) +#define OEM_CRC32_LEN (192 / 2) +#define AWB_CRC32_LEN (32 / 2) +#define SHADING_CRC32_LEN (2336 / 2) +#endif + +#define FIMC_IS_MAX_COMPANION_FW_SIZE (120 * 1024) +#define FIMC_IS_CAL_START_ADDR (0x013D0000) +#define FIMC_IS_CAL_START_ADDR_FRONT (0x013E0000) + +#define FIMC_IS_CAL_RETRY_CNT (2) +#define FIMC_IS_FW_RETRY_CNT (2) +#define FROM_VERSION_V004 '4' +#define FROM_VERSION_V005 '5' + +enum { + CC_BIN1 = 0, + CC_BIN2, + CC_BIN3, + CC_BIN4, + CC_BIN5, + CC_BIN6, + CC_BIN_MAX, +}; + +int fimc_is_sec_set_force_caldata_dump(bool fcd); + +ssize_t write_data_to_file(char *name, char *buf, size_t count, loff_t *pos); +ssize_t read_data_from_file(char *name, char *buf, size_t count, loff_t *pos); + +int fimc_is_sec_get_sysfs_finfo(struct fimc_is_from_info **finfo); +int fimc_is_sec_get_sysfs_pinfo(struct fimc_is_from_info **pinfo); +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +int fimc_is_sec_get_sysfs_finfo_front(struct fimc_is_from_info **finfo); +int fimc_is_sec_get_sysfs_pinfo_front(struct fimc_is_from_info **pinfo); +int fimc_is_sec_get_front_cal_buf(char **buf); +#endif + +int fimc_is_sec_get_cal_buf(char **buf); +int fimc_is_sec_get_loaded_fw(char **buf); +int fimc_is_sec_get_loaded_c1_fw(char **buf); + +int fimc_is_sec_get_camid_from_hal(char *fw_name, char *setf_name); +int fimc_is_sec_get_camid(void); +int fimc_is_sec_set_camid(int id); +int fimc_is_sec_get_pixel_size(char *header_ver); +int fimc_is_sec_fw_find(struct fimc_is_core *core, char *fw_name, char *setf_name); + +int fimc_is_sec_readfw(struct fimc_is_core *core); +#if defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) || defined(CONFIG_CAMERA_EEPROM_SUPPORT_FRONT) +int fimc_is_sec_fw_sel_eeprom(struct device *dev, char *fw_name, char *setf_name, int id, bool headerOnly); +#endif +#if !defined(CONFIG_CAMERA_EEPROM_SUPPORT_REAR) +int fimc_is_sec_readcal(struct fimc_is_core *core); +int fimc_is_sec_fw_sel(struct fimc_is_core *core, struct device *dev, char *fw_name, char *setf_name, bool headerOnly); +#endif +#ifdef CONFIG_COMPANION_USE +int fimc_is_sec_concord_fw_sel(struct fimc_is_core *core, struct device *dev, + char *fw_name, char *master_setf_name, char *mode_setf_name); +#endif +int fimc_is_sec_fw_revision(char *fw_ver); +int fimc_is_sec_fw_revision(char *fw_ver); +bool fimc_is_sec_fw_module_compare(char *fw_ver1, char *fw_ver2); + +bool fimc_is_sec_check_fw_crc32(char *buf); +bool fimc_is_sec_check_cal_crc32(char *buf, int id); +void fimc_is_sec_make_crc32_table(u32 *table, u32 id); + +int fimc_is_sec_gpio_enable(struct exynos_platform_fimc_is *pdata, char *name, bool on); +int fimc_is_sec_core_voltage_select(struct device *dev, char *header_ver); +int fimc_is_sec_ldo_enable(struct device *dev, char *name, bool on); + +int fimc_is_spi_reset_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size); +int fimc_is_spi_read_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size); +#ifdef CONFIG_COMPANION_USE +void fimc_is_set_spi_config(struct fimc_is_spi_gpio *spi_gpio, int func, bool ssn); +#endif +#endif /* FIMC_IS_SEC_DEFINE_H */ diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-spi.c b/drivers/media/platform/exynos/fimc-is/fimc-is-spi.c new file mode 100644 index 000000000000..8151b3109e4e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-spi.c @@ -0,0 +1,276 @@ +/* + * driver for FIMC-IS SPI + * + * Copyright (c) 2011, Samsung Electronics. All rights reserved + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include "fimc-is-core.h" +#include "fimc-is-regs.h" + +#define STREAM_TO_U16(var16, p) {(var16) = ((u16)(*((u8 *)p+1)) + \ + ((u8)(*((u8 *)p) << 8))); } + +#ifndef __devinit +#define __devinit +#endif + +#ifndef __devexit +#define __devexit +#endif + +#ifndef __devexit_p +#define __devexit_p(x) x +#endif + +static struct spi_device *g_spi; + +int fimc_is_spi_reset(void *buf, u32 rx_addr, size_t size) +{ + unsigned char req_rst[1] = { 0x99 }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + t_c.tx_buf = req_rst; + t_c.len = 1; + t_c.cs_change = 0; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + + ret = spi_sync(g_spi, &m); + if (ret) { + err("spi sync error - can't get device information"); + return -EIO; + } + + return 0; +} + +int fimc_is_spi_read(void *buf, u32 rx_addr, size_t size) +{ + unsigned char req_data[4] = { 0x03, }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + req_data[1] = (rx_addr & 0xFF0000) >> 16; + req_data[2] = (rx_addr & 0xFF00) >> 8; + req_data[3] = (rx_addr & 0xFF); + + t_c.tx_buf = req_data; + t_c.len = 4; + t_c.cs_change = 1; + t_c.bits_per_word = 32; + + t_r.rx_buf = buf; + t_r.len = size; + t_r.cs_change = 0; + t_r.bits_per_word = 32; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + spi_message_add_tail(&t_r, &m); + + ret = spi_sync(g_spi, &m); + if (ret) { + err("spi sync error - can't read data"); + return -EIO; + } else + return 0; +} + +int fimc_is_spi_reset_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size) +{ + unsigned char req_rst[1] = { 0x99 }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + t_c.tx_buf = req_rst; + t_c.len = 1; + t_c.cs_change = 0; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + + ret = spi_sync(spi, &m); + if (ret) { + err("spi sync error - can't get device information"); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(fimc_is_spi_reset_by_core); + +int fimc_is_spi_read_by_core(struct spi_device *spi, void *buf, u32 rx_addr, size_t size) +{ + unsigned char req_data[4] = { 0x03, }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + req_data[1] = (rx_addr & 0xFF0000) >> 16; + req_data[2] = (rx_addr & 0xFF00) >> 8; + req_data[3] = (rx_addr & 0xFF); + + t_c.tx_buf = req_data; + t_c.len = 4; + t_c.cs_change = 1; + t_c.bits_per_word = 32; + + t_r.rx_buf = buf; + t_r.len = size; + t_r.cs_change = 0; + t_r.bits_per_word = 32; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + spi_message_add_tail(&t_r, &m); + + spi->max_speed_hz = 48000000; + ret = spi_sync(spi, &m); + if (ret) { + err("spi sync error - can't read data"); + return -EIO; + } else { + return 0; + } +} +EXPORT_SYMBOL(fimc_is_spi_read_by_core); + +int fimc_is_spi_read_module_id(struct spi_device *spi, void *buf, u16 rx_addr, size_t size) +{ + unsigned char req_data[4] = { 0x90, }; + int ret; + + struct spi_transfer t_c; + struct spi_transfer t_r; + + struct spi_message m; + + memset(&t_c, 0x00, sizeof(t_c)); + memset(&t_r, 0x00, sizeof(t_r)); + + req_data[1] = (rx_addr & 0xFF00) >> 8; + req_data[2] = (rx_addr & 0xFF); + + t_c.tx_buf = req_data; + t_c.len = 4; + t_c.cs_change = 1; + t_c.bits_per_word = 32; + + t_r.rx_buf = buf; + t_r.len = size; + + spi_message_init(&m); + spi_message_add_tail(&t_c, &m); + spi_message_add_tail(&t_r, &m); + + spi->max_speed_hz = 48000000; + ret = spi_sync(spi, &m); + if (ret) { + err("spi sync error - can't read data"); + return -EIO; + } else { + return 0; + } +} +EXPORT_SYMBOL(fimc_is_spi_read_module_id); + + +static int __devinit fimc_is_spi_probe(struct spi_device *spi) +{ + int ret = 0; + + dbg_core("%s\n", __func__); + + /* spi->bits_per_word = 16; */ + if (spi_setup(spi)) { + pr_err("failed to setup spi for fimc_is_spi\n"); + ret = -EINVAL; + goto exit; + } + + g_spi = spi; + +exit: + return ret; +} + +static int __devexit fimc_is_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver fimc_is_spi_driver = { + .driver = { + .name = "fimc_is_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = fimc_is_spi_probe, + .remove = __devexit_p(fimc_is_spi_remove), +}; + +static int __init fimc_is_spi_init(void) +{ + int ret; + + ret = spi_register_driver(&fimc_is_spi_driver); + + if (ret) + pr_err("failed to register imc_is_spi- %x\n", ret); + + return ret; +} + +static void __exit fimc_is_spi_exit(void) +{ + spi_unregister_driver(&fimc_is_spi_driver); +} + +module_init(fimc_is_spi_init); +module_exit(fimc_is_spi_exit); + +MODULE_DESCRIPTION("FIMC-IS SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.c b/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.c new file mode 100644 index 000000000000..8f0d966d4ec1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.c @@ -0,0 +1,395 @@ +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" + +int fimc_is_subdev_open(struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx, + const struct param_control *init_ctl) +{ + int ret = 0; + + if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state)) { + warn("subdev%d already open", subdev->entry); + goto p_err; + } + + mutex_init(&subdev->mutex_state); + subdev->vctx = vctx; + subdev->input.width = 0; + subdev->input.height = 0; + subdev->output.width = 0; + subdev->output.height = 0; + + if (init_ctl) { + if (init_ctl->cmd != CONTROL_COMMAND_START) { + if ((subdev->entry == ENTRY_DIS) || + (subdev->entry == ENTRY_TDNR)) { +#if defined(ENABLE_VDIS) || defined(ENABLE_TDNR) + err("%d entry is not start", subdev->entry); +#endif + } else { + err("%d entry is not start", subdev->entry); + } + ret = -EINVAL; + goto p_err; + } + + if (init_ctl->bypass == CONTROL_BYPASS_ENABLE) + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + else if (init_ctl->bypass == CONTROL_BYPASS_DISABLE) + set_bit(FIMC_IS_SUBDEV_START, &subdev->state); + else { + err("%d entry has invalid bypass value(%d)", + subdev->entry, init_ctl->bypass); + ret = -EINVAL; + goto p_err; + } + } else { + /* isp, scc, scp do not use bypass(memory interface)*/ + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + } + + set_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state); + +p_err: + return ret; +} + +int fimc_is_subdev_close(struct fimc_is_subdev *subdev) +{ + clear_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state); + + return 0; +} + +int fimc_is_subdev_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue) +{ + return 0; +} + +int fimc_is_subdev_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!queue); + + framemgr = &queue->framemgr; + +/* + if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { + mwarn("already stop", device); + goto p_err; + } +*/ + + framemgr_e_barrier_irqs(framemgr, FMGR_IDX_4, flags); + + if (framemgr->frame_pro_cnt > 0) { + framemgr_x_barrier_irqr(framemgr, FMGR_IDX_4, flags); + merr("being processed, can't stop", device); + ret = -EINVAL; + goto p_err; + } + + fimc_is_frame_complete_head(framemgr, &frame); + while (frame) { + fimc_is_frame_trans_com_to_fre(framemgr, frame); + fimc_is_frame_complete_head(framemgr, &frame); + } + + fimc_is_frame_request_head(framemgr, &frame); + while (frame) { + fimc_is_frame_trans_req_to_fre(framemgr, frame); + fimc_is_frame_request_head(framemgr, &frame); + } + + framemgr_x_barrier_irqr(framemgr, FMGR_IDX_4, flags); + + clear_bit(FIMC_IS_SUBDEV_START, &subdev->state); + +p_err: + return ret; +} + +int fimc_is_subdev_s_format(struct fimc_is_subdev *subdev, + u32 width, u32 height) +{ + int ret = 0; + + BUG_ON(!subdev); + + subdev->output.width = width; + subdev->output.height = height; + + return ret; +} + +int fimc_is_subdev_buffer_queue(struct fimc_is_subdev *subdev, + u32 index) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_video_ctx *vctx; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!subdev); + BUG_ON(!subdev->vctx); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + + vctx = subdev->vctx; + framemgr = GET_DST_FRAMEMGR(vctx); + + /* 1. check frame validation */ + frame = &framemgr->frame[index]; + if (!frame) { + merr("frame is null\n", vctx); + ret = EINVAL; + goto p_err; + } + + if (unlikely(!test_bit(FRAME_INI_MEM, &frame->memory))) { + err("frame %d is NOT init", index); + ret = EINVAL; + goto p_err; + } + + /* 2. update frame manager */ + framemgr_e_barrier_irqs(framemgr, index, flags); + + if (frame->state == FIMC_IS_FRAME_STATE_FREE) { + if (frame->req_flag) { + warn("%d request flag is not clear(%08X)\n", + frame->index, (u32)frame->req_flag); + frame->req_flag = 0; + } + + fimc_is_frame_trans_fre_to_req(framemgr, frame); + } else { + merr("frame(%d) is invalid state(%d)\n", vctx, index, frame->state); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, index, flags); + +p_err: + return ret; +} + +int fimc_is_subdev_buffer_finish(struct fimc_is_subdev *subdev, + u32 index) +{ + int ret = 0; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!subdev); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + + framemgr = GET_SUBDEV_FRAMEMGR(subdev); + if (!framemgr) { + err("framemgr is NULL"); + ret = -EINVAL; + goto p_err; + } + + framemgr_e_barrier_irq(framemgr, index); + + fimc_is_frame_complete_head(framemgr, &frame); + if (frame) { + if (frame->index == index) { + fimc_is_frame_trans_com_to_fre(framemgr, frame); + } else { + err("buffer index is NOT matched(%d != %d)\n", + index, frame->index); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + } else { + err("frame is empty from complete"); + fimc_is_frame_print_all(framemgr); + ret = -EINVAL; + } + + framemgr_x_barrier_irq(framemgr, index); + +p_err: + return ret; +} + +const struct fimc_is_queue_ops fimc_is_ischain_sub_ops = { + .start_streaming = fimc_is_subdev_start, + .stop_streaming = fimc_is_subdev_stop +}; + +void fimc_is_subdev_dis_start(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + +#ifdef ENABLE_VDIS + param->control.cmd = CONTROL_COMMAND_START; + param->control.bypass = CONTROL_BYPASS_DISABLE; + param->control.buffer_number = SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF; + param->control.buffer_address = device->imemory.dvaddr_shared + 300 * sizeof(u32); + device->is_region->shared[300] = device->imemory.dvaddr_dis; +#else + merr("can't start hw vdis", device); + BUG(); +#endif + + *lindex |= LOWBIT_OF(PARAM_DIS_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_DIS_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_dis_stop(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + param->control.cmd = CONTROL_COMMAND_STOP; + param->control.bypass = CONTROL_BYPASS_DISABLE; + + *lindex |= LOWBIT_OF(PARAM_DIS_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_DIS_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_dis_bypass(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (device->pdata->subip_info->_dis.full_bypass) + param->control.cmd = CONTROL_COMMAND_STOP; + else + param->control.cmd = CONTROL_COMMAND_START; + /* + * special option + * bypass command should be 2 for enabling software dis + * software dis is not active because output format of software + * can't support multi-plane. + */ + param->control.bypass = CONTROL_BYPASS_ENABLE; + + *lindex |= LOWBIT_OF(PARAM_DIS_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_DIS_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_dnr_start(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!ctl_param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + ctl_param->cmd = CONTROL_COMMAND_START; + ctl_param->bypass = CONTROL_BYPASS_DISABLE; + ctl_param->buffer_number = SIZE_DNR_INTERNAL_BUF * NUM_DNR_INTERNAL_BUF; + ctl_param->buffer_address = device->imemory.dvaddr_shared + 350 * sizeof(u32); + device->is_region->shared[350] = device->imemory.dvaddr_3dnr; + + *lindex |= LOWBIT_OF(PARAM_TDNR_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_TDNR_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_dnr_stop(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!ctl_param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + ctl_param->cmd = CONTROL_COMMAND_STOP; + ctl_param->bypass = CONTROL_BYPASS_DISABLE; + + *lindex |= LOWBIT_OF(PARAM_TDNR_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_TDNR_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_dnr_bypass(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!ctl_param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (device->pdata->subip_info->_dnr.full_bypass) + ctl_param->cmd = CONTROL_COMMAND_STOP; + else + ctl_param->cmd = CONTROL_COMMAND_START; + + ctl_param->bypass = CONTROL_BYPASS_ENABLE; + + *lindex |= LOWBIT_OF(PARAM_TDNR_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_TDNR_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_drc_start(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!ctl_param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + ctl_param->cmd = CONTROL_COMMAND_START; + ctl_param->bypass = CONTROL_BYPASS_DISABLE; + *lindex |= LOWBIT_OF(PARAM_DRC_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_DRC_CONTROL); + (*indexes)++; +} + +void fimc_is_subdev_drc_bypass(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes) +{ + BUG_ON(!device); + BUG_ON(!ctl_param); + BUG_ON(!lindex); + BUG_ON(!hindex); + BUG_ON(!indexes); + + if (device->pdata->subip_info->_drc.full_bypass) + ctl_param->cmd = CONTROL_COMMAND_STOP; + else + ctl_param->cmd = CONTROL_COMMAND_START; + + ctl_param->bypass = CONTROL_BYPASS_ENABLE; + + *lindex |= LOWBIT_OF(PARAM_DRC_CONTROL); + *hindex |= HIGHBIT_OF(PARAM_DRC_CONTROL); + (*indexes)++; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.h b/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.h new file mode 100644 index 000000000000..94fc7c85db87 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-subdev-ctrl.h @@ -0,0 +1,90 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_SUBDEV_H +#define FIMC_IS_SUBDEV_H + +#include "fimc-is-param.h" +#include "fimc-is-video.h" + +struct fimc_is_group; +struct fimc_is_device_ischain; + +enum fimc_is_subdev_state { + FIMC_IS_SUBDEV_OPEN, + FIMC_IS_SUBDEV_START +}; + +struct fimc_is_subdev_path { + u32 width; + u32 height; +}; + +struct fimc_is_subdev { + u32 id; + u32 entry; + unsigned long state; + struct mutex mutex_state; + + struct fimc_is_subdev_path input; + struct fimc_is_subdev_path output; + + struct fimc_is_group *group; + struct fimc_is_video_ctx *vctx; + struct fimc_is_subdev *leader; +}; + +#define GET_LEADER_FRAMEMGR(leader) \ + (((leader) && (leader)->vctx) ? (&(leader)->vctx->q_src->framemgr) : NULL) +#define GET_SUBDEV_FRAMEMGR(subdev) \ + (((subdev) && (subdev)->vctx) ? (&(subdev)->vctx->q_dst->framemgr) : NULL) + +#define GET_LEADER_QUEUE(leader) \ + (((leader) && (leader)->vctx) ? ((leader)->vctx->q_src) : NULL) +#define GET_SUBDEV_QUEUE(subdev) \ + (((subdev) && (subdev)->vctx) ? ((subdev)->vctx->q_dst) : NULL) + +/*common subdev*/ +int fimc_is_subdev_open(struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx, + const struct param_control *init_ctl); +int fimc_is_subdev_close(struct fimc_is_subdev *subdev); +int fimc_is_subdev_start(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_subdev_stop(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +int fimc_is_subdev_s_format(struct fimc_is_subdev *subdev, + u32 width, u32 height); +int fimc_is_subdev_buffer_queue(struct fimc_is_subdev *subdev, + u32 index); +int fimc_is_subdev_buffer_finish(struct fimc_is_subdev *subdev, + u32 index); + +void fimc_is_subdev_dis_start(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_dis_stop(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_dis_bypass(struct fimc_is_device_ischain *device, + struct dis_param *param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_dnr_start(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_dnr_stop(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_dnr_bypass(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_drc_start(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes); +void fimc_is_subdev_drc_bypass(struct fimc_is_device_ischain *device, + struct param_control *ctl_param, u32 *lindex, u32 *hindex, u32 *indexes); +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-time.c b/drivers/media/platform/exynos/fimc-is/fimc-is-time.c new file mode 100644 index 000000000000..f60adf8d6e5d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-time.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include + +#include "fimc-is-time.h" + +static struct timeval itime1; + +void TIME_STR1(void) +{ + do_gettimeofday(&itime1); +} + +void TIME_END1(void) +{ + u32 time; + struct timeval temp; + + do_gettimeofday(&temp); + time = (temp.tv_sec - itime1.tv_sec)*1000000 + + (temp.tv_usec - itime1.tv_usec); + + pr_info("TIME_MEASURE : %dus\n", time); +} + +uint64_t fimc_is_get_timestamp(void) +{ + struct timespec curtime; + + do_posix_clock_monotonic_gettime(&curtime); + + return (uint64_t)curtime.tv_sec*1000000000 + curtime.tv_nsec; +} + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME + +void measure_init(struct fimc_is_time *time, + u32 instance, + u32 group_id, + u32 report_period) +{ + time->instance = instance; + time->group_id = group_id; + time->report_period = report_period; + time->time_count = 0; + time->time1_min = 0; + time->time1_max = 0; + time->time1_tot = 0; + time->time2_min = 0; + time->time2_max = 0; + time->time2_tot = 0; + time->time3_min = 0; + time->time3_max = 0; + time->time3_tot = 0; + time->time4_cur = 0; + time->time4_old = 0; + time->time4_tot = 0; +} + +void measure_period(struct fimc_is_time *time, + u32 report_period) +{ + time->report_period = report_period; +} + +void measure_time( + struct fimc_is_time *time, + struct timeval *time_queued, + struct timeval *time_shot, + struct timeval *time_shotdone, + struct timeval *time_dequeued) +{ + u32 temp1, temp2, temp3; + + temp1 = (time_shot->tv_sec - time_queued->tv_sec)*1000000 + + (time_shot->tv_usec - time_queued->tv_usec); + temp2 = (time_shotdone->tv_sec - time_shot->tv_sec)*1000000 + + (time_shotdone->tv_usec - time_shot->tv_usec); + temp3 = (time_dequeued->tv_sec - time_shotdone->tv_sec)*1000000 + + (time_dequeued->tv_usec - time_shotdone->tv_usec); + + if (!time->time_count) { + time->time1_min = temp1; + time->time1_max = temp1; + time->time2_min = temp2; + time->time2_max = temp2; + time->time3_min = temp3; + time->time3_max = temp3; + } else { + if (time->time1_min > temp1) + time->time1_min = temp1; + + if (time->time1_max < temp1) + time->time1_max = temp1; + + if (time->time2_min > temp2) + time->time2_min = temp2; + + if (time->time2_max < temp2) + time->time2_max = temp2; + + if (time->time3_min > temp3) + time->time3_min = temp3; + + if (time->time3_max < temp3) + time->time3_max = temp3; + } + + time->time1_tot += temp1; + time->time2_tot += temp2; + time->time3_tot += temp3; + + time->time4_cur = time_queued->tv_sec*1000000 + time_queued->tv_usec; + time->time4_tot += (time->time4_cur - time->time4_old); + time->time4_old = time->time4_cur; + + time->time_count++; + + if (time->time_count % time->report_period) + return; + + pr_info("I%dG%d t1(%05d,%05d,%05d), t2(%05d,%05d,%05d), t3(%05d,%05d,%05d) : %d(%dfps)", + time->instance, time->group_id, + temp1, time->time1_max, time->time1_tot / time->time_count, + temp2, time->time2_max, time->time2_tot / time->time_count, + temp3, time->time3_max, time->time3_tot / time->time_count, + time->time4_tot / time->report_period, + (1000000 * time->report_period) / time->time4_tot); + + time->time_count = 0; + time->time1_tot = 0; + time->time2_tot = 0; + time->time3_tot = 0; + time->time4_tot = 0; +} + +#endif + +#ifdef INTERFACE_TIME +void measure_init(struct fimc_is_interface_time *time, u32 cmd) +{ + time->cmd = cmd; + time->time_max = 0; + time->time_min = 0; + time->time_tot = 0; + time->time_cnt = 0; +} + +void measure_time(struct fimc_is_interface_time *time, + u32 instance, + u32 group, + struct timeval *start, + struct timeval *end) +{ + u32 temp; + + temp = (end->tv_sec - start->tv_sec)*1000000 + (end->tv_usec - start->tv_usec); + + if (time->time_cnt) { + time->time_max = temp; + time->time_min = temp; + } else { + if (time->time_min > temp) + time->time_min = temp; + + if (time->time_max < temp) + time->time_max = temp; + } + + time->time_tot += temp; + time->time_cnt++; + + pr_info("cmd[%d][%d](%d) : curr(%d), max(%d), avg(%d)\n", + instance, group, time->cmd, temp, time->time_max, time->time_tot / time->time_cnt); +} +#endif + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-time.h b/drivers/media/platform/exynos/fimc-is/fimc-is-time.h new file mode 100644 index 000000000000..70c14a95932c --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-time.h @@ -0,0 +1,85 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_TIME_H +#define FIMC_IS_TIME_H + +/* #define MEASURE_TIME */ +/* #define INTERNAL_TIME */ +/* #define EXTERNAL_TIME */ +/* #define INTERFACE_TIME */ + +#define INSTANCE_MASK 0x3 + +#define TM_FLITE_STR 0 +#define TM_FLITE_END 1 +#define TM_SHOT 2 +#define TM_SHOT_D 3 +#define TM_META_D 4 +#define TM_MAX_INDEX 5 + +struct fimc_is_time { + u32 instance; + u32 group_id; + u32 report_period; + u32 time_count; + u32 time1_min; + u32 time1_max; + u32 time1_tot; + u32 time2_min; + u32 time2_max; + u32 time2_tot; + u32 time3_min; + u32 time3_max; + u32 time3_tot; + u32 time4_cur; + u32 time4_old; + u32 time4_tot; +}; + +void TIME_STR1(void); +void TIME_END1(void); +uint64_t fimc_is_get_timestamp(void); + +#ifdef MEASURE_TIME +#ifdef INTERNAL_TIME +void measure_init(struct fimc_is_time *time, + u32 instance, + u32 group_id, + u32 report_period); +void measure_period(struct fimc_is_time *time, + u32 report_period); +void measure_time( + struct fimc_is_time *time, + struct timeval *time_queued, + struct timeval *time_shot, + struct timeval *time_shotdone, + struct timeval *time_dequeued); +#endif +#ifdef INTERFACE_TIME +struct fimc_is_interface_time { + u32 cmd; + u32 time_tot; + u32 time_min; + u32 time_max; + u32 time_cnt; +}; + +void measure_init(struct fimc_is_interface_time *time, u32 cmd); +void measure_time(struct fimc_is_interface_time *time, + u32 instance, + u32 group, + struct timeval *start, + struct timeval *end); +#endif +#endif + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-type.h b/drivers/media/platform/exynos/fimc-is/fimc-is-type.h new file mode 100644 index 000000000000..2f70fa828dbc --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-type.h @@ -0,0 +1,40 @@ +#ifndef FIMC_IS_TYPE_H +#define FIMC_IS_TYPE_H + +#include + +#include + +enum fimc_is_device_type { + FIMC_IS_DEVICE_SENSOR, + FIMC_IS_DEVICE_ISCHAIN +}; + +struct fimc_is_window { + u32 o_width; + u32 o_height; + u32 width; + u32 height; + u32 offs_h; + u32 offs_v; + u32 otf_width; + u32 otf_height; +}; + +struct fimc_is_fmt { + char *name; + enum v4l2_mbus_pixelcode mbus_code; + u32 pixelformat; + u32 field; + u32 num_planes; +}; + +struct fimc_is_image { + u32 framerate; + struct fimc_is_window window; + struct fimc_is_fmt format; +}; + +#define TO_WORD_OFFSET(byte_offset) ((byte_offset) >> 2) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aa.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aa.c new file mode 100644 index 000000000000..6dc3d085288a --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aa.c @@ -0,0 +1,960 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-metadata.h" + +const struct v4l2_file_operations fimc_is_3aa_video_fops; +const struct v4l2_ioctl_ops fimc_is_3aa_video_ioctl_ops; +const struct vb2_ops fimc_is_3aa_qops; + +int fimc_is_3a0_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_3a0; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_3AA_NAME(0), + FIMC_IS_VIDEO_3A0_NUM, + VFL_DIR_M2M, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_3aa_video_fops, + &fimc_is_3aa_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +int fimc_is_3a1_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_3a1; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_3AA_NAME(1), + FIMC_IS_VIDEO_3A1_NUM, + VFL_DIR_M2M, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_3aa_video_fops, + &fimc_is_3aa_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_3aa_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + + if (video->id == FIMC_IS_VIDEO_3A0_NUM) + core = container_of(video, struct fimc_is_core, video_3a0); + else + core = container_of(video, struct fimc_is_core, video_3a1); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_3AA_GRP, FRAMEMGR_ID_3AAP); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[3A%d:V:%d] %s\n", GET_3AA_ID(video), vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + info("[3A%d:V:%d] <-> [ISP:V:%d] \n", GET_3AA_ID(video), + vctx->instance, device->instance); + + ret = fimc_is_video_open(vctx, + device, + VIDEO_3AA_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_M2M, + &fimc_is_3aa_qops, + &fimc_is_ischain_3aa_ops, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_ischain_3aa_open(device, vctx); + if (ret) { + err("fimc_is_ischain_3aa_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_3aa_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_video *video = NULL; + struct fimc_is_device_ischain *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + err("video is NULL"); + ret = -EINVAL; + goto p_err; + } + + info("[3A%d:V:%d] %s\n", GET_3AA_ID(video), vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_ischain_3aa_close(device, vctx); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_3aa_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + if (ret) + merr("fimc_is_video_poll is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_3aa_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_3aa_video_open, + .release = fimc_is_3aa_video_close, + .poll = fimc_is_3aa_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_3aa_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_3aa_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + /* Todo : add to query capability code */ + return 0; +} + +static int fimc_is_3aa_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + /* Todo : add to enumerate format code */ + return 0; +} + +static int fimc_is_3aa_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + /* Todo : add to get format code */ + return 0; +} + +static int fimc_is_3aa_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aa("%s\n", vctx, __func__); + + device = vctx->device; + leader = &device->group_3aa.leader; + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) { + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + goto p_err; + } + + if (V4L2_TYPE_IS_OUTPUT(format->type)) { + queue = vctx->q_src; + fimc_is_ischain_3aa_s_format(device, + queue->framecfg.width, + queue->framecfg.height); + } else { + queue = vctx->q_dst; + fimc_is_subdev_s_format(leader, + queue->framecfg.width, + queue->framecfg.height); + } + +p_err: + return ret; +} + +static int fimc_is_3aa_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + /* Todo : add to crop capability code */ + return 0; +} + +static int fimc_is_3aa_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + /* Todo : add to get crop control code */ + return 0; +} + +static int fimc_is_3aa_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *ischain; + struct fimc_is_group *group; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_3aa("%s\n", vctx, __func__); + + ischain = vctx->device; + BUG_ON(!ischain); + group = &ischain->group_3aa; + BUG_ON(!group); + subdev = &group->leader; + + if (V4L2_TYPE_IS_OUTPUT(crop->type)) + fimc_is_ischain_3aa_s_format(ischain, + crop->c.width, crop->c.height); + else + fimc_is_subdev_s_format(subdev, + crop->c.width, crop->c.height); + + return 0; +} + +static int fimc_is_3aa_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + + mdbgv_3aa("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + if (V4L2_TYPE_IS_OUTPUT(buf->type)) { + ret = fimc_is_ischain_3aa_reqbufs(device, buf->count); + if (ret) { + merr("3a0_reqbufs is fail(%d)", vctx, ret); + goto p_err; + } + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_3aa_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aa("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + mdbgv_3aa("%s(%02d:%d)\n", vctx, __func__, buf->type, buf->index); +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf(type %d) is fail(%d)", vctx, buf->type, ret); + + return ret; +} + +static int fimc_is_3aa_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + mdbgv_3aa("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + /* HACK : this log is commented until timeout issue fixed */ + /* if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + */ + return ret; +} + +static int fimc_is_3aa_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aa("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aa("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo: add enum input control code */ + return 0; +} + +static int fimc_is_3aa_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + /* Todo: add to get input control code */ + return 0; +} + +static int fimc_is_3aa_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aa("%s(input : %08X)\n", vctx, __func__, input); + + device = vctx->device; + + ret = fimc_is_ischain_3aa_s_input(device, input); + if (ret) { + merr("fimc_is_ischain_3aa_s_input is fail", vctx); + goto p_err; + } + + ret = fimc_is_ischain_init_wrap(device, input); + if (ret) { + merr("fimc_is_device_init(%d) is fail", vctx, input); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_3aa_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + + dbg_isp("%s\n", __func__); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + switch (ctrl->id) { + case V4L2_CID_IS_INTENT: + device->group_3aa.intent_ctl.aa.captureIntent = ctrl->value; + minfo("[3AA:V] s_ctrl intent(%d)\n", vctx, ctrl->value); + break; + case V4L2_CID_IS_FORCE_DONE: + set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &device->group_3aa.state); + break; + case V4L2_CID_IS_MAP_BUFFER: + { + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct dma_buf *dmabuf; + struct dma_buf_attachment *attachment; + dma_addr_t dva; + struct v4l2_buffer *buf = NULL; + struct v4l2_plane *planes; + size_t size; + u32 write, plane, group_id; + + size = sizeof(struct v4l2_buffer); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + merr("kmalloc is fail", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = copy_from_user(buf, (void __user *)ctrl->value, size); + if (ret) { + merr("copy_from_user is fail(%d)", vctx, ret); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + merr("single plane is not supported", vctx); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (buf->index >= FRAMEMGR_MAX_REQUEST) { + merr("buffer index is invalid(%d)", vctx, buf->index); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (buf->length > VIDEO_MAX_PLANES) { + merr("planes[%d] is invalid", vctx, buf->length); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + queue = GET_QUEUE(vctx, buf->type); + if (queue->vbq->memory != V4L2_MEMORY_DMABUF) { + merr("memory type(%d) is not supported", vctx, queue->vbq->memory); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + size = sizeof(struct v4l2_plane) * buf->length; + planes = kmalloc(size, GFP_KERNEL); + if (IS_ERR(planes)) { + merr("kmalloc is fail(%p)", vctx, planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + ret = copy_from_user(planes, (void __user *)buf->m.planes, size); + if (ret) { + merr("copy_from_user is fail(%d)", vctx, ret); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + framemgr = &queue->framemgr; + frame = &framemgr->frame[buf->index]; + if (test_bit(FRAME_MAP_MEM, &frame->memory)) { + merr("this buffer(%d) is already mapped", vctx, buf->index); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + /* only last buffer need to map */ + if (buf->length > 0) { + plane = buf->length - 1; + } else { + merr("buf size is abnormal(%d)", vctx, buf->length); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + dmabuf = dma_buf_get(planes[plane].m.fd); + if (IS_ERR(dmabuf)) { + merr("dma_buf_get is fail(%p)", vctx, dmabuf); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + attachment = dma_buf_attach(dmabuf, &device->pdev->dev); + if (IS_ERR(attachment)) { + merr("dma_buf_attach is fail(%p)", vctx, attachment); + kfree(planes); + kfree(buf); + dma_buf_put(dmabuf); + ret = -EINVAL; + goto p_err; + } + + write = !V4L2_TYPE_IS_OUTPUT(buf->type); + dva = ion_iovmm_map(attachment, 0, dmabuf->size, write, plane); + if (IS_ERR_VALUE(dva)) { + merr("ion_iovmm_map is fail(%X)", vctx, dva); + kfree(planes); + kfree(buf); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + ret = -EINVAL; + goto p_err; + } + + group_id = GROUP_ID(device->group_3aa.id); + ret = fimc_is_itf_map(device, group_id, dva, dmabuf->size); + if (ret) { + merr("fimc_is_itf_map is fail(%d)", vctx, ret); + kfree(planes); + kfree(buf); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + goto p_err; + } + + minfo("[3AA:V] buffer%d.plane%d mapping\n", vctx, buf->index, plane); + set_bit(FRAME_MAP_MEM, &frame->memory); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + kfree(planes); + kfree(buf); + } + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +static int fimc_is_3aa_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + /* Todo: add to get control code */ + return 0; +} + +static int fimc_is_3aa_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + /* Todo: add to get extra control code */ + return 0; +} + +const struct v4l2_ioctl_ops fimc_is_3aa_video_ioctl_ops = { + .vidioc_querycap = fimc_is_3aa_video_querycap, + + .vidioc_enum_fmt_vid_out_mplane = fimc_is_3aa_video_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_3aa_video_enum_fmt_mplane, + + .vidioc_g_fmt_vid_out_mplane = fimc_is_3aa_video_get_format_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_3aa_video_get_format_mplane, + + .vidioc_s_fmt_vid_out_mplane = fimc_is_3aa_video_set_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_3aa_video_set_format_mplane, + + .vidioc_querybuf = fimc_is_3aa_video_querybuf, + .vidioc_reqbufs = fimc_is_3aa_video_reqbufs, + + .vidioc_qbuf = fimc_is_3aa_video_qbuf, + .vidioc_dqbuf = fimc_is_3aa_video_dqbuf, + + .vidioc_streamon = fimc_is_3aa_video_streamon, + .vidioc_streamoff = fimc_is_3aa_video_streamoff, + + .vidioc_enum_input = fimc_is_3aa_video_enum_input, + .vidioc_g_input = fimc_is_3aa_video_g_input, + .vidioc_s_input = fimc_is_3aa_video_s_input, + + .vidioc_s_ctrl = fimc_is_3aa_video_s_ctrl, + .vidioc_g_ctrl = fimc_is_3aa_video_g_ctrl, + .vidioc_g_ext_ctrls = fimc_is_3aa_video_g_ext_ctrl, + + .vidioc_cropcap = fimc_is_3aa_video_cropcap, + .vidioc_g_crop = fimc_is_3aa_video_get_crop, + .vidioc_s_crop = fimc_is_3aa_video_set_crop, +}; + +static int fimc_is_3aa_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_3aa("%s\n", vctx, __func__); + + queue = GET_VCTX_QUEUE(vctx, vbq); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_buffer_prepare(struct vb2_buffer *vb) +{ + /* Todo : add to prepare buffer */ + return 0; +} + +static inline void fimc_is_3aa_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_3aa_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_3aa_start_streaming(struct vb2_queue *vbq, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aa("%s\n", vctx, __func__); + + device = vctx->device; + + if (V4L2_TYPE_IS_OUTPUT(vbq->type)) { + queue = GET_SRC_QUEUE(vctx); + subdev = &device->group_3aa.leader; + } else { + queue = GET_DST_QUEUE(vctx); + subdev = &device->taap; + } + + ret = fimc_is_queue_start_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aa_stop_streaming(struct vb2_queue *vbq) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aa("%s\n", vctx, __func__); + + device = vctx->device; + + if (V4L2_TYPE_IS_OUTPUT(vbq->type)) { + queue = GET_SRC_QUEUE(vctx); + subdev = &device->group_3aa.leader; + } else { + queue = GET_DST_QUEUE(vctx); + subdev = &device->taap; + } + + ret = fimc_is_queue_stop_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static void fimc_is_3aa_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + u32 index; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + index = vb->v4l2_buf.index; + +#ifdef DBG_STREAMING + mdbgv_3aa("%s(%02d:%d)\n", vctx, __func__, vb->v4l2_buf.type, index); +#endif + + video = vctx->video; + device = vctx->device; + leader = &device->group_3aa.leader; + + if (V4L2_TYPE_IS_OUTPUT(vb->v4l2_buf.type)) { + queue = GET_SRC_QUEUE(vctx); + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_ischain_3aa_buffer_queue(device, queue, index); + if (ret) { + merr("fimc_is_ischain_3aa_buffer_queue is fail(%d)", vctx, ret); + return; + } + } else { + queue = GET_DST_QUEUE(vctx); + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_subdev_buffer_queue(leader, index); + if (ret) { + merr("fimc_is_subdev_buffer_queue is fail(%d)", vctx, ret); + return; + } + } +} + +static int fimc_is_3aa_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + u32 index = vb->v4l2_buf.index; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *device = vctx->device; + struct fimc_is_group *group = &device->group_3aa; + struct fimc_is_subdev *subdev = &group->leader; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!device); + +#ifdef DBG_STREAMING + mdbgv_3aa("%s(%02d:%d)\n", vctx, __func__, vb->v4l2_buf.type, index); +#endif + + if (V4L2_TYPE_IS_OUTPUT(vb->v4l2_buf.type)) { + queue = vctx->q_src; + fimc_is_ischain_3aa_buffer_finish(device, index); + } else { + queue = vctx->q_dst; + fimc_is_subdev_buffer_finish(subdev, index); + } + + return ret; +} + +const struct vb2_ops fimc_is_3aa_qops = { + .queue_setup = fimc_is_3aa_queue_setup, + .buf_prepare = fimc_is_3aa_buffer_prepare, + .buf_queue = fimc_is_3aa_buffer_queue, + .buf_finish = fimc_is_3aa_buffer_finish, + .wait_prepare = fimc_is_3aa_wait_prepare, + .wait_finish = fimc_is_3aa_wait_finish, + .start_streaming = fimc_is_3aa_start_streaming, + .stop_streaming = fimc_is_3aa_stop_streaming, +}; + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aac.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aac.c new file mode 100644 index 000000000000..3f63054c95e2 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-3aac.c @@ -0,0 +1,711 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +const struct v4l2_file_operations fimc_is_3aac_video_fops; +const struct v4l2_ioctl_ops fimc_is_3aac_video_ioctl_ops; +const struct vb2_ops fimc_is_3aac_qops; + +int fimc_is_3a0c_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_3a0c; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_3AAC_NAME(0), + FIMC_IS_VIDEO_3A0C_NUM, + VFL_DIR_RX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_3aac_video_fops, + &fimc_is_3aac_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +int fimc_is_3a1c_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_3a1c; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_3AAC_NAME(1), + FIMC_IS_VIDEO_3A1C_NUM, + VFL_DIR_RX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_3aac_video_fops, + &fimc_is_3aac_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + info("[3AC1:V:1] %s(%d)\n", __func__, ret); + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_3aac_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + + if (video->id == FIMC_IS_VIDEO_3A0C_NUM) + core = container_of(video, struct fimc_is_core, video_3a0c); + else + core = container_of(video, struct fimc_is_core, video_3a1c); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_3AAC); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[3A%dC:V:%d] %s\n", GET_3AAC_ID(video), vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_3AAC_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_CAPTURE, + &fimc_is_3aac_qops, + NULL, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_subdev_open(&device->taac, vctx, NULL); + if (ret) { + err("fimc_is_subdev_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_3aac_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_video *video = NULL; + struct fimc_is_device_ischain *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + merr("video is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + info("[3A%dC:V:%d] %s\n", GET_3AAC_ID(video), vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_subdev_close(&device->taac); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_3aac_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + if (ret) + merr("fimc_is_video_poll is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_3aac_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_3aac_video_open, + .release = fimc_is_3aac_video_close, + .poll = fimc_is_3aac_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_3aac_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_3aac_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + /* Todo : add to query capability code */ + return 0; +} + +static int fimc_is_3aac_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + BUG_ON(!format); + + mdbgv_3aac("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + subdev = &device->taac; + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) { + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + goto p_err; + } + + ret = fimc_is_subdev_s_format(subdev, queue->framecfg.width, queue->framecfg.height); + if (ret) { + merr("fimc_is_ischain_3aac_s_format is fail(%d)", vctx, ret); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_3aac_video_try_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_3aac("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + subdev = &device->taac; + leader = subdev->leader; + + if (!leader) { + merr("leader is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("leader still running, not applied", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret); + + p_err: + return ret; +} + +static int fimc_is_3aac_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aac("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_3aac("%s(index : %d)\n", vctx, __func__, buf->index); +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_3aac("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aac("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_3aac("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo: add enum input control code */ + return 0; +} + +static int fimc_is_3aac_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_3aac_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + return 0; +} + +static int fimc_is_3aac_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_framemgr *framemgr; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + mdbgv_3aac("%s\n", vctx, __func__); + + framemgr = GET_DST_FRAMEMGR(vctx); + + switch (ctrl->id) { + case V4L2_CID_IS_G_COMPLETES: + ctrl->value = framemgr->frame_com_cnt; + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int fimc_is_3aac_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + return 0; +} + +static int fimc_is_3aac_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + mdbgv_3aac("%s\n", vctx, __func__); + + switch (ctrl->id) { + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + return ret; +} + +const struct v4l2_ioctl_ops fimc_is_3aac_video_ioctl_ops = { + .vidioc_querycap = fimc_is_3aac_video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_3aac_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_3aac_video_get_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_3aac_video_set_format_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_is_3aac_video_try_format_mplane, + .vidioc_cropcap = fimc_is_3aac_video_cropcap, + .vidioc_g_crop = fimc_is_3aac_video_get_crop, + .vidioc_s_crop = fimc_is_3aac_video_set_crop, + .vidioc_reqbufs = fimc_is_3aac_video_reqbufs, + .vidioc_querybuf = fimc_is_3aac_video_querybuf, + .vidioc_qbuf = fimc_is_3aac_video_qbuf, + .vidioc_dqbuf = fimc_is_3aac_video_dqbuf, + .vidioc_streamon = fimc_is_3aac_video_streamon, + .vidioc_streamoff = fimc_is_3aac_video_streamoff, + .vidioc_enum_input = fimc_is_3aac_video_enum_input, + .vidioc_g_input = fimc_is_3aac_video_g_input, + .vidioc_s_input = fimc_is_3aac_video_s_input, + .vidioc_g_ctrl = fimc_is_3aac_video_g_ctrl, + .vidioc_s_ctrl = fimc_is_3aac_video_s_ctrl, + .vidioc_g_ext_ctrls = fimc_is_3aac_video_g_ext_ctrl, +}; + +static int fimc_is_3aac_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_3aac("%s\n", vctx, __func__); + + queue = GET_VCTX_QUEUE(vctx, vbq); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_buffer_prepare(struct vb2_buffer *vb) +{ + return 0; +} + +static inline void fimc_is_3aac_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_3aac_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_3aac_start_streaming(struct vb2_queue *vbq, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aac("%s\n", vctx, __func__); + + queue = GET_VCTX_QUEUE(vctx, vbq); + device = vctx->device; + subdev = &device->taac; + + ret = fimc_is_queue_start_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_3aac_stop_streaming(struct vb2_queue *vbq) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_3aac("%s\n", vctx, __func__); + + queue = GET_VCTX_QUEUE(vctx, vbq); + device = vctx->device; + subdev = &device->taac; + + ret = fimc_is_queue_stop_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static void fimc_is_3aac_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_video *video; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + mdbgv_3aac("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + device = vctx->device; + subdev = &device->taac; + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_subdev_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_3aac_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *ischain = vctx->device; + struct fimc_is_subdev *subdev = &ischain->taac; + +#ifdef DBG_STREAMING + mdbgv_3aac("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + ret = fimc_is_subdev_buffer_finish(subdev, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_3aac_qops = { + .queue_setup = fimc_is_3aac_queue_setup, + .buf_prepare = fimc_is_3aac_buffer_prepare, + .buf_queue = fimc_is_3aac_buffer_queue, + .buf_finish = fimc_is_3aac_buffer_finish, + .wait_prepare = fimc_is_3aac_wait_prepare, + .wait_finish = fimc_is_3aac_wait_finish, + .start_streaming = fimc_is_3aac_start_streaming, + .stop_streaming = fimc_is_3aac_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-companion.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-companion.c new file mode 100644 index 000000000000..c3da8b7d2265 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-companion.c @@ -0,0 +1,208 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-device-companion.h" +#include "fimc-is-video.h" + +const struct v4l2_file_operations fimc_is_comp_video_fops; +const struct v4l2_ioctl_ops fimc_is_comp_video_ioctl_ops; +const struct vb2_ops fimc_is_comp_qops; + +int fimc_is_comp_video_probe(void *data) +{ + int ret = 0; + char name[255]; + u32 number; + struct fimc_is_device_companion *device; + struct fimc_is_video *video; + + BUG_ON(!data); + + device = (struct fimc_is_device_companion *)data; + video = &device->video; + snprintf(name, sizeof(name), "%s%d", FIMC_IS_VIDEO_SENSOR_NAME, 9); + number = FIMC_IS_VIDEO_SS0_NUM + 9; + + if (!device->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + name, + number, + VFL_DIR_RX, + &device->mem, + &device->v4l2_dev, + &video->lock, + &fimc_is_comp_video_fops, + &fimc_is_comp_video_ioctl_ops); + if (ret) + dev_err(&device->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + info("[CP%d:V:X] %s(%d)\n", number, __func__, ret); + return ret; +} + +#ifdef CONFIG_OIS_USE +extern int fimc_is_ois_sine_mode(struct fimc_is_core *core, int mode); +#endif + +static int fimc_is_comp_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video *video; + struct fimc_is_device_companion *device; + + dbg_isp("%s\n", __func__); + + video = video_drvdata(file); + device = container_of(video, struct fimc_is_device_companion, video); + + if (!device->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + switch (ctrl->id) { +#ifdef CONFIG_OIS_USE + case V4L2_CID_CAMERA_OIS_SINE_MODE: + if (fimc_is_ois_sine_mode(device->private_data, ctrl->value)) { + err("failed to set ois sine mode : %d\n - %d", + ctrl->value, ret); + ret = -EINVAL; + } + break; +#endif + + default: + info("unsupported ioctl(%d, sine id = %d)\n", ctrl->id, V4L2_CID_CAMERA_OIS_SINE_MODE); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_comp_video_open(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_companion *device; + + vctx = NULL; + video = video_drvdata(file); + device = container_of(video, struct fimc_is_device_companion, video); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_INVALID); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[CP%d:V:%d] %s\n", video->id, vctx->instance, __func__); + + ret = fimc_is_companion_open(device); + if (ret) { + merr("fimc_is_comp_open is fail(%d)", vctx, ret); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_comp_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video = NULL; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_companion *device = NULL; + + BUG_ON(!file); + + video = video_drvdata(file); + device = container_of(video, struct fimc_is_device_companion, video); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + info("[CP:V:%d] %s\n", vctx->instance, __func__); + + ret = fimc_is_companion_close(device); + if (ret) + err("fimc_is_companion_close is fail(%d)", ret); + + ret = close_vctx(file, video, vctx); + if (ret) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +const struct v4l2_file_operations fimc_is_comp_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_comp_video_open, + .release = fimc_is_comp_video_close, + .unlocked_ioctl = video_ioctl2, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +const struct v4l2_ioctl_ops fimc_is_comp_video_ioctl_ops = { + .vidioc_s_ctrl = fimc_is_comp_video_s_ctrl, +}; + +const struct vb2_ops fimc_is_comp_qops = { +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-isp.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-isp.c new file mode 100644 index 000000000000..2dea15182122 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-isp.c @@ -0,0 +1,967 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-metadata.h" + +extern struct fimc_is_from_info *sysfs_finfo; +extern struct fimc_is_from_info *sysfs_pinfo; +extern bool is_dumped_fw_loading_needed; + +const struct v4l2_file_operations fimc_is_isp_video_fops; +const struct v4l2_ioctl_ops fimc_is_isp_video_ioctl_ops; +const struct vb2_ops fimc_is_isp_qops; + +int fimc_is_isp_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_isp; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_ISP_NAME, + FIMC_IS_VIDEO_ISP_NUM, + VFL_DIR_M2M, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_isp_video_fops, + &fimc_is_isp_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_isp_video_open(struct file *file) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + core = container_of(video, struct fimc_is_core, video_isp); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_ISP_GRP, FRAMEMGR_ID_INVALID); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[ISP:V:%d] %s\n", vctx->instance, __func__); + + device = &core->ischain[vctx->instance]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_ISP_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_OUTPUT, + &fimc_is_isp_qops, + &fimc_is_ischain_isp_ops, + NULL); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_ischain_open(device, vctx, &core->minfo); + if (ret) { + err("fimc_is_ischain_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_isp_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video = NULL; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_ischain *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + err("video is NULL"); + ret = -EINVAL; + goto p_err; + } + + info("[ISP:V:%d] %s\n", video->id, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (pm_qos_request_active(&device->user_qos)) + pm_qos_remove_request(&device->user_qos); + + fimc_is_ischain_close(device, vctx); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_isp_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + if (ret) + merr("fimc_is_video_poll is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_isp_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_isp_video_open, + .release = fimc_is_isp_video_close, + .poll = fimc_is_isp_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_isp_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_isp_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_is_core *isp = video_drvdata(file); + + strncpy(cap->driver, isp->pdev->name, sizeof(cap->driver) - 1); + + dbg_isp("%s(devname : %s)\n", __func__, cap->driver); + strncpy(cap->card, isp->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + + return 0; +} + +static int fimc_is_isp_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + dbg_isp("%s\n", __func__); + return 0; +} + +static int fimc_is_isp_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg_isp("%s\n", __func__); + return 0; +} + +static int fimc_is_isp_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + + mdbgv_isp("%s\n", vctx, __func__); + + queue = GET_VCTX_QUEUE(vctx, format); + device = vctx->device; + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + + fimc_is_ischain_isp_s_format(device, + queue->framecfg.width, + queue->framecfg.height); + + return ret; +} + +static int fimc_is_isp_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + dbg_isp("%s\n", __func__); + return 0; +} + +static int fimc_is_isp_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg_isp("%s\n", __func__); + return 0; +} + +static int fimc_is_isp_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *ischain; + + BUG_ON(!vctx); + + mdbgv_isp("%s\n", vctx, __func__); + + ischain = vctx->device; + BUG_ON(!ischain); + + fimc_is_ischain_isp_s_format(ischain, + crop->c.width, crop->c.height); + + return 0; +} + +static int fimc_is_isp_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + + mdbgv_isp("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_ischain_isp_reqbufs(device, buf->count); + if (ret) { + merr("isp_reqbufs is fail(%d)", vctx, ret); + goto p_err; + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(error %d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_isp_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_isp("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + mdbgv_isp("%s(index : %d)\n", vctx, __func__, buf->index); +#endif + + queue = GET_VCTX_QUEUE(vctx, buf); + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + merr("stream off state, can NOT qbuf", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_isp_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_isp("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_isp("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_isp("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo : add to enum input control code */ + return 0; +} + +static int fimc_is_isp_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + /* Todo : add to get input control code */ + return 0; +} + +static int fimc_is_isp_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + + mdbgv_isp("%s(input : %08X)\n", vctx, __func__, input); + + device = vctx->device; + + ret = fimc_is_ischain_isp_s_input(device, input); + if (ret) { + merr("fimc_is_ischain_isp_s_input is fail", vctx); + goto p_err; + } + + /* if there's only one group of isp, defines group id to 3a0 connected */ + if (GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a0) + || GET_FIMC_IS_NUM_OF_SUBIP2(device, 3a1)) + goto p_err; + + ret = fimc_is_ischain_init_wrap(device, input); + if (ret) { + merr("fimc_is_device_init(%d) is fail", vctx, input); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_isp_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + int i2c_clk; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_core *core; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + BUG_ON(!vctx->video); + + dbg_isp("%s\n", __func__); + + device = vctx->device; + video = vctx->video; + core = container_of(video, struct fimc_is_core, video_isp); + + if (core->resourcemgr.dvfs_ctrl.cur_int_qos == DVFS_L0) + i2c_clk = I2C_L0; + else + i2c_clk = I2C_L1; + + switch (ctrl->id) { + case V4L2_CID_IS_DEBUG_DUMP: + info("Print fimc-is info dump by HAL"); + if (device != NULL) { + fimc_is_hw_logdump(device->interface); + fimc_is_hw_regdump(device->interface); + CALL_POPS(device, print_clk, device->pdev); + } + if (ctrl->value) { + err("BUG_ON from HAL"); + BUG(); + } + break; + case V4L2_CID_IS_DEBUG_SYNC_LOG: + fimc_is_logsync(device->interface, ctrl->value, IS_MSG_TEST_SYNC_LOG); + break; + case V4L2_CID_IS_G_CAPABILITY: + ret = fimc_is_ischain_g_capability(device, ctrl->value); + dbg_isp("V4L2_CID_IS_G_CAPABILITY : %X\n", ctrl->value); + break; + case V4L2_CID_IS_FORCE_DONE: + set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &device->group_isp.state); + break; + case V4L2_CID_IS_DVFS_LOCK: + ret = fimc_is_itf_i2c_lock(device, I2C_L0, true); + if (ret) { + err("fimc_is_itf_i2_clock fail\n"); + break; + } + pm_qos_add_request(&device->user_qos, PM_QOS_DEVICE_THROUGHPUT, + ctrl->value); + ret = fimc_is_itf_i2c_lock(device, I2C_L0, false); + if (ret) { + err("fimc_is_itf_i2c_unlock fail\n"); + break; + } + dbg_isp("V4L2_CID_IS_DVFS_LOCK : %d\n", ctrl->value); + break; + case V4L2_CID_IS_DVFS_UNLOCK: + ret = fimc_is_itf_i2c_lock(device, i2c_clk, true); + if (ret) { + err("fimc_is_itf_i2_clock fail\n"); + break; + } + pm_qos_remove_request(&device->user_qos); + ret = fimc_is_itf_i2c_lock(device, i2c_clk, false); + if (ret) { + err("fimc_is_itf_i2c_unlock fail\n"); + break; + } + dbg_isp("V4L2_CID_IS_DVFS_UNLOCK : %d I2C(%d)\n", ctrl->value, i2c_clk); + break; + case V4L2_CID_IS_SET_SETFILE: + if (test_bit(FIMC_IS_SUBDEV_START, &device->group_isp.leader.state)) { + err("Setting setfile is only avaiable before starting device!! (0x%08x)", + ctrl->value); + ret = -EINVAL; + } else { + device->setfile = ctrl->value; + minfo("[ISP:V] setfile: 0x%08X\n", vctx, ctrl->value); + } + break; + case V4L2_CID_IS_COLOR_RANGE: + if (test_bit(FIMC_IS_SUBDEV_START, &device->group_isp.leader.state)) { + err("failed to change color range: device started already (0x%08x)", + ctrl->value); + ret = -EINVAL; + } else { + device->color_range &= ~FIMC_IS_ISP_CRANGE_MASK; + + if (ctrl->value) + device->color_range |= + (FIMC_IS_CRANGE_LIMITED << FIMC_IS_ISP_CRANGE_SHIFT); + } + break; + case V4L2_CID_IS_MAP_BUFFER: + { + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + struct dma_buf *dmabuf; + struct dma_buf_attachment *attachment; + dma_addr_t dva; + struct v4l2_buffer *buf = NULL; + struct v4l2_plane *planes; + size_t size; + u32 write, plane, group_id; + + size = sizeof(struct v4l2_buffer); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + merr("kmalloc is fail", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = copy_from_user(buf, (void __user *)ctrl->value, size); + if (ret) { + merr("copy_from_user is fail(%d)", vctx, ret); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + merr("single plane is not supported", vctx); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (buf->index >= FRAMEMGR_MAX_REQUEST) { + merr("buffer index is invalid(%d)", vctx, buf->index); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + if (buf->length > VIDEO_MAX_PLANES) { + merr("planes[%d] is invalid", vctx, buf->length); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + queue = GET_QUEUE(vctx, buf->type); + if (queue->vbq->memory != V4L2_MEMORY_DMABUF) { + merr("memory type(%d) is not supported", vctx, queue->vbq->memory); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + size = sizeof(struct v4l2_plane) * buf->length; + planes = kmalloc(size, GFP_KERNEL); + if (IS_ERR(planes)) { + merr("kmalloc is fail(%p)", vctx, planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + ret = copy_from_user(planes, (void __user *)buf->m.planes, size); + if (ret) { + merr("copy_from_user is fail(%d)", vctx, ret); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + framemgr = &queue->framemgr; + frame = &framemgr->frame[buf->index]; + if (test_bit(FRAME_MAP_MEM, &frame->memory)) { + merr("this buffer(%d) is already mapped", vctx, buf->index); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + /* only last buffer need to map */ + if (buf->length > 0) { + plane = buf->length - 1; + } else { + merr("buf size is abnormal(%d)", vctx, buf->length); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + dmabuf = dma_buf_get(planes[plane].m.fd); + if (IS_ERR(dmabuf)) { + merr("dma_buf_get is fail(%p)", vctx, dmabuf); + kfree(planes); + kfree(buf); + ret = -EINVAL; + goto p_err; + } + + attachment = dma_buf_attach(dmabuf, &device->pdev->dev); + if (IS_ERR(attachment)) { + merr("dma_buf_attach is fail(%p)", vctx, attachment); + kfree(planes); + kfree(buf); + dma_buf_put(dmabuf); + ret = -EINVAL; + goto p_err; + } + + write = !V4L2_TYPE_IS_OUTPUT(buf->type); + dva = ion_iovmm_map(attachment, 0, dmabuf->size, write, plane); + if (IS_ERR_VALUE(dva)) { + merr("ion_iovmm_map is fail(%X)", vctx, dva); + kfree(planes); + kfree(buf); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + ret = -EINVAL; + goto p_err; + } + + group_id = GROUP_ID(device->group_isp.id); + ret = fimc_is_itf_map(device, group_id, dva, dmabuf->size); + if (ret) { + merr("fimc_is_itf_map is fail(%d)", vctx, ret); + kfree(planes); + kfree(buf); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + goto p_err; + } + + minfo("[ISP:V] buffer%d.plane%d mapping\n", vctx, buf->index, plane); + set_bit(FRAME_MAP_MEM, &frame->memory); + dma_buf_detach(dmabuf, attachment); + dma_buf_put(dmabuf); + kfree(planes); + kfree(buf); + } + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +static int fimc_is_isp_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *ischain = vctx->device; + + dbg_isp("%s\n", __func__); + + switch (ctrl->id) { + case V4L2_CID_IS_BDS_WIDTH: + ctrl->value = ischain->chain0_width; + break; + case V4L2_CID_IS_BDS_HEIGHT: + ctrl->value = ischain->chain0_height; + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int fimc_is_isp_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + int ret = 0; + struct v4l2_ext_control *ctrl; + + dbg_isp("%s\n", __func__); + + + ctrl = ctrls->controls; + + switch (ctrl->id) { + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +const struct v4l2_ioctl_ops fimc_is_isp_video_ioctl_ops = { + .vidioc_querycap = fimc_is_isp_video_querycap, + .vidioc_enum_fmt_vid_out_mplane = fimc_is_isp_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_is_isp_video_get_format_mplane, + .vidioc_s_fmt_vid_out_mplane = fimc_is_isp_video_set_format_mplane, + .vidioc_cropcap = fimc_is_isp_video_cropcap, + .vidioc_g_crop = fimc_is_isp_video_get_crop, + .vidioc_s_crop = fimc_is_isp_video_set_crop, + .vidioc_reqbufs = fimc_is_isp_video_reqbufs, + .vidioc_querybuf = fimc_is_isp_video_querybuf, + .vidioc_qbuf = fimc_is_isp_video_qbuf, + .vidioc_dqbuf = fimc_is_isp_video_dqbuf, + .vidioc_streamon = fimc_is_isp_video_streamon, + .vidioc_streamoff = fimc_is_isp_video_streamoff, + .vidioc_enum_input = fimc_is_isp_video_enum_input, + .vidioc_g_input = fimc_is_isp_video_g_input, + .vidioc_s_input = fimc_is_isp_video_s_input, + .vidioc_s_ctrl = fimc_is_isp_video_s_ctrl, + .vidioc_g_ctrl = fimc_is_isp_video_g_ctrl, + .vidioc_g_ext_ctrls = fimc_is_isp_video_g_ext_ctrl, +}; + +static int fimc_is_isp_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_isp("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_buffer_prepare(struct vb2_buffer *vb) +{ + /*dbg_isp("%s\n", __func__);*/ + return 0; +} + +static inline void fimc_is_isp_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_isp_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_isp_start_streaming(struct vb2_queue *vbq, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_isp("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + device = vctx->device; + leader = &device->group_isp.leader; + + ret = fimc_is_queue_start_streaming(queue, device, leader, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_isp_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_isp("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + leader = &device->group_isp.leader; + + ret = fimc_is_queue_stop_streaming(queue, device, leader, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static void fimc_is_isp_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + u32 index; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_video *video; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + index = vb->v4l2_buf.index; + +#ifdef DBG_STREAMING + mdbgv_isp("%s(%d)\n", vctx, __func__, index); +#endif + + queue = GET_SRC_QUEUE(vctx); + video = vctx->video; + device = vctx->device; + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_ischain_isp_buffer_queue(device, queue, index); + if (ret) { + merr("fimc_is_ischain_isp_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_isp_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *device = vctx->device; + +#ifdef DBG_STREAMING + mdbgv_isp("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + ret = fimc_is_ischain_isp_buffer_finish(device, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_isp_qops = { + .queue_setup = fimc_is_isp_queue_setup, + .buf_prepare = fimc_is_isp_buffer_prepare, + .buf_queue = fimc_is_isp_buffer_queue, + .buf_finish = fimc_is_isp_buffer_finish, + .wait_prepare = fimc_is_isp_wait_prepare, + .wait_finish = fimc_is_isp_wait_finish, + .start_streaming = fimc_is_isp_start_streaming, + .stop_streaming = fimc_is_isp_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-scc.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-scc.c new file mode 100644 index 000000000000..ebe1a0191a42 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-scc.c @@ -0,0 +1,701 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +const struct v4l2_file_operations fimc_is_scc_video_fops; +const struct v4l2_ioctl_ops fimc_is_scc_video_ioctl_ops; +const struct vb2_ops fimc_is_scc_qops; + +int fimc_is_scc_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_scc; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_SCC_NAME, + FIMC_IS_VIDEO_SCC_NUM, + VFL_DIR_RX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_scc_video_fops, + &fimc_is_scc_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_scc_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + core = container_of(video, struct fimc_is_core, video_scc); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_SCC); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[SCC:V:%d] %s\n", vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_SCC_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_CAPTURE, + &fimc_is_scc_qops, + NULL, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_subdev_open(&device->scc, vctx, NULL); + if (ret) { + err("fimc_is_subdev_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_scc_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video = NULL; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_ischain *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + merr("video is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + info("[SCC:V:%d] %s\n", vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_subdev_close(&device->scc); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_scc_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + + return ret; +} + +static int fimc_is_scc_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_scc_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_scc_video_open, + .release = fimc_is_scc_video_close, + .poll = fimc_is_scc_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_scc_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_scc_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_is_core *isp = video_drvdata(file); + + strncpy(cap->driver, isp->pdev->name, sizeof(cap->driver) - 1); + + dbg("(devname : %s)\n", cap->driver); + strncpy(cap->card, isp->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + + return 0; +} + +static int fimc_is_scc_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scc("%s\n", vctx, __func__); + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_try_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_scc("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + subdev = &device->scc; + leader = subdev->leader; + + if (!leader) { + merr("leader is NULL ptr", vctx); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("leader still running, not applied", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_scc_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_scc("%s(index : %d)\n", vctx, __func__, buf->index); +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_scc("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scc("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scc("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo : add to enum input control code */ + return 0; +} + +static int fimc_is_scc_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scc_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + return 0; +} + +static int fimc_is_scc_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_framemgr *framemgr = GET_DST_FRAMEMGR(vctx); + + dbg_scc("%s\n", __func__); + + switch (ctrl->id) { + case V4L2_CID_IS_G_COMPLETES: + ctrl->value = framemgr->frame_com_cnt; + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int fimc_is_scc_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + mdbgv_scc("%s\n", vctx, __func__); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + framemgr = GET_DST_FRAMEMGR(vctx); + frame = NULL; + + switch (ctrl->id) { + case V4L2_CID_IS_FORCE_DONE: + if (framemgr->frame_pro_cnt) { + err("force done can be performed(process count %d)", + framemgr->frame_pro_cnt); + ret = -EINVAL; + } else if (!framemgr->frame_req_cnt) { + err("force done can be performed(request count %d)", + framemgr->frame_req_cnt); + ret = -EINVAL; + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + fimc_is_frame_trans_req_to_com(framemgr, frame); + buffer_done(vctx, frame->index); + } else { + err("frame is NULL"); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + break; + case V4L2_CID_IS_COLOR_RANGE: + if (test_bit(FIMC_IS_SUBDEV_START, &device->group_isp.leader.state)) { + err("failed to change color range: device started already (0x%08x)", + ctrl->value); + ret = -EINVAL; + } else { + device->color_range &= ~FIMC_IS_SCC_CRANGE_MASK; + + if (ctrl->value) + device->color_range |= + (FIMC_IS_CRANGE_LIMITED << FIMC_IS_SCC_CRANGE_SHIFT); + } + break; + default: + merr("unsupported ioctl(%d)\n", vctx, ctrl->id); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +const struct v4l2_ioctl_ops fimc_is_scc_video_ioctl_ops = { + .vidioc_querycap = fimc_is_scc_video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_scc_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_scc_video_get_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_scc_video_set_format_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_is_scc_video_try_format_mplane, + .vidioc_cropcap = fimc_is_scc_video_cropcap, + .vidioc_g_crop = fimc_is_scc_video_get_crop, + .vidioc_s_crop = fimc_is_scc_video_set_crop, + .vidioc_reqbufs = fimc_is_scc_video_reqbufs, + .vidioc_querybuf = fimc_is_scc_video_querybuf, + .vidioc_qbuf = fimc_is_scc_video_qbuf, + .vidioc_dqbuf = fimc_is_scc_video_dqbuf, + .vidioc_streamon = fimc_is_scc_video_streamon, + .vidioc_streamoff = fimc_is_scc_video_streamoff, + .vidioc_enum_input = fimc_is_scc_video_enum_input, + .vidioc_g_input = fimc_is_scc_video_g_input, + .vidioc_s_input = fimc_is_scc_video_s_input, + .vidioc_g_ctrl = fimc_is_scc_video_g_ctrl, + .vidioc_s_ctrl = fimc_is_scc_video_s_ctrl, +}; + +static int fimc_is_scc_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_scc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} +static int fimc_is_scc_buffer_prepare(struct vb2_buffer *vb) +{ + return 0; +} + +static inline void fimc_is_scc_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_scc_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_scc_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_scc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + subdev = &device->scc; + + ret = fimc_is_queue_start_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scc_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + mdbgv_scc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + subdev = &device->scc; + + ret = fimc_is_queue_stop_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static void fimc_is_scc_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue = vctx->q_dst; + struct fimc_is_video *video = vctx->video; + struct fimc_is_device_ischain *ischain = vctx->device; + struct fimc_is_subdev *scc = &ischain->scc; + +#ifdef DBG_STREAMING + dbg_scc("%s\n", __func__); +#endif + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_subdev_buffer_queue(scc, vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_subdev_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_scc_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + mdbgv_scc("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + device = vctx->device; + subdev = &device->scc; + + ret = fimc_is_subdev_buffer_finish(subdev, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_scc_qops = { + .queue_setup = fimc_is_scc_queue_setup, + .buf_prepare = fimc_is_scc_buffer_prepare, + .buf_queue = fimc_is_scc_buffer_queue, + .buf_finish = fimc_is_scc_buffer_finish, + .wait_prepare = fimc_is_scc_wait_prepare, + .wait_finish = fimc_is_scc_wait_finish, + .start_streaming = fimc_is_scc_start_streaming, + .stop_streaming = fimc_is_scc_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-scp.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-scp.c new file mode 100644 index 000000000000..42600bb2c5b8 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-scp.c @@ -0,0 +1,762 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +const struct v4l2_file_operations fimc_is_scp_video_fops; +const struct v4l2_ioctl_ops fimc_is_scp_video_ioctl_ops; +const struct vb2_ops fimc_is_scp_qops; + +int fimc_is_scp_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_scp; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_SCP_NAME, + FIMC_IS_VIDEO_SCP_NUM, + VFL_DIR_RX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_scp_video_fops, + &fimc_is_scp_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_scp_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + core = container_of(video, struct fimc_is_core, video_scp); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_SCP); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[SCP:V:%d] %s\n", vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_SCP_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_CAPTURE, + &fimc_is_scp_qops, + NULL, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_subdev_open(&device->scp, vctx, NULL); + if (ret) { + err("fimc_is_subdev_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_scp_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video = NULL; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_ischain *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + merr("video is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + info("[SCP:V:%d] %s\n", vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_subdev_close(&device->scp); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_scp_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + + return ret; +} + +static int fimc_is_scp_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_scp_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_scp_video_open, + .release = fimc_is_scp_video_close, + .poll = fimc_is_scp_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_scp_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_scp_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_is_core *core = video_drvdata(file); + + strncpy(cap->driver, core->pdev->name, sizeof(cap->driver) - 1); + + dbg("%s(devname : %s)\n", __func__, cap->driver); + strncpy(cap->card, core->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + + return 0; +} + +static int fimc_is_scp_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *ischain; + + BUG_ON(!vctx); + BUG_ON(!vctx->device); + BUG_ON(!format); + + mdbgv_scp("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + ischain = vctx->device; + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) { + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + goto p_err; + } + + ret = fimc_is_ischain_scp_s_format(ischain, + queue->framecfg.format.pixelformat, + queue->framecfg.width, + queue->framecfg.height); + if (ret) + merr("fimc_is_ischain_scp_s_format is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_scp_video_try_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + + mdbgv_scp("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_ischain_scp_s_format(device, + queue->framecfg.format.pixelformat, + crop->c.width, + crop->c.height); + if (ret) + merr("fimc_is_ischain_scp_s_format is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_scp_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_scp("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + subdev = &device->scp; + leader = subdev->leader; + + if (!leader) { + merr("leader is NULL ptr", vctx); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("leader still running, not applied", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_scp_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scp("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_scp("%s(index : %d)\n", vctx, __func__, buf->index); +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_scp("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scp("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_scp("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo : add to enum input control code */ + return 0; +} + +static int fimc_is_scp_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + dbg("%s\n", __func__); + return 0; +} + +static int fimc_is_scp_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + return 0; +} + +static int fimc_is_scp_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_framemgr *framemgr; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + mdbgv_scp("%s\n", vctx, __func__); + + framemgr = GET_DST_FRAMEMGR(vctx); + + switch (ctrl->id) { + case V4L2_CID_IS_G_COMPLETES: + ctrl->value = framemgr->frame_com_cnt; + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int fimc_is_scp_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + return 0; +} + +static int fimc_is_scp_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + mdbgv_scp("%s\n", vctx, __func__); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + framemgr = GET_DST_FRAMEMGR(vctx); + frame = NULL; + + switch (ctrl->id) { + case V4L2_CID_IS_FORCE_DONE: + if (framemgr->frame_pro_cnt) { + err("force done can be performed(process count %d)", + framemgr->frame_pro_cnt); + ret = -EINVAL; + } else if (!framemgr->frame_req_cnt) { + err("force done can be performed(request count %d)", + framemgr->frame_req_cnt); + ret = -EINVAL; + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + fimc_is_frame_trans_req_to_com(framemgr, frame); + buffer_done(vctx, frame->index); + } else { + err("frame is NULL"); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + break; + case V4L2_CID_IS_COLOR_RANGE: + if (test_bit(FIMC_IS_SUBDEV_START, &device->group_isp.leader.state)) { + err("failed to change color range: device started already (0x%08x)", + ctrl->value); + ret = -EINVAL; + } else { + device->color_range &= ~FIMC_IS_SCP_CRANGE_MASK; + + if (ctrl->value) + device->color_range |= + (FIMC_IS_CRANGE_LIMITED << FIMC_IS_SCP_CRANGE_SHIFT); + } + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +const struct v4l2_ioctl_ops fimc_is_scp_video_ioctl_ops = { + .vidioc_querycap = fimc_is_scp_video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_scp_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_scp_video_get_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_scp_video_set_format_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_is_scp_video_try_format_mplane, + .vidioc_cropcap = fimc_is_scp_video_cropcap, + .vidioc_g_crop = fimc_is_scp_video_get_crop, + .vidioc_s_crop = fimc_is_scp_video_set_crop, + .vidioc_reqbufs = fimc_is_scp_video_reqbufs, + .vidioc_querybuf = fimc_is_scp_video_querybuf, + .vidioc_qbuf = fimc_is_scp_video_qbuf, + .vidioc_dqbuf = fimc_is_scp_video_dqbuf, + .vidioc_streamon = fimc_is_scp_video_streamon, + .vidioc_streamoff = fimc_is_scp_video_streamoff, + .vidioc_enum_input = fimc_is_scp_video_enum_input, + .vidioc_g_input = fimc_is_scp_video_g_input, + .vidioc_s_input = fimc_is_scp_video_s_input, + .vidioc_g_ctrl = fimc_is_scp_video_g_ctrl, + .vidioc_s_ctrl = fimc_is_scp_video_s_ctrl, + .vidioc_g_ext_ctrls = fimc_is_scp_video_g_ext_ctrl, +}; + +static int fimc_is_scp_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_scp("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_buffer_prepare(struct vb2_buffer *vb) +{ + return 0; +} + +static inline void fimc_is_scp_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_scp_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_scp_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_scp("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + subdev = &device->scp; + + ret = fimc_is_queue_start_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_scp_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_scp("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + subdev = &device->scp; + + ret = fimc_is_queue_stop_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static void fimc_is_scp_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_video *video; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + +#ifdef DBG_STREAMING + dbg_scp("%s\n", __func__); +#endif + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + device = vctx->device; + subdev = &device->scp; + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_subdev_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_scp_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *ischain = vctx->device; + struct fimc_is_subdev *scp = &ischain->scp; + +#ifdef DBG_STREAMING + dbg_scp("%s(%d)\n", __func__, vb->v4l2_buf.index); +#endif + + ret = fimc_is_subdev_buffer_finish(scp, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_scp_qops = { + .queue_setup = fimc_is_scp_queue_setup, + .buf_prepare = fimc_is_scp_buffer_prepare, + .buf_queue = fimc_is_scp_buffer_queue, + .buf_finish = fimc_is_scp_buffer_finish, + .wait_prepare = fimc_is_scp_wait_prepare, + .wait_finish = fimc_is_scp_wait_finish, + .start_streaming = fimc_is_scp_start_streaming, + .stop_streaming = fimc_is_scp_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-sensor.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-sensor.c new file mode 100644 index 000000000000..d7c7917feac3 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-sensor.c @@ -0,0 +1,871 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-device-sensor.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-metadata.h" + +const struct v4l2_file_operations fimc_is_sen_video_fops; +const struct v4l2_ioctl_ops fimc_is_sen_video_ioctl_ops; +const struct vb2_ops fimc_is_sen_qops; + +int fimc_is_sen_video_probe(void *data) +{ + int ret = 0; + char name[255]; + u32 number; + struct fimc_is_device_sensor *device; + struct fimc_is_video *video; + + BUG_ON(!data); + + device = (struct fimc_is_device_sensor *)data; + video = &device->video; + snprintf(name, sizeof(name), "%s%d", FIMC_IS_VIDEO_SENSOR_NAME, device->instance); + number = FIMC_IS_VIDEO_SS0_NUM + device->instance; + + if (!device->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + name, + number, + VFL_DIR_RX, + &device->mem, + &device->v4l2_dev, + &video->lock, + &fimc_is_sen_video_fops, + &fimc_is_sen_video_ioctl_ops); + if (ret) + dev_err(&device->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +/* + * ============================================================================= + * Video File Opertation + * ============================================================================= + */ + +static int fimc_is_sen_video_open(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_sensor *device; + + vctx = NULL; + video = video_drvdata(file); + device = container_of(video, struct fimc_is_device_sensor, video); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_SENSOR); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[SS%d:V:%d] %s\n", video->id, vctx->instance, __func__); + + ret = fimc_is_video_open(vctx, + device, + VIDEO_SENSOR_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_CAPTURE, + &fimc_is_sen_qops, + NULL, + NULL); + if (ret) { + merr("fimc_is_video_open is fail(%d)", vctx, ret); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_sensor_open(device, vctx); + if (ret) { + merr("fimc_is_sen_open is fail(%d)", vctx, ret); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_sen_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video = NULL; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_sensor *device = NULL; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + merr("video is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + info("[SS0:V:%d] %s\n", vctx->instance, __func__); + + device = vctx->device; + if (!device) { + merr("device is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_close(device); + if (ret) + err("fimc_is_sensor_close is fail(%d)", ret); + + ret = fimc_is_video_close(vctx); + if (ret) + err("fimc_is_video_close is fail(%d)", ret); + + ret = close_vctx(file, video, vctx); + if (ret) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_sen_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + + return ret; +} + +static int fimc_is_sen_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_sen_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_sen_video_open, + .release = fimc_is_sen_video_close, + .poll = fimc_is_sen_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_sen_video_mmap, +}; + +/* + * ============================================================================= + * Video Ioctl Opertation + * ============================================================================= + */ + +static int fimc_is_sen_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + /* Todo : add to query capability code */ + return 0; +} + +static int fimc_is_sen_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + /* Todo : add to enumerate format code */ + return 0; +} + +static int fimc_is_sen_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + /* Todo : add to get format code */ + return 0; +} + +static int fimc_is_sen_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + struct fimc_is_device_sensor *device; + + BUG_ON(!vctx); + + mdbgv_sensor("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) { + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + goto p_err; + } + + ret = fimc_is_sensor_s_format(device, + &queue->framecfg.format, + queue->framecfg.width, + queue->framecfg.height); + if (ret) { + merr("fimc_is_sensor_s_format is fail(%d)", vctx, ret); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_sen_video_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + /* Todo : add to crop capability code */ + return 0; +} + +static int fimc_is_sen_video_get_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + /* Todo : add to get crop control code */ + return 0; +} + +static int fimc_is_sen_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *sensor; + + BUG_ON(!vctx); + + mdbgv_sensor("%s\n", vctx, __func__); + + sensor = vctx->device; + BUG_ON(!sensor); + + fimc_is_sensor_s_format(sensor, &sensor->image.format, crop->c.width, crop->c.height); + + return 0; +} + +static int fimc_is_sen_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + BUG_ON(!vctx); + + mdbgv_sensor("%s(buffers : %d)\n", vctx, __func__, buf->count); + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(error %d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_sensor("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + /*dbg_sensor("%s\n", __func__);*/ +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + bool blocking; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_sensor("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) { + blocking = file->f_flags & O_NONBLOCK; + if (!blocking || (ret != -EAGAIN)) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + } + + return ret; +} + +static int fimc_is_sen_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_sensor("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_sensor("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo: add to enumerate input code */ + info("%s is calld\n", __func__); + return 0; +} + +static int fimc_is_sen_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + /* Todo: add to get input control code */ + return 0; +} + +static int fimc_is_sen_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + int ret = 0; + u32 scenario; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *device; + struct fimc_is_framemgr *framemgr; + + BUG_ON(!vctx); + + mdbgv_sensor("%s(input : %08X)\n", vctx, __func__, input); + + device = vctx->device; + framemgr = GET_DST_FRAMEMGR(vctx); + scenario = (input & SENSOR_SCENARIO_MASK) >> SENSOR_SCENARIO_SHIFT; + input = (input & SENSOR_MODULE_MASK) >> SENSOR_MODULE_SHIFT; + + ret = fimc_is_sensor_s_input(device, input, scenario); + if (ret) { + merr("fimc_is_sensor_s_input is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_sen_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *device; + struct v4l2_subdev *subdev_flite; + + BUG_ON(!ctrl); + BUG_ON(!vctx); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + subdev_flite = device->subdev_flite; + if (!subdev_flite) { + err("subdev_flite is NULL"); + ret = -EINVAL; + goto p_err; + } + + switch (ctrl->id) { + case V4L2_CID_IS_S_STREAM: + { + u32 sstream, instant, noblock; + + sstream = (ctrl->value & SENSOR_SSTREAM_MASK) >> SENSOR_SSTREAM_SHIFT; + instant = (ctrl->value & SENSOR_INSTANT_MASK) >> SENSOR_INSTANT_SHIFT; + noblock = (ctrl->value & SENSOR_NOBLOCK_MASK) >> SENSOR_NOBLOCK_SHIFT; + /* + * nonblock(0) : blocking command + * nonblock(1) : non-blocking command + */ + + if (sstream == IS_ENABLE_STREAM) { + ret = fimc_is_sensor_front_start(device, instant, noblock); + if (ret) { + merr("fimc_is_sensor_front_start is fail(%d)", device, ret); + goto p_err; + } + } else { + ret = fimc_is_sensor_front_stop(device); + if (ret) { + merr("fimc_is_sensor_front_stop is fail(%d)", device, ret); + goto p_err; + } + } + } + break; + case V4L2_CID_IS_S_BNS: + if (device->pdata->is_bns == false) { + mwarn("Could not support BNS\n", device); + goto p_err; + } + + ret = fimc_is_sensor_s_bns(device, ctrl->value); + if (ret) { + merr("fimc_is_sensor_s_bns is fail(%d)", device, ret); + goto p_err; + } + + ret = v4l2_subdev_call(subdev_flite, core, s_ctrl, ctrl); + if (ret) { + merr("v4l2_flite_call(s_ctrl) is fail(%d)", device, ret); + goto p_err; + } + break; + /* + * gain boost: min_target_fps, max_target_fps, scene_mode + */ + case V4L2_CID_IS_MIN_TARGET_FPS: + if (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + err("failed to set min_target_fps: %d - sensor stream on already\n", + ctrl->value); + ret = -EINVAL; + } else { + device->min_target_fps = ctrl->value; + } + break; + case V4L2_CID_IS_MAX_TARGET_FPS: + if (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + err("failed to set max_target_fps: %d - sensor stream on already\n", + ctrl->value); + ret = -EINVAL; + } else { + device->max_target_fps = ctrl->value; + } + break; + case V4L2_CID_SCENEMODE: + if (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state)) { + err("failed to set scene_mode: %d - sensor stream on already\n", + ctrl->value); + ret = -EINVAL; + } else { + device->scene_mode = ctrl->value; + } + break; + case V4L2_CID_SENSOR_SET_FRAME_RATE: + if (fimc_is_sensor_s_frame_duration(device, ctrl->value)) { + err("failed to set frame duration : %d\n - %d", + ctrl->value, ret); + ret = -EINVAL; + } + break; + case V4L2_CID_SENSOR_SET_AE_TARGET: + if (fimc_is_sensor_s_exposure_time(device, ctrl->value)) { + err("failed to set exposure time : %d\n - %d", + ctrl->value, ret); + ret = -EINVAL; + } + break; + default: + ret = fimc_is_sensor_s_ctrl(device, ctrl); + if (ret) { + err("invalid ioctl(0x%08X) is requested", ctrl->id); + ret = -EINVAL; + goto p_err; + } + break; + } + +p_err: + return ret; +} + +static int fimc_is_sen_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *device; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + switch (ctrl->id) { + case V4L2_CID_IS_G_STREAM: + if (device->instant_ret) + ctrl->value = device->instant_ret; + else + ctrl->value = (test_bit(FIMC_IS_SENSOR_FRONT_START, &device->state) ? + IS_ENABLE_STREAM : IS_DISABLE_STREAM); + break; + case V4L2_CID_IS_G_BNS_SIZE: + { + u32 width, height; + + width = fimc_is_sensor_g_bns_width(device); + height = fimc_is_sensor_g_bns_height(device); + + ctrl->value = (width << 16) | height; + } + break; + case V4L2_CID_IS_G_DTPSTATUS: + if (test_bit(FIMC_IS_SENSOR_FRONT_DTP_STOP, &device->state)) + ctrl->value = 1; + else + ctrl->value = 0; + break; + default: + ret = fimc_is_sensor_g_ctrl(device, ctrl); + if (ret) { + err("invalid ioctl(0x%08X) is requested", ctrl->id); + ret = -EINVAL; + goto p_err; + } + break; + } + +p_err: + return ret; +} + +static int fimc_is_sen_video_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *device = vctx->device; + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tfp = &cp->timeperframe; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tfp->numerator = 1; + tfp->denominator = device->image.framerate; + + return 0; +} + +static int fimc_is_sen_video_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_sensor *device; + + BUG_ON(!vctx); + BUG_ON(!parm); + + mdbgv_sensor("%s\n", vctx, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_s_framerate(device, parm); + if (ret) { + merr("fimc_is_sen_s_framerate is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +const struct v4l2_ioctl_ops fimc_is_sen_video_ioctl_ops = { + .vidioc_querycap = fimc_is_sen_video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_sen_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_sen_video_get_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_sen_video_set_format_mplane, + .vidioc_cropcap = fimc_is_sen_video_cropcap, + .vidioc_g_crop = fimc_is_sen_video_get_crop, + .vidioc_s_crop = fimc_is_sen_video_set_crop, + .vidioc_reqbufs = fimc_is_sen_video_reqbufs, + .vidioc_querybuf = fimc_is_sen_video_querybuf, + .vidioc_qbuf = fimc_is_sen_video_qbuf, + .vidioc_dqbuf = fimc_is_sen_video_dqbuf, + .vidioc_streamon = fimc_is_sen_video_streamon, + .vidioc_streamoff = fimc_is_sen_video_streamoff, + .vidioc_enum_input = fimc_is_sen_video_enum_input, + .vidioc_g_input = fimc_is_sen_video_g_input, + .vidioc_s_input = fimc_is_sen_video_s_input, + .vidioc_s_ctrl = fimc_is_sen_video_s_ctrl, + .vidioc_g_ctrl = fimc_is_sen_video_g_ctrl, + .vidioc_g_parm = fimc_is_sen_video_g_parm, + .vidioc_s_parm = fimc_is_sen_video_s_parm, +}; + +static int fimc_is_sen_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_sensor("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_sen_buffer_prepare(struct vb2_buffer *vb) +{ + return 0; +} + +static inline void fimc_is_sen_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_sen_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_sen_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_sensor *device; + + BUG_ON(!vctx); + + mdbgv_sensor("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state) && + test_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state)) { + set_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + fimc_is_sensor_back_start(device); + } else { + err("already stream on or buffer is not ready(%ld)", + queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + fimc_is_sensor_back_stop(device); + ret = -EINVAL; + } + + return 0; +} + +static int fimc_is_sen_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_sensor *device; + + BUG_ON(!vctx); + + mdbgv_sensor("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + + if (test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + clear_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + ret = fimc_is_sensor_back_stop(device); + } else { + err("already stream off"); + ret = -EINVAL; + } + + return ret; +} + +static void fimc_is_sen_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_video *video; + struct fimc_is_device_sensor *device; + +#ifdef DBG_STREAMING + mdbgv_sensor("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + video = vctx->video; + if (!video) { + merr("video is NULL", device); + return; + } + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret); + return; + } + + ret = fimc_is_sensor_buffer_queue(device, vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_sensor_buffer_queue is fail(%d)", device, ret); + return; + } +} + +static int fimc_is_sen_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_sensor *device; + +#ifdef DBG_STREAMING + mdbgv_sensor("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + device = vctx->device; + + ret = fimc_is_sensor_buffer_finish( + device, + vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_sensor_buffer_finish is fail(%d)", device, ret); + goto p_err; + } + +p_err: + return ret; +} + +const struct vb2_ops fimc_is_sen_qops = { + .queue_setup = fimc_is_sen_queue_setup, + .buf_prepare = fimc_is_sen_buffer_prepare, + .buf_queue = fimc_is_sen_buffer_queue, + .buf_finish = fimc_is_sen_buffer_finish, + .wait_prepare = fimc_is_sen_wait_prepare, + .wait_finish = fimc_is_sen_wait_finish, + .start_streaming = fimc_is_sen_start_streaming, + .stop_streaming = fimc_is_sen_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdisc.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdisc.c new file mode 100644 index 000000000000..afe74ba41059 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdisc.c @@ -0,0 +1,675 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" + +const struct v4l2_file_operations fimc_is_vdc_video_fops; +const struct v4l2_ioctl_ops fimc_is_vdc_video_ioctl_ops; +const struct vb2_ops fimc_is_vdc_qops; + +int fimc_is_vdc_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_vdc; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_VDC_NAME, + FIMC_IS_VIDEO_VDC_NUM, + VFL_DIR_RX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_vdc_video_fops, + &fimc_is_vdc_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +static int fimc_is_vdc_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + core = container_of(video, struct fimc_is_core, video_vdc); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_INVALID, FRAMEMGR_ID_DIS); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[VDC:V:%d] %s\n", vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_VDISC_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_CAPTURE, + &fimc_is_vdc_qops, + NULL, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_subdev_open(&device->dis, vctx, NULL); + if (ret) { + err("fimc_is_subdev_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_vdc_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_device_ischain *device; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + merr("video is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + info("[VDC:V:%d] %s\n", vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_subdev_close(&device->dis); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_vdc_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + if (ret) + merr("fimc_is_video_poll is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_is_core *core = video_drvdata(file); + + dbg_vdisc("%s(devname : %s)\n", __func__, core->pdev->name); + + strncpy(cap->driver, core->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, core->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + + return 0; +} + +static int fimc_is_vdc_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + /* Todo: add enum format control code */ + return 0; +} + +static int fimc_is_vdc_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + /* Todo: add get format control code */ + return 0; +} + +static int fimc_is_vdc_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue = vctx->q_dst; + struct fimc_is_device_ischain *ischain = vctx->device; + + mdbgv_vdc("%s\n", vctx, __func__); + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + + dbg_vdisc("req w : %d req h : %d\n", + queue->framecfg.width, + queue->framecfg.height); + + fimc_is_ischain_vdc_s_format(ischain, + queue->framecfg.width, + queue->framecfg.height); + + return ret; +} + +static int fimc_is_vdc_video_set_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *ischain; + + BUG_ON(!vctx); + + mdbgv_vdc("%s\n", vctx, __func__); + + ischain = vctx->device; + BUG_ON(!ischain); + + fimc_is_ischain_vdc_s_format(ischain, + crop->c.width, crop->c.height); + + return 0; +} + +static int fimc_is_vdc_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_vdc("%s(buffers : %d)\n", vctx, __func__, buf->count); + + device = vctx->device; + subdev = &device->dis; + leader = subdev->leader; + + if (!leader) { + merr("leader is NULL ptr", vctx); + ret = -EINVAL; + goto p_err; + } + + if (test_bit(FIMC_IS_SUBDEV_START, &leader->state)) { + merr("leader still running, not applied", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_vdc_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdc("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_vdc("%s(index : %d)\n", vctx, __func__, buf->index); +#endif + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_vdc("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdc("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdc("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo : add to enum input control code */ + return 0; +} + +static int fimc_is_vdc_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + /* Todo: add get input control code */ + return 0; +} + +static int fimc_is_vdc_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + /* Todo: add set input control code */ + return 0; +} + +static int fimc_is_vdc_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_framemgr *framemgr = &vctx->q_dst->framemgr; + + dbg_vdisc("%s\n", __func__); + + switch (ctrl->id) { + case V4L2_CID_IS_G_COMPLETES: + ctrl->value = framemgr->frame_com_cnt; + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int fimc_is_vdc_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + /* Todo: add get extra control code */ + return 0; +} + +static int fimc_is_vdc_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int ret = 0; + unsigned long flags; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + BUG_ON(!vctx); + BUG_ON(!ctrl); + + dbg_vdisc("%s\n", __func__); + + framemgr = GET_DST_FRAMEMGR(vctx); + + switch (ctrl->id) { + case V4L2_CID_IS_FORCE_DONE: + if (framemgr->frame_pro_cnt) { + err("force done can be performed(process count %d)", + framemgr->frame_pro_cnt); + ret = -EINVAL; + } else if (!framemgr->frame_req_cnt) { + err("force done can be performed(request count %d)", + framemgr->frame_req_cnt); + ret = -EINVAL; + } else { + framemgr_e_barrier_irqs(framemgr, 0, flags); + + fimc_is_frame_request_head(framemgr, &frame); + if (frame) { + fimc_is_frame_trans_req_to_com(framemgr, frame); + buffer_done(vctx, frame->index); + } else { + err("frame is NULL"); + ret = -EINVAL; + } + + framemgr_x_barrier_irqr(framemgr, 0, flags); + } + break; + default: + err("unsupported ioctl(%d)\n", ctrl->id); + ret = -EINVAL; + break; + } + + return ret; +} + +const struct v4l2_file_operations fimc_is_vdc_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_vdc_video_open, + .release = fimc_is_vdc_video_close, + .poll = fimc_is_vdc_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_vdc_video_mmap, +}; + +const struct v4l2_ioctl_ops fimc_is_vdc_video_ioctl_ops = { + .vidioc_querycap = fimc_is_vdc_video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_is_vdc_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_is_vdc_video_get_format_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_is_vdc_video_set_format_mplane, + .vidioc_s_crop = fimc_is_vdc_video_set_crop, + .vidioc_reqbufs = fimc_is_vdc_video_reqbufs, + .vidioc_querybuf = fimc_is_vdc_video_querybuf, + .vidioc_qbuf = fimc_is_vdc_video_qbuf, + .vidioc_dqbuf = fimc_is_vdc_video_dqbuf, + .vidioc_streamon = fimc_is_vdc_video_streamon, + .vidioc_streamoff = fimc_is_vdc_video_streamoff, + .vidioc_enum_input = fimc_is_vdc_video_enum_input, + .vidioc_g_input = fimc_is_vdc_video_g_input, + .vidioc_s_input = fimc_is_vdc_video_s_input, + .vidioc_g_ctrl = fimc_is_vdc_video_g_ctrl, + .vidioc_s_ctrl = fimc_is_vdc_video_s_ctrl, + .vidioc_g_ext_ctrls = fimc_is_vdc_video_g_ext_ctrl, +}; + +static int fimc_is_vdc_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_vdc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_buffer_prepare(struct vb2_buffer *vb) +{ + /* Todo: add buffer prepare control code */ + return 0; +} + +static inline void fimc_is_vdc_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_vdc_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_vdc_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_vdc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + subdev = &device->dis; + + ret = fimc_is_queue_start_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdc_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *subdev; + + BUG_ON(!vctx); + + mdbgv_vdc("%s\n", vctx, __func__); + + queue = GET_DST_QUEUE(vctx); + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + subdev = &device->dis; + + ret = fimc_is_queue_stop_streaming(queue, device, subdev, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static void fimc_is_vdc_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue = vctx->q_dst; + struct fimc_is_video *video = vctx->video; + struct fimc_is_device_ischain *ischain = vctx->device; + struct fimc_is_subdev *subdev = &ischain->dis; + +#ifdef DBG_STREAMING + dbg_vdisc("%s\n", __func__); +#endif + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index); + if (ret) { + merr("fimc_is_subdev_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_vdc_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *ischain = vctx->device; + struct fimc_is_subdev *dis = &ischain->dis; + +#ifdef DBG_STREAMING + dbg_vdisc("%s(%d)\n", __func__, vb->v4l2_buf.index); +#endif + + ret = fimc_is_subdev_buffer_finish(dis, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_vdc_qops = { + .queue_setup = fimc_is_vdc_queue_setup, + .buf_prepare = fimc_is_vdc_buffer_prepare, + .buf_queue = fimc_is_vdc_buffer_queue, + .buf_finish = fimc_is_vdc_buffer_finish, + .wait_prepare = fimc_is_vdc_wait_prepare, + .wait_finish = fimc_is_vdc_wait_finish, + .start_streaming = fimc_is_vdc_start_streaming, + .stop_streaming = fimc_is_vdc_stop_streaming, +}; diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdiso.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdiso.c new file mode 100644 index 000000000000..3270f820bd4d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video-vdiso.c @@ -0,0 +1,586 @@ +/* + * Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" +#include "fimc-is-video.h" +#include "fimc-is-metadata.h" + +const struct v4l2_file_operations fimc_is_vdo_video_fops; +const struct v4l2_ioctl_ops fimc_is_vdo_video_ioctl_ops; +const struct vb2_ops fimc_is_vdo_qops; + +int fimc_is_vdo_video_probe(void *data) +{ + int ret = 0; + struct fimc_is_core *core; + struct fimc_is_video *video; + + BUG_ON(!data); + + core = (struct fimc_is_core *)data; + video = &core->video_vdo; + + if (!core->pdev) { + err("pdev is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_probe(video, + FIMC_IS_VIDEO_VDO_NAME, + FIMC_IS_VIDEO_VDO_NUM, + VFL_DIR_TX, + &core->mem, + &core->v4l2_dev, + &video->lock, + &fimc_is_vdo_video_fops, + &fimc_is_vdo_video_ioctl_ops); + if (ret) + dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret); + +p_err: + return ret; +} + +static int fimc_is_vdo_video_open(struct file *file) +{ + int ret = 0; + u32 refcount; + struct fimc_is_core *core; + struct fimc_is_video *video; + struct fimc_is_video_ctx *vctx; + struct fimc_is_device_ischain *device; + + vctx = NULL; + video = video_drvdata(file); + core = container_of(video, struct fimc_is_core, video_vdo); + + ret = open_vctx(file, video, &vctx, FRAMEMGR_ID_DIS_GRP, FRAMEMGR_ID_INVALID); + if (ret) { + err("open_vctx is fail(%d)", ret); + goto p_err; + } + + info("[VDO:V:%d] %s\n", vctx->instance, __func__); + + refcount = atomic_read(&core->video_isp.refcount); + if (refcount > FIMC_IS_MAX_NODES || refcount < 1) { + err("invalid ischain refcount(%d)", refcount); + close_vctx(file, video, vctx); + ret = -EINVAL; + goto p_err; + } + + device = &core->ischain[refcount - 1]; + + ret = fimc_is_video_open(vctx, + device, + VIDEO_VDISO_READY_BUFFERS, + video, + FIMC_IS_VIDEO_TYPE_OUTPUT, + &fimc_is_vdo_qops, + &fimc_is_ischain_vdo_ops, + &fimc_is_ischain_sub_ops); + if (ret) { + err("fimc_is_video_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + + ret = fimc_is_ischain_vdo_open(device, vctx); + if (ret) { + err("fimc_is_ischain_vdo_open is fail"); + close_vctx(file, video, vctx); + goto p_err; + } + +p_err: + return ret; +} + +static int fimc_is_vdo_video_close(struct file *file) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = NULL; + struct fimc_is_video *video; + struct fimc_is_device_ischain *device; + + BUG_ON(!file); + + vctx = file->private_data; + if (!vctx) { + err("vctx is NULL"); + ret = -EINVAL; + goto p_err; + } + + video = vctx->video; + if (!video) { + err("video is NULL"); + ret = -EINVAL; + goto p_err; + } + + info("[VDO:V:%d] %s\n", vctx->instance, __func__); + + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + + fimc_is_ischain_vdo_close(device, vctx); + fimc_is_video_close(vctx); + + ret = close_vctx(file, video, vctx); + if (ret < 0) + err("close_vctx is fail(%d)", ret); + +p_err: + return ret; +} + +static unsigned int fimc_is_vdo_video_poll(struct file *file, + struct poll_table_struct *wait) +{ + u32 ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_poll(file, vctx, wait); + if (ret) + merr("fimc_is_video_poll is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_mmap(struct file *file, + struct vm_area_struct *vma) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + ret = fimc_is_video_mmap(file, vctx, vma); + if (ret) + merr("fimc_is_video_mmap is fail(%d)", vctx, ret); + + return ret; +} + +const struct v4l2_file_operations fimc_is_vdo_video_fops = { + .owner = THIS_MODULE, + .open = fimc_is_vdo_video_open, + .release = fimc_is_vdo_video_close, + .poll = fimc_is_vdo_video_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_is_vdo_video_mmap, +}; + +static int fimc_is_vdo_video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_is_core *core = video_drvdata(file); + + strncpy(cap->driver, core->pdev->name, sizeof(cap->driver) - 1); + + dbg_vdiso("%s(devname : %s)\n", __func__, cap->driver); + strncpy(cap->card, core->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + + return 0; +} + +static int fimc_is_vdo_video_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + /* Todo: add enum format control code */ + return 0; +} + +static int fimc_is_vdo_video_get_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + /* Todo: add get format control code */ + return 0; +} + +static int fimc_is_vdo_video_set_format_mplane(struct file *file, void *fh, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s\n", vctx, __func__); + + ret = fimc_is_video_set_format_mplane(file, vctx, format); + if (ret) + merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret); + + dbg_vdiso("req w : %d req h : %d\n", + vctx->q_src->framecfg.width, + vctx->q_src->framecfg.height); + + return ret; +} + +static int fimc_is_vdo_video_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s(buffers : %d)\n", vctx, __func__, buf->count); + + ret = fimc_is_video_reqbufs(file, vctx, buf); + if (ret) + merr("fimc_is_video_reqbufs is fail(error %d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s\n", vctx, __func__); + + ret = fimc_is_video_querybuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_querybuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + struct fimc_is_queue *queue; + +#ifdef DBG_STREAMING + dbg_vdiso("%s\n", __func__); +#endif + + queue = GET_SRC_QUEUE(vctx); + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + merr("stream off state, can NOT qbuf", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_video_qbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_qbuf is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static int fimc_is_vdo_video_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + +#ifdef DBG_STREAMING + mdbgv_vdo("%s\n", vctx, __func__); +#endif + + ret = fimc_is_video_dqbuf(file, vctx, buf); + if (ret) + merr("fimc_is_video_dqbuf is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s\n", vctx, __func__); + + ret = fimc_is_video_streamon(file, vctx, type); + if (ret) + merr("fimc_is_vdo_video_streamon is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = file->private_data; + + mdbgv_vdo("%s\n", vctx, __func__); + + ret = fimc_is_video_streamoff(file, vctx, type); + if (ret) + merr("fimc_is_video_streamoff is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_video_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + /* Todo : add to enum input control code */ + return 0; +} + +static int fimc_is_vdo_video_g_input(struct file *file, void *priv, + unsigned int *input) +{ + /* Todo: add get input control code */ + return 0; +} + +static int fimc_is_vdo_video_s_input(struct file *file, void *priv, + unsigned int input) +{ + /* Todo: add set input control code */ + return 0; +} + +static int fimc_is_vdo_video_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + /* Todo: add set control code */ + return 0; +} + +static int fimc_is_vdo_video_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + /* Todo: add get control code */ + return 0; +} + +static int fimc_is_vdo_video_g_ext_ctrl(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + /* Todo: add get extra control code */ + return 0; +} + +const struct v4l2_ioctl_ops fimc_is_vdo_video_ioctl_ops = { + .vidioc_querycap = fimc_is_vdo_video_querycap, + .vidioc_enum_fmt_vid_out_mplane = fimc_is_vdo_video_enum_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_is_vdo_video_get_format_mplane, + .vidioc_s_fmt_vid_out_mplane = fimc_is_vdo_video_set_format_mplane, + .vidioc_reqbufs = fimc_is_vdo_video_reqbufs, + .vidioc_querybuf = fimc_is_vdo_video_querybuf, + .vidioc_qbuf = fimc_is_vdo_video_qbuf, + .vidioc_dqbuf = fimc_is_vdo_video_dqbuf, + .vidioc_streamon = fimc_is_vdo_video_streamon, + .vidioc_streamoff = fimc_is_vdo_video_streamoff, + .vidioc_enum_input = fimc_is_vdo_video_enum_input, + .vidioc_g_input = fimc_is_vdo_video_g_input, + .vidioc_s_input = fimc_is_vdo_video_s_input, + .vidioc_s_ctrl = fimc_is_vdo_video_s_ctrl, + .vidioc_g_ctrl = fimc_is_vdo_video_g_ctrl, + .vidioc_g_ext_ctrls = fimc_is_vdo_video_g_ext_ctrl, +}; + +static int fimc_is_vdo_queue_setup(struct vb2_queue *vbq, + const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vbq->drv_priv; + struct fimc_is_video *video; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + + mdbgv_isp("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + video = vctx->video; + + ret = fimc_is_queue_setup(queue, + video->alloc_ctx, + num_planes, + sizes, + allocators); + if (ret) + merr("fimc_is_queue_setup is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_buffer_prepare(struct vb2_buffer *vb) +{ + /* Todo: add buffer prepare control code */ + return 0; +} + +static inline void fimc_is_vdo_wait_prepare(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_prepare(vbq); +} + +static inline void fimc_is_vdo_wait_finish(struct vb2_queue *vbq) +{ + fimc_is_queue_wait_finish(vbq); +} + +static int fimc_is_vdo_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_vdo("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + device = vctx->device; + leader = &device->group_dis.leader; + + ret = fimc_is_queue_start_streaming(queue, device, leader, vctx); + if (ret) + merr("fimc_is_queue_start_streaming is fail(%d)", vctx, ret); + + return ret; +} + +static int fimc_is_vdo_stop_streaming(struct vb2_queue *q) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = q->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_device_ischain *device; + struct fimc_is_subdev *leader; + + BUG_ON(!vctx); + + mdbgv_vdo("%s\n", vctx, __func__); + + queue = GET_SRC_QUEUE(vctx); + device = vctx->device; + if (!device) { + err("device is NULL"); + ret = -EINVAL; + goto p_err; + } + leader = &device->group_dis.leader; + + ret = fimc_is_queue_stop_streaming(queue, device, leader, vctx); + if (ret) + merr("fimc_is_queue_stop_streaming is fail(%d)", vctx, ret); + +p_err: + return ret; +} + +static void fimc_is_vdo_buffer_queue(struct vb2_buffer *vb) +{ + int ret = 0; + u32 index; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_queue *queue; + struct fimc_is_video *video; + struct fimc_is_device_ischain *device; + + BUG_ON(!vctx); + index = vb->v4l2_buf.index; + +#ifdef DBG_STREAMING + dbg_vdiso("%s(%d)\n", __func__, index); +#endif + + queue = GET_SRC_QUEUE(vctx); + video = vctx->video; + device = vctx->device; + + ret = fimc_is_queue_buffer_queue(queue, video->vb2, vb); + if (ret) { + merr("fimc_is_queue_buffer_queue is fail(%d)", vctx, ret); + return; + } + + ret = fimc_is_ischain_vdo_buffer_queue(device, queue, index); + if (ret) { + merr("fimc_is_ischain_vdo_buffer_queue is fail(%d)", vctx, ret); + return; + } +} + +static int fimc_is_vdo_buffer_finish(struct vb2_buffer *vb) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv; + struct fimc_is_device_ischain *device = vctx->device; + +#ifdef DBG_STREAMING + mdbgv_vdo("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index); +#endif + + ret = fimc_is_ischain_vdo_buffer_finish(device, vb->v4l2_buf.index); + + return ret; +} + +const struct vb2_ops fimc_is_vdo_qops = { + .queue_setup = fimc_is_vdo_queue_setup, + .buf_prepare = fimc_is_vdo_buffer_prepare, + .buf_queue = fimc_is_vdo_buffer_queue, + .buf_finish = fimc_is_vdo_buffer_finish, + .wait_prepare = fimc_is_vdo_wait_prepare, + .wait_finish = fimc_is_vdo_wait_finish, + .start_streaming = fimc_is_vdo_start_streaming, + .stop_streaming = fimc_is_vdo_stop_streaming, +}; + diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video.c b/drivers/media/platform/exynos/fimc-is/fimc-is-video.c new file mode 100644 index 000000000000..70a110018b7e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video.c @@ -0,0 +1,1372 @@ +/* +* Samsung Exynos5 SoC series FIMC-IS driver + * + * exynos5 fimc-is video functions + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "fimc-is-time.h" +#include "fimc-is-core.h" +#include "fimc-is-param.h" +#include "fimc-is-cmd.h" +#include "fimc-is-regs.h" +#include "fimc-is-err.h" + +#define SPARE_PLANE 1 +#define SPARE_SIZE (32 * 1024) + +struct fimc_is_fmt fimc_is_formats[] = { + { + .name = "YUV 4:2:2 packed, YCbYCr", + .pixelformat = V4L2_PIX_FMT_YUYV, + .num_planes = 1 + SPARE_PLANE, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .num_planes = 1 + SPARE_PLANE, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + }, { + .name = "YUV 4:2:2 planar, Y/Cb/Cr", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 planar, YCbCr", + .pixelformat = V4L2_PIX_FMT_YUV420, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 planar, YCbCr", + .pixelformat = V4L2_PIX_FMT_YVU420, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 planar, Y/CbCr", + .pixelformat = V4L2_PIX_FMT_NV12, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 planar, Y/CrCb", + .pixelformat = V4L2_PIX_FMT_NV21, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", + .pixelformat = V4L2_PIX_FMT_NV12M, + .num_planes = 2 + SPARE_PLANE, + }, { + .name = "YVU 4:2:0 non-contiguous 2-planar, Y/CrCb", + .pixelformat = V4L2_PIX_FMT_NV21M, + .num_planes = 2 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", + .pixelformat = V4L2_PIX_FMT_YUV420M, + .num_planes = 3 + SPARE_PLANE, + }, { + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb", + .pixelformat = V4L2_PIX_FMT_YVU420M, + .num_planes = 3 + SPARE_PLANE, + }, { + .name = "BAYER 8 bit", + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "BAYER 10 bit", + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "BAYER 12 bit", + .pixelformat = V4L2_PIX_FMT_SBGGR12, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "BAYER 16 bit", + .pixelformat = V4L2_PIX_FMT_SBGGR16, + .num_planes = 1 + SPARE_PLANE, + }, { + .name = "JPEG", + .pixelformat = V4L2_PIX_FMT_JPEG, + .num_planes = 1 + SPARE_PLANE, + .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, + } +}; + +struct fimc_is_fmt *fimc_is_find_format(u32 *pixelformat, + u32 *mbus_code, int index) +{ + struct fimc_is_fmt *fmt, *def_fmt = NULL; + unsigned int i; + + if (index >= ARRAY_SIZE(fimc_is_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(fimc_is_formats); ++i) { + fmt = &fimc_is_formats[i]; + if (pixelformat && fmt->pixelformat == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == i) + def_fmt = fmt; + } + return def_fmt; + +} + +int get_plane_size_flite(int width, int height) +{ + int PlaneSize; + int Alligned_Width; + int Bytes; + + Alligned_Width = (width + 9) / 10 * 10; + Bytes = Alligned_Width * 8 / 5 ; + + PlaneSize = Bytes * height; + + return PlaneSize; +} + +void fimc_is_set_plane_size(struct fimc_is_frame_cfg *frame, unsigned int sizes[]) +{ + u32 plane; + u32 width[FIMC_IS_MAX_PLANES]; + + for (plane = 0; plane < FIMC_IS_MAX_PLANES; ++plane) + width[plane] = frame->width + frame->width_stride[plane]; + + switch (frame->format.pixelformat) { + case V4L2_PIX_FMT_YUYV: + dbg("V4L2_PIX_FMT_YUYV(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0]*frame->height*2; + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_NV12: + dbg("V4L2_PIX_FMT_NV12(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0] * frame->height * 3 / 2; + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_NV12M: + dbg("V4L2_PIX_FMT_NV12M(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0]*frame->height; + sizes[1] = width[1]*frame->height/2; + sizes[2] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_NV21: + dbg("V4L2_PIX_FMT_NV21(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0] * frame->height * 3 / 2; + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_NV21M: + dbg("V4L2_PIX_FMT_NV21M(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0]*frame->height; + sizes[1] = width[1]*frame->height/2; + sizes[2] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + dbg("V4L2_PIX_FMT_YVU420M(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = width[0]*frame->height; + sizes[1] = width[1]*frame->height/4; + sizes[2] = width[2]*frame->height/4; + sizes[3] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_SGRBG8: + dbg("V4L2_PIX_FMT_SGRBG8(w:%d)(h:%d)\n", frame->width, frame->height); + sizes[0] = frame->width*frame->height; + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_SBGGR10: + dbg("V4L2_PIX_FMT_SBGGR10(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = get_plane_size_flite(frame->width,frame->height); + if (frame->bytesperline[0]) { + if (frame->bytesperline[0] >= frame->width * 5 / 4) { + sizes[0] = frame->bytesperline[0] + * frame->height; + } else { + err("Bytesperline too small\ + (fmt(V4L2_PIX_FMT_SBGGR10), W(%d), Bytes(%d))", + frame->width, + frame->bytesperline[0]); + } + } + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_SBGGR16: + dbg("V4L2_PIX_FMT_SBGGR16(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = frame->width*frame->height*2; + if (frame->bytesperline[0]) { + if (frame->bytesperline[0] >= frame->width * 2) { + sizes[0] = frame->bytesperline[0] + * frame->height; + } else { + err("Bytesperline too small\ + (fmt(V4L2_PIX_FMT_SBGGR16), W(%d), Bytes(%d))", + frame->width, + frame->bytesperline[0]); + } + } + sizes[1] = SPARE_SIZE; + break; + case V4L2_PIX_FMT_SBGGR12: + dbg("V4L2_PIX_FMT_SBGGR12(w:%d)(h:%d)\n", + frame->width, frame->height); + sizes[0] = get_plane_size_flite(frame->width,frame->height); + if (frame->bytesperline[0]) { + if (frame->bytesperline[0] >= frame->width * 3 / 2) { + sizes[0] = frame->bytesperline[0] + * frame->height; + } else { + err("Bytesperline too small\ + (fmt(V4L2_PIX_FMT_SBGGR12), W(%d), Bytes(%d))", + frame->width, + frame->bytesperline[0]); + } + } + sizes[1] = SPARE_SIZE; + break; + default: + err("unknown pixelformat\n"); + break; + } +} + +static inline void vref_init(struct fimc_is_video *video) +{ + atomic_set(&video->refcount, 0); +} + +static inline int vref_get(struct fimc_is_video *video) +{ + return atomic_inc_return(&video->refcount) - 1; +} + +static inline int vref_put(struct fimc_is_video *video, + void (*release)(struct fimc_is_video *video)) +{ + int ret = 0; + + ret = atomic_sub_and_test(1, &video->refcount); + if (ret) + pr_debug("closed all instacne"); + + return atomic_read(&video->refcount); +} + +static int queue_init(void *priv, struct vb2_queue *vbq_src, + struct vb2_queue *vbq_dst) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx = priv; + + BUG_ON(!vctx); + + if (vctx->type == FIMC_IS_VIDEO_TYPE_OUTPUT) { + BUG_ON(!vbq_src); + + vbq_src->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + vbq_src->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vbq_src->drv_priv = vctx; + vbq_src->ops = vctx->vb2_ops; + vbq_src->mem_ops = vctx->mem_ops; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) + vbq_src->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +#endif + ret = vb2_queue_init(vbq_src); + if (ret) { + err("vb2_queue_init fail"); + goto p_err; + } + vctx->q_src->vbq = vbq_src; + } else if (vctx->type == FIMC_IS_VIDEO_TYPE_CAPTURE) { + BUG_ON(!vbq_dst); + + vbq_dst->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vbq_dst->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vbq_dst->drv_priv = vctx; + vbq_dst->ops = vctx->vb2_ops; + vbq_dst->mem_ops = vctx->mem_ops; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) + vbq_dst->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +#endif + ret = vb2_queue_init(vbq_dst); + if (ret) { + err("vb2_queue_init fail"); + goto p_err; + } + vctx->q_dst->vbq = vbq_dst; + } else if (vctx->type == FIMC_IS_VIDEO_TYPE_M2M) { + BUG_ON(!vbq_src); + BUG_ON(!vbq_dst); + + vbq_src->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + vbq_src->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vbq_src->drv_priv = vctx; + vbq_src->ops = vctx->vb2_ops; + vbq_src->mem_ops = vctx->mem_ops; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) + vbq_src->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +#endif + ret = vb2_queue_init(vbq_src); + if (ret) { + err("vb2_queue_init fail"); + goto p_err; + } + vctx->q_src->vbq = vbq_src; + + vbq_dst->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vbq_dst->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vbq_dst->drv_priv = vctx; + vbq_dst->ops = vctx->vb2_ops; + vbq_dst->mem_ops = vctx->mem_ops; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) + vbq_dst->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +#endif + ret = vb2_queue_init(vbq_dst); + if (ret) { + err("vb2_queue_init fail"); + goto p_err; + } + vctx->q_dst->vbq = vbq_dst; + } else { + merr("video type is invalid(%d)", vctx, vctx->type); + ret = -EINVAL; + } + +p_err: + return ret; +} + +int open_vctx(struct file *file, + struct fimc_is_video *video, + struct fimc_is_video_ctx **vctx, + u32 id_src, u32 id_dst) +{ + int ret = 0; + struct fimc_is_queue *q_src = NULL; + struct fimc_is_queue *q_dst = NULL; + + BUG_ON(!file); + BUG_ON(!video); + + if (atomic_read(&video->refcount) > FIMC_IS_MAX_NODES) { + err("can't open vctx, refcount is invalid"); + ret = -EINVAL; + goto exit; + } + + *vctx = kzalloc(sizeof(struct fimc_is_video_ctx), GFP_KERNEL); + if (*vctx == NULL) { + err("kzalloc is fail"); + ret = -ENOMEM; + *vctx = NULL; + goto exit; + } + + q_src = kzalloc(sizeof(struct fimc_is_queue), GFP_KERNEL); + if (q_src == NULL) { + err("kzalloc is fail(q_src)"); + ret = -ENOMEM; + kfree(*vctx); + *vctx = NULL; + goto exit; + } + + q_dst = kzalloc(sizeof(struct fimc_is_queue), GFP_KERNEL); + if (q_dst == NULL) { + err("kzalloc is fail(q_dst)"); + ret = -ENOMEM; + kfree(*vctx); + kfree(q_src); + *vctx = NULL; + goto exit; + } + + (*vctx)->instance = vref_get(video); + (*vctx)->q_src = q_src; + (*vctx)->q_dst = q_dst; + (*vctx)->q_src->id = id_src; + (*vctx)->q_dst->id = id_dst; + + file->private_data = *vctx; + +exit: + return ret; +} + +int close_vctx(struct file *file, + struct fimc_is_video *video, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + + kfree(vctx->q_src); + kfree(vctx->q_dst); + kfree(vctx); + file->private_data = NULL; + ret = vref_put(video, NULL); + + return ret; +} + +/* + * ============================================================================= + * Queue Opertation + * ============================================================================= + */ + +static int fimc_is_queue_open(struct fimc_is_queue *queue, + u32 rdycount) +{ + int ret = 0; + + queue->buf_maxcount = 0; + queue->buf_refcount = 0; + queue->buf_rdycount = rdycount; + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + memset(&queue->framecfg, 0, sizeof(struct fimc_is_frame_cfg)); + fimc_is_frame_probe(&queue->framemgr, queue->id); + + return ret; +} + +static int fimc_is_queue_close(struct fimc_is_queue *queue) +{ + int ret = 0; + + queue->buf_maxcount = 0; + queue->buf_refcount = 0; + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + fimc_is_frame_close(&queue->framemgr); + + return ret; +} + +static int fimc_is_queue_set_format_mplane(struct fimc_is_queue *queue, + struct v4l2_format *format) +{ + int ret = 0; + u32 plane; + struct v4l2_pix_format_mplane *pix; + struct fimc_is_fmt *fmt; + + pix = &format->fmt.pix_mp; + fmt = fimc_is_find_format(&pix->pixelformat, NULL, 0); + if (!fmt) { + err("pixel format is not found\n"); + ret = -EINVAL; + goto p_err; + } + + queue->framecfg.format.pixelformat = fmt->pixelformat; + queue->framecfg.format.mbus_code = fmt->mbus_code; + queue->framecfg.format.num_planes = fmt->num_planes; + queue->framecfg.width = pix->width; + queue->framecfg.height = pix->height; + + for (plane = 0; plane < fmt->num_planes; ++plane) { + if (pix->plane_fmt[plane].bytesperline) { + queue->framecfg.bytesperline[plane] = + pix->plane_fmt[plane].bytesperline; + queue->framecfg.width_stride[plane] = + pix->plane_fmt[plane].bytesperline - pix->width; + } else { + queue->framecfg.bytesperline[plane] = 0; + queue->framecfg.width_stride[plane] = 0; + } + } + +p_err: + return ret; +} + +int fimc_is_queue_setup(struct fimc_is_queue *queue, + void *alloc_ctx, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]) +{ + u32 ret = 0; + u32 plane; + + BUG_ON(!queue); + BUG_ON(!alloc_ctx); + BUG_ON(!num_planes); + BUG_ON(!sizes); + BUG_ON(!allocators); + + *num_planes = (unsigned int)(queue->framecfg.format.num_planes); + fimc_is_set_plane_size(&queue->framecfg, sizes); + + for (plane = 0; plane < *num_planes; plane++) { + allocators[plane] = alloc_ctx; + queue->framecfg.size[plane] = sizes[plane]; + mdbgv_vid("queue[%d] size : %d\n", plane, sizes[plane]); + } + + return ret; +} + +int fimc_is_queue_buffer_queue(struct fimc_is_queue *queue, + const struct fimc_is_vb2 *vb2, + struct vb2_buffer *vb) +{ + u32 ret = 0, i; + u32 index; + u32 ext_size; + u32 spare; + struct fimc_is_framemgr *framemgr; + struct fimc_is_frame *frame; + + index = vb->v4l2_buf.index; + framemgr = &queue->framemgr; + + BUG_ON(framemgr->id == FRAMEMGR_ID_INVALID); + + for (i = 0; i < vb->num_planes; i++) + queue->buf_dva[index][i] = vb2->plane_addr(vb, i); + + frame = &framemgr->frame[index]; + + /* uninitialized frame need to get info */ + if (!test_bit(FRAME_INI_MEM, &frame->memory)) + goto set_info; + + /* plane count check */ + if (frame->planes != vb->num_planes) { + err("plane count is changed(%08X != %08X)", + frame->planes, vb->num_planes); + ret = -EINVAL; + goto exit; + } + + /* plane address check */ + for (i = 0; i < frame->planes; i++) { + if (frame->dvaddr_buffer[i] != queue->buf_dva[index][i]) { + err("buffer %d plane %d is changed(%08X != %08X)", + index, i, + frame->dvaddr_buffer[i], + queue->buf_dva[index][i]); + ret = -EINVAL; + goto exit; + } + } + + goto exit; + +set_info: + if (test_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state)) { + err("already prepared but new index(%d) is came", index); + ret = -EINVAL; + goto exit; + } + + frame->vb = vb; + frame->planes = vb->num_planes; + spare = frame->planes - 1; + + for (i = 0; i < frame->planes; i++) { + frame->dvaddr_buffer[i] = queue->buf_dva[index][i]; +#ifdef PRINT_BUFADDR + info("%04X %d.%d %08X\n", framemgr->id, index, i, frame->dvaddr_buffer[i]); +#endif + } + + if (framemgr->id & FRAMEMGR_ID_SHOT) { + ext_size = sizeof(struct camera2_shot_ext) - sizeof(struct camera2_shot); + + /* Create Kvaddr for Metadata */ + queue->buf_kva[index][spare] = vb2->plane_kvaddr(vb, spare); + if (!queue->buf_kva[index][spare]) { + err("plane_kvaddr is fail(%08X)", framemgr->id); + ret = -EINVAL; + goto exit; + } + + frame->dvaddr_shot = queue->buf_dva[index][spare] + ext_size; + frame->kvaddr_shot = queue->buf_kva[index][spare] + ext_size; + frame->cookie_shot = (u32)vb2_plane_cookie(vb, spare); + frame->shot = (struct camera2_shot *)frame->kvaddr_shot; + frame->shot_ext = (struct camera2_shot_ext *)queue->buf_kva[index][spare]; + frame->shot_size = queue->framecfg.size[spare] - ext_size; +#ifdef MEASURE_TIME + frame->tzone = (struct timeval *)frame->shot_ext->timeZone; +#endif + } else { + /* Create Kvaddr for frame sync */ + queue->buf_kva[index][spare] = vb2->plane_kvaddr(vb, spare); + if (!queue->buf_kva[index][spare]) { + err("plane_kvaddr is fail(%08X)", framemgr->id); + ret = -EINVAL; + goto exit; + } + + frame->stream = (struct camera2_stream *)queue->buf_kva[index][spare]; + frame->stream->address = queue->buf_kva[index][spare]; + frame->stream_size = queue->framecfg.size[spare]; + } + + set_bit(FRAME_INI_MEM, &frame->memory); + + queue->buf_refcount++; + + if (queue->buf_rdycount == queue->buf_refcount) + set_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + + if (queue->buf_maxcount == queue->buf_refcount) + set_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + +exit: + return ret; +} + +void fimc_is_queue_wait_prepare(struct vb2_queue *vbq) +{ + struct fimc_is_video_ctx *vctx; + struct fimc_is_video *video; + + BUG_ON(!vbq); + + vctx = vbq->drv_priv; + if (!vctx) { + err("vctx is NULL"); + return; + } + + video = vctx->video; + mutex_unlock(&video->lock); +} + +void fimc_is_queue_wait_finish(struct vb2_queue *vbq) +{ + int ret = 0; + struct fimc_is_video_ctx *vctx; + struct fimc_is_video *video; + + BUG_ON(!vbq); + + vctx = vbq->drv_priv; + if (!vctx) { + err("vctx is NULL"); + return; + } + + video = vctx->video; + ret = mutex_lock_interruptible(&video->lock); + if (ret) + err("mutex_lock_interruptible is fail(%d)", ret); +} + +int fimc_is_queue_start_streaming(struct fimc_is_queue *queue, + struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + + BUG_ON(!queue); + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!vctx); + + if (test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + merr("already stream on(%ld)", vctx, queue->state); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state)) { + merr("buffer state is not ready(%ld)", vctx, queue->state); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_QOPS(queue, start_streaming, device, subdev, queue); + if (ret) { + merr("start_streaming is fail(%d)", vctx, ret); + ret = -EINVAL; + goto p_err; + } + + set_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + +p_err: + return ret; +} + +int fimc_is_queue_stop_streaming(struct fimc_is_queue *queue, + struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + + BUG_ON(!queue); + BUG_ON(!device); + BUG_ON(!subdev); + BUG_ON(!vctx); + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + merr("already stream off(%ld)", vctx, queue->state); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_QOPS(queue, stop_streaming, device, subdev, queue); + if (ret) { + merr("stop_streaming is fail(%d)", vctx, ret); + ret = -EINVAL; + goto p_err; + } + +p_err: + /* HACK: this state must be clear only if all ops was finished */ + clear_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + + return ret; +} + +int fimc_is_video_probe(struct fimc_is_video *video, + char *video_name, + u32 video_number, + u32 vfl_dir, + struct fimc_is_mem *mem, + struct v4l2_device *v4l2_dev, + struct mutex *lock, + const struct v4l2_file_operations *fops, + const struct v4l2_ioctl_ops *ioctl_ops) +{ + int ret = 0; + u32 video_id; + + vref_init(video); + mutex_init(&video->lock); + snprintf(video->vd.name, sizeof(video->vd.name), "%s", video_name); + video->id = video_number; + video->vb2 = mem->vb2; + video->alloc_ctx = mem->alloc_ctx; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) + video->vd.vfl_dir = vfl_dir; +#endif + video->vd.v4l2_dev = v4l2_dev; + video->vd.fops = fops; + video->vd.ioctl_ops = ioctl_ops; + video->vd.minor = -1; + video->vd.release = video_device_release; + video->vd.lock = lock; + video_set_drvdata(&video->vd, video); + + video_id = EXYNOS_VIDEONODE_FIMC_IS + video_number; + ret = video_register_device(&video->vd, + VFL_TYPE_GRABBER, + (EXYNOS_VIDEONODE_FIMC_IS + video_number)); + if (ret) { + err("Failed to register video device"); + goto p_err; + } + +p_err: + info("[VID] %s(%d) is created\n", video_name, video_id); + return ret; +} + +int fimc_is_video_open(struct fimc_is_video_ctx *vctx, + void *device, + u32 buf_rdycount, + struct fimc_is_video *video, + u32 video_type, + const struct vb2_ops *vb2_ops, + const struct fimc_is_queue_ops *src_qops, + const struct fimc_is_queue_ops *dst_qops) +{ + int ret = 0; + struct fimc_is_queue *q_src, *q_dst; + + BUG_ON(!video); + BUG_ON(!video->vb2); + BUG_ON(!vb2_ops); + + q_src = vctx->q_src; + q_dst = vctx->q_dst; + q_src->vbq = NULL; + q_dst->vbq = NULL; + q_src->qops = src_qops; + q_dst->qops = dst_qops; + + vctx->type = video_type; + vctx->device = device; + vctx->video = video; + vctx->vb2_ops = vb2_ops; + vctx->mem_ops = video->vb2->ops; + mutex_init(&vctx->lock); + + switch (video_type) { + case FIMC_IS_VIDEO_TYPE_OUTPUT: + fimc_is_queue_open(q_src, buf_rdycount); + + q_src->vbq = kzalloc(sizeof(struct vb2_queue), GFP_KERNEL); + if (!q_src->vbq) { + err("kzalloc is fail"); + ret = -ENOMEM; + goto p_err; + } + + ret = queue_init(vctx, q_src->vbq, NULL); + if (ret) { + err("queue_init fail"); + kfree(q_src->vbq); + goto p_err; + } + break; + case FIMC_IS_VIDEO_TYPE_CAPTURE: + fimc_is_queue_open(q_dst, buf_rdycount); + + q_dst->vbq = kzalloc(sizeof(struct vb2_queue), GFP_KERNEL); + if (!q_dst->vbq) { + err("kzalloc is fail"); + ret = -ENOMEM; + goto p_err; + } + + ret = queue_init(vctx, NULL, q_dst->vbq); + if (ret) { + err("queue_init fail"); + kfree(q_dst->vbq); + goto p_err; + } + break; + case FIMC_IS_VIDEO_TYPE_M2M: + fimc_is_queue_open(q_src, buf_rdycount); + fimc_is_queue_open(q_dst, buf_rdycount); + + q_src->vbq = kzalloc(sizeof(struct vb2_queue), GFP_KERNEL); + if (!q_src->vbq) { + err("kzalloc is fail"); + ret = -ENOMEM; + goto p_err; + } + + q_dst->vbq = kzalloc(sizeof(struct vb2_queue), GFP_KERNEL); + if (!q_dst->vbq) { + err("kzalloc is fail"); + kfree(q_src->vbq); + ret = -ENOMEM; + goto p_err; + } + + ret = queue_init(vctx, q_src->vbq, q_dst->vbq); + if (ret) { + err("queue_init fail"); + kfree(q_src->vbq); + kfree(q_dst->vbq); + goto p_err; + } + break; + default: + merr("invalid type(%d)", vctx, video_type); + ret = -EINVAL; + break; + } + +p_err: + return ret; +} + +int fimc_is_video_close(struct fimc_is_video_ctx *vctx) +{ + int ret = 0; + u32 video_type; + struct fimc_is_queue *q_src, *q_dst; + + BUG_ON(!vctx); + + video_type = vctx->type; + + q_src = vctx->q_src; + q_dst = vctx->q_dst; + + switch (video_type) { + case FIMC_IS_VIDEO_TYPE_OUTPUT: + BUG_ON(!q_src->vbq); + fimc_is_queue_close(q_src); + vb2_queue_release(q_src->vbq); + kfree(q_src->vbq); + break; + case FIMC_IS_VIDEO_TYPE_CAPTURE: + BUG_ON(!q_dst->vbq); + fimc_is_queue_close(q_dst); + vb2_queue_release(q_dst->vbq); + kfree(q_dst->vbq); + break; + case FIMC_IS_VIDEO_TYPE_M2M: + BUG_ON(!q_src->vbq); + BUG_ON(!q_dst->vbq); + fimc_is_queue_close(q_src); + vb2_queue_release(q_src->vbq); + fimc_is_queue_close(q_dst); + vb2_queue_release(q_dst->vbq); + kfree(q_src->vbq); + kfree(q_dst->vbq); + break; + default: + merr("invalid type(%d)", vctx, video_type); + ret = -EINVAL; + break; + } + + /* + * vb2 release can call stop callback + * not if video node is not stream off + */ + vctx->device = NULL; + + return ret; +} + +u32 fimc_is_video_poll(struct file *file, + struct fimc_is_video_ctx *vctx, + struct poll_table_struct *wait) +{ + u32 ret = 0; + u32 video_type = vctx->type; + + switch (video_type) { + case FIMC_IS_VIDEO_TYPE_OUTPUT: + ret = vb2_poll(vctx->q_src->vbq, file, wait); + break; + case FIMC_IS_VIDEO_TYPE_CAPTURE: + ret = vb2_poll(vctx->q_dst->vbq, file, wait); + break; + case FIMC_IS_VIDEO_TYPE_M2M: + merr("video poll is not supported", vctx); + ret = -EINVAL; + break; + default: + merr("invalid type(%d)", vctx, video_type); + break; + } + + return ret; +} + +int fimc_is_video_mmap(struct file *file, + struct fimc_is_video_ctx *vctx, + struct vm_area_struct *vma) +{ + u32 ret = 0; + u32 video_type = vctx->type; + + switch (video_type) { + case FIMC_IS_VIDEO_TYPE_OUTPUT: + ret = vb2_mmap(vctx->q_src->vbq, vma); + break; + case FIMC_IS_VIDEO_TYPE_CAPTURE: + ret = vb2_mmap(vctx->q_dst->vbq, vma); + break; + case FIMC_IS_VIDEO_TYPE_M2M: + merr("video mmap is not supported", vctx); + ret = -EINVAL; + break; + default: + merr("invalid type(%d)", vctx, video_type); + ret = -EINVAL; + break; + } + + return ret; +} + +int fimc_is_video_reqbufs(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_requestbuffers *request) +{ + int ret = 0; + struct fimc_is_framemgr *framemgr; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!request); + + queue = GET_VCTX_QUEUE(vctx, request); + + if (test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + err("video is stream on, not applied"); + ret = -EINVAL; + goto p_err; + } + + ret = vb2_reqbufs(queue->vbq, request); + if (ret) { + err("vb2_reqbufs is fail(%d)", ret); + goto p_err; + } + + framemgr = &queue->framemgr; + queue->buf_maxcount = request->count; + if (queue->buf_maxcount == 0) { + queue->buf_refcount = 0; + clear_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + clear_bit(FIMC_IS_QUEUE_BUFFER_PREPARED, &queue->state); + fimc_is_frame_close(framemgr); + } else { + if (queue->buf_maxcount < queue->buf_rdycount) { + err("buffer count is not invalid(%d < %d)", + queue->buf_maxcount, queue->buf_rdycount); + ret = -EINVAL; + goto p_err; + } + + if (!queue->buf_rdycount) + set_bit(FIMC_IS_QUEUE_BUFFER_READY, &queue->state); + + fimc_is_frame_open(framemgr, queue->buf_maxcount); + } + +p_err: + return ret; +} + +int fimc_is_video_querybuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_queue *queue; + + queue = GET_VCTX_QUEUE(vctx, buf); + + ret = vb2_querybuf(queue->vbq, buf); + + return ret; +} + +int fimc_is_video_set_format_mplane(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_format *format) +{ + int ret = 0; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!format); + + queue = GET_VCTX_QUEUE(vctx, format); + + ret = fimc_is_queue_set_format_mplane(queue, format); + + mdbgv_vid("set_format(%d x %d)\n", queue->framecfg.width, + queue->framecfg.height); + + return ret; +} + +int fimc_is_video_qbuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct vb2_queue *vbq; + struct vb2_buffer *vb; + + BUG_ON(!file); + BUG_ON(!vctx); + BUG_ON(!buf); + + buf->flags &= ~V4L2_BUF_FLAG_USE_SYNC; + queue = GET_VCTX_QUEUE(vctx, buf); + vbq = queue->vbq; + + if (!vbq) { + merr("vbq is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + if (vbq->fileio) { + merr("file io in progress", vctx); + ret = -EBUSY; + goto p_err; + } + + if (buf->type != queue->vbq->type) { + merr("buf type is invalid(%d != %d)", vctx, + buf->type, queue->vbq->type); + ret = -EINVAL; + goto p_err; + } + + if (buf->index >= vbq->num_buffers) { + merr("buffer index%d out of range", vctx, buf->index); + ret = -EINVAL; + goto p_err; + } + + if (buf->memory != vbq->memory) { + merr("invalid memory type%d", vctx, buf->memory); + ret = -EINVAL; + goto p_err; + } + + vb = vbq->bufs[buf->index]; + if (!vb) { + merr("vb is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = vb2_qbuf(queue->vbq, buf); + if (ret) { + merr("vb2_qbuf is fail(index : %d, %d)", vctx, buf->index, ret); + goto p_err; + } + +p_err: + return ret; +} + +int fimc_is_video_dqbuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf) +{ + int ret = 0; + u32 qcount; + bool blocking; + struct fimc_is_queue *queue; + struct fimc_is_framemgr *framemgr; + + BUG_ON(!file); + BUG_ON(!vctx); + BUG_ON(!buf); + + blocking = file->f_flags & O_NONBLOCK; + queue = GET_VCTX_QUEUE(vctx, buf); + framemgr = &queue->framemgr; + + if (!queue->vbq) { + merr("vbq is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + if (buf->type != queue->vbq->type) { + merr("buf type is invalid(%d != %d)", vctx, + buf->type, queue->vbq->type); + ret = -EINVAL; + goto p_err; + } + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + merr("queue is not streamon(%ld)", vctx, queue->state); + ret = -EINVAL; + goto p_err; + } + + framemgr_e_barrier_irq(framemgr, 0); + qcount = framemgr->frame_req_cnt + + framemgr->frame_pro_cnt + + framemgr->frame_com_cnt; + framemgr_x_barrier_irq(framemgr, 0); + + if (qcount <= 0) { + /* HACK : this log is commented until timeout issue fixed */ + /* merr("dqbuf can not be executed without qbuf(%d)", vctx, qcount); */ + ret = -EINVAL; + goto p_err; + } + + ret = vb2_dqbuf(queue->vbq, buf, blocking); + +p_err: + return ret; +} + +int fimc_is_video_streamon(struct file *file, + struct fimc_is_video_ctx *vctx, + enum v4l2_buf_type type) +{ + int ret = 0; + struct fimc_is_queue *queue; + struct vb2_queue *vbq; + + BUG_ON(!file); + BUG_ON(!vctx); + + queue = GET_QUEUE(vctx, type); + vbq = queue->vbq; + if (!vbq) { + merr("vbq is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + if (vbq->type != type) { + merr("invalid stream type(%d != %d)", vctx, vbq->type, type); + ret = -EINVAL; + goto p_err; + } + + if (vbq->streaming) { + merr("streamon: already streaming", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = vb2_streamon(vbq, type); + +p_err: + return ret; +} + +int fimc_is_video_streamoff(struct file *file, + struct fimc_is_video_ctx *vctx, + enum v4l2_buf_type type) +{ + int ret = 0; + u32 qcount; + struct fimc_is_queue *queue; + struct vb2_queue *vbq; + struct fimc_is_framemgr *framemgr; + + BUG_ON(!file); + BUG_ON(!vctx); + + queue = GET_QUEUE(vctx, type); + framemgr = &queue->framemgr; + vbq = queue->vbq; + if (!vbq) { + merr("vbq is NULL", vctx); + ret = -EINVAL; + goto p_err; + } + + framemgr_e_barrier_irq(framemgr, 0); + qcount = framemgr->frame_req_cnt + + framemgr->frame_pro_cnt + + framemgr->frame_com_cnt; + framemgr_x_barrier_irq(framemgr, 0); + + if (qcount > 0) + mwarn("video%d stream off : queued buffer is not empty(%d)", vctx, + vctx->video->id, qcount); + + if (vbq->type != type) { + merr("invalid stream type(%d != %d)", vctx, vbq->type, type); + ret = -EINVAL; + goto p_err; + } + + if (!vbq->streaming) { + merr("streamoff: not streaming", vctx); + ret = -EINVAL; + goto p_err; + } + + ret = vb2_streamoff(vbq, type); + +p_err: + return ret; +} + +int queue_done(struct fimc_is_video_ctx *vctx, + struct fimc_is_queue *queue, + u32 index, u32 state) +{ + int ret = 0; + struct vb2_buffer *vb; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + BUG_ON(!queue); + BUG_ON(!queue->vbq); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + + vb = queue->vbq->bufs[index]; + + if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) { + warn("%d video queue is not stream on", vctx->video->id); + ret = -EINVAL; + goto p_err; + } + + if (vb->state != VB2_BUF_STATE_ACTIVE) { + err("vb buffer[%d] state is not active(%d)", index, vb->state); + ret = -EINVAL; + goto p_err; + } + + vb2_buffer_done(vb, state); + +p_err: + return ret; +} + +int buffer_done(struct fimc_is_video_ctx *vctx, u32 index) +{ + int ret = 0; + struct fimc_is_queue *queue; + + BUG_ON(!vctx); + BUG_ON(!vctx->video); + BUG_ON(index >= FRAMEMGR_MAX_REQUEST); + BUG_ON(vctx->type == FIMC_IS_VIDEO_TYPE_M2M); + + switch (vctx->type) { + case FIMC_IS_VIDEO_TYPE_OUTPUT: + queue = GET_SRC_QUEUE(vctx); + queue_done(vctx, queue, index, VB2_BUF_STATE_DONE); + break; + case FIMC_IS_VIDEO_TYPE_CAPTURE: + queue = GET_DST_QUEUE(vctx); + queue_done(vctx, queue, index, VB2_BUF_STATE_DONE); + break; + default: + merr("invalid type(%d)", vctx, vctx->type); + ret = -EINVAL; + break; + } + + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/fimc-is-video.h b/drivers/media/platform/exynos/fimc-is/fimc-is-video.h new file mode 100644 index 000000000000..817bf4ce1220 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/fimc-is-video.h @@ -0,0 +1,234 @@ +#ifndef FIMC_IS_VIDEO_H +#define FIMC_IS_VIDEO_H + +#include +#include +#include "fimc-is-type.h" +#include "fimc-is-mem.h" +#include "fimc-is-framemgr.h" +#include "fimc-is-metadata.h" +#include "fimc-is-config.h" + +/* configuration by linux kernel version */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) +#define VFL_DIR_RX 0 +#define VFL_DIR_TX 1 +#define VFL_DIR_M2M 2 +#endif + +#define FIMC_IS_MAX_NODES (3) +#define FIMC_IS_INVALID_BUF_INDEX (0xFF) + +#define VIDEO_SENSOR_READY_BUFFERS 0 +#define VIDEO_3AA_READY_BUFFERS 0 +#define VIDEO_3AAC_READY_BUFFERS 0 +#define VIDEO_3AAP_READY_BUFFERS 0 +#define VIDEO_ISP_READY_BUFFERS 0 +#define VIDEO_SCC_READY_BUFFERS 0 +#define VIDEO_SCP_READY_BUFFERS 0 +#define VIDEO_VDISC_READY_BUFFERS 1 +#define VIDEO_VDISO_READY_BUFFERS 0 + +#define FIMC_IS_VIDEO_NAME(name) ("exynos-fimc-is-"name) +#define FIMC_IS_VIDEO_SENSOR_NAME FIMC_IS_VIDEO_NAME("sensor") +#define FIMC_IS_VIDEO_3AA_NAME(id) FIMC_IS_VIDEO_NAME("3aa."#id) +#define FIMC_IS_VIDEO_3AAC_NAME(id) FIMC_IS_VIDEO_NAME("3aa."#id"c") +#define FIMC_IS_VIDEO_3AAP_NAME(id) FIMC_IS_VIDEO_NAME("3aa."#id"p") +#define FIMC_IS_VIDEO_ISP_NAME FIMC_IS_VIDEO_NAME("isp") +#define FIMC_IS_VIDEO_SCC_NAME FIMC_IS_VIDEO_NAME("scalerc") +#define FIMC_IS_VIDEO_SCP_NAME FIMC_IS_VIDEO_NAME("scalerp") +#define FIMC_IS_VIDEO_VDC_NAME FIMC_IS_VIDEO_NAME("vdisc") +#define FIMC_IS_VIDEO_VDO_NAME FIMC_IS_VIDEO_NAME("vdiso") + +struct fimc_is_device_ischain; +struct fimc_is_subdev; +struct fimc_is_queue; + +enum fimc_is_video_dev_num { + FIMC_IS_VIDEO_SS0_NUM = 0, + FIMC_IS_VIDEO_SS1_NUM = 1, + FIMC_IS_VIDEO_SS2_NUM = 2, + FIMC_IS_VIDEO_SS3_NUM = 3, + FIMC_IS_VIDEO_3A0_NUM = 10, + FIMC_IS_VIDEO_3A0C_NUM = 11, + FIMC_IS_VIDEO_3A0P_NUM = 12, + FIMC_IS_VIDEO_3A1_NUM = 14, + FIMC_IS_VIDEO_3A1C_NUM = 15, + FIMC_IS_VIDEO_3A1P_NUM = 16, + FIMC_IS_VIDEO_ISP_NUM = 30, + FIMC_IS_VIDEO_SCC_NUM = 34, + FIMC_IS_VIDEO_SCP_NUM = 37, + FIMC_IS_VIDEO_VDC_NUM = 40, + FIMC_IS_VIDEO_VDO_NUM = 41, + FIMC_IS_VIDEO_FD_NUM = 46, + FIMC_IS_VIDEO_MAX_NUM = 49 +}; + +enum fimc_is_video_type { + FIMC_IS_VIDEO_TYPE_CAPTURE, + FIMC_IS_VIDEO_TYPE_OUTPUT, + FIMC_IS_VIDEO_TYPE_M2M, +}; + +enum fimc_is_queue_state { + FIMC_IS_QUEUE_BUFFER_PREPARED, + FIMC_IS_QUEUE_BUFFER_READY, + FIMC_IS_QUEUE_STREAM_ON +}; + +struct fimc_is_frame_cfg { + struct fimc_is_fmt format; + u32 width; + u32 height; + u32 width_stride[FIMC_IS_MAX_PLANES]; + u32 size[FIMC_IS_MAX_PLANES]; + u32 bytesperline[FIMC_IS_MAX_PLANES]; +}; + +struct fimc_is_queue_ops { + int (*start_streaming)(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); + int (*stop_streaming)(struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_queue *queue); +}; + +struct fimc_is_queue { + struct vb2_queue *vbq; + const struct fimc_is_queue_ops *qops; + struct fimc_is_framemgr framemgr; + struct fimc_is_frame_cfg framecfg; + + u32 buf_maxcount; + u32 buf_rdycount; + u32 buf_refcount; + u32 buf_dva[FIMC_IS_MAX_BUFS][FIMC_IS_MAX_PLANES]; + u32 buf_kva[FIMC_IS_MAX_BUFS][FIMC_IS_MAX_PLANES]; + + u32 id; + unsigned long state; +}; + +struct fimc_is_video_ctx { + struct fimc_is_queue *q_src; + struct fimc_is_queue *q_dst; + struct mutex lock; + u32 type; + u32 instance; + + void *device; + struct fimc_is_video *video; + + const struct vb2_ops *vb2_ops; + const struct vb2_mem_ops *mem_ops; +}; + +struct fimc_is_video { + u32 id; + atomic_t refcount; + struct mutex lock; + + struct video_device vd; + struct media_pad pads; + const struct fimc_is_vb2 *vb2; + void *alloc_ctx; +}; + +struct fimc_is_core *fimc_is_video_ctx_2_core(struct fimc_is_video_ctx *vctx); + +/* video context operation */ +int open_vctx(struct file *file, + struct fimc_is_video *video, + struct fimc_is_video_ctx **vctx, + u32 id_src, u32 id_dst); +int close_vctx(struct file *file, + struct fimc_is_video *video, + struct fimc_is_video_ctx *vctx); + +/* queue operation */ +int fimc_is_queue_setup(struct fimc_is_queue *queue, + void *alloc_ctx, + unsigned int *num_planes, + unsigned int sizes[], + void *allocators[]); +int fimc_is_queue_buffer_queue(struct fimc_is_queue *queue, + const struct fimc_is_vb2 *vb2, + struct vb2_buffer *vb); +void fimc_is_queue_wait_prepare(struct vb2_queue *vbq); +void fimc_is_queue_wait_finish(struct vb2_queue *vbq); +int fimc_is_queue_start_streaming(struct fimc_is_queue *queue, + struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx); +int fimc_is_queue_stop_streaming(struct fimc_is_queue *queue, + struct fimc_is_device_ischain *device, + struct fimc_is_subdev *subdev, + struct fimc_is_video_ctx *vctx); + +/* video operation */ +int fimc_is_video_probe(struct fimc_is_video *video, + char *video_name, + u32 video_number, + u32 vfl_dir, + struct fimc_is_mem *mem, + struct v4l2_device *v4l2_dev, + struct mutex *lock, + const struct v4l2_file_operations *fops, + const struct v4l2_ioctl_ops *ioctl_ops); +int fimc_is_video_open(struct fimc_is_video_ctx *vctx, + void *device, + u32 buf_rdycount, + struct fimc_is_video *video, + u32 video_type, + const struct vb2_ops *vb2_ops, + const struct fimc_is_queue_ops *src_qops, + const struct fimc_is_queue_ops *dst_qops); +int fimc_is_video_close(struct fimc_is_video_ctx *vctx); +u32 fimc_is_video_poll(struct file *file, + struct fimc_is_video_ctx *vctx, + struct poll_table_struct *wait); +int fimc_is_video_mmap(struct file *file, + struct fimc_is_video_ctx *vctx, + struct vm_area_struct *vma); +int fimc_is_video_reqbufs(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_requestbuffers *request); +int fimc_is_video_querybuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf); +int fimc_is_video_set_format_mplane(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_format *format); +int fimc_is_video_qbuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf); +int fimc_is_video_dqbuf(struct file *file, + struct fimc_is_video_ctx *vctx, + struct v4l2_buffer *buf); +int fimc_is_video_streamon(struct file *file, + struct fimc_is_video_ctx *vctx, + enum v4l2_buf_type type); +int fimc_is_video_streamoff(struct file *file, + struct fimc_is_video_ctx *vctx, + enum v4l2_buf_type type); + +int queue_done(struct fimc_is_video_ctx *vctx, + struct fimc_is_queue *queue, + u32 index, u32 state); +int buffer_done(struct fimc_is_video_ctx *vctx, u32 index); +long video_ioctl3(struct file *file, unsigned int cmd, unsigned long arg); + +#define GET_QUEUE(vctx, type) \ + (V4L2_TYPE_IS_OUTPUT((type)) ? vctx->q_src : vctx->q_dst) +#define GET_VCTX_QUEUE(vctx, vbq) (GET_QUEUE(vctx, vbq->type)) + +#define GET_SRC_QUEUE(vctx) (vctx->q_src) +#define GET_DST_QUEUE(vctx) (vctx->q_dst) + +#define GET_SRC_FRAMEMGR(vctx) (&vctx->q_src->framemgr) +#define GET_DST_FRAMEMGR(vctx) (&vctx->q_dst->framemgr) + +#define CALL_QOPS(q, op, args...) (((q)->qops->op) ? ((q)->qops->op(args)) : 0) + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.c new file mode 100644 index 000000000000..aa062f05f59d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.c @@ -0,0 +1,309 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-2p2.h" + +#define SENSOR_NAME "S5K2P2" + +static struct fimc_is_sensor_cfg config_2p2[] = { + /* 5328x3000@30fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 30, 30, 0), + /* 5328x3000@24fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 24, 30, 1), + /* 4000X3000@30fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 30, 23, 2), + /* 4000X3000@24fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 24, 23, 3), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 30, 19, 4), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 24, 19, 5), + /* 2664X1500@60fps */ + FIMC_IS_SENSOR_CFG(2664, 1500, 60, 19, 6), + /* 1328X748@120fps */ + FIMC_IS_SENSOR_CFG(1328, 748, 120, 13, 7), + /* 824X496@300fps */ + FIMC_IS_SENSOR_CFG(824, 496, 300, 13, 8), +}; + +static int sensor_2p2_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_2p2_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +#ifdef CONFIG_OF +#ifdef CONFIG_COMPANION_USE +static int sensor_2p2_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_comp_en = 0, gpio_comp_rst = 0; + int gpio_none = 0; + int gpio_reset = 0; + int gpios_cam_en = -EINVAL; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_comp_en = of_get_named_gpio(dnode, "gpios_comp_en", 0); + if (!gpio_is_valid(gpio_comp_en)) { + dev_err(dev, "failed to get main comp en gpio\n"); + } else { + gpio_request_one(gpio_comp_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_en); + } + + gpio_comp_rst = of_get_named_gpio(dnode, "gpios_comp_reset", 0); + if (!gpio_is_valid(gpio_comp_rst)) { + dev_err(dev, "failed to get main comp reset gpio\n"); + } else { + gpio_request_one(gpio_comp_rst, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_rst); + } + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + dev_err(dev, "failed to get main/front cam en gpio\n"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + + /* COMPANION - POWER ON */ + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_comp_en, 0, NULL, 150, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 7, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 8, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 9, gpio_none, 0, "af", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 10, gpio_none, 0, NULL, 0, PIN_END); + + /* COMPANION - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_comp_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 8, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 9, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#else +static int sensor_2p2_power_setpin(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_COMPANION_USE */ +#endif /* CONFIG_OF */ + +int sensor_2p2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_2P2_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K2P2; + module->subdev = subdev_module; + module->device = SENSOR_2P2_INSTANCE; + module->client = client; + module->active_width = 5312; + module->active_height = 2990; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 300; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K2P2"; + module->setfile_name = "setfile_2p2.bin"; + module->cfgs = ARRAY_SIZE(config_2p2); + module->cfg = config_2p2; + module->ops = NULL; + module->private_data = NULL; +#ifdef CONFIG_OF + module->power_setpin = sensor_2p2_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_S5K2P2; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x5A; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->actuator_con.peri_setting.i2c.slave_address = 0x5A; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#ifdef CONFIG_LEDS_MAX77804 + ext->flash_con.product_name = FLADRV_NAME_MAX77693; +#endif +#if defined(CONFIG_LEDS_LM3560) || !defined(CONFIG_USE_VENDER_FEATURE) + ext->flash_con.product_name = FLADRV_NAME_LM3560; +#endif +#ifdef CONFIG_LEDS_SKY81296 + ext->flash_con.product_name = FLADRV_NAME_SKY81296; +#endif + ext->flash_con.peri_type = SE_GPIO; +#ifdef CONFIG_USE_VENDER_FEATURE + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 1; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 2; +#else + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 2; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 3; +#endif + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + +#ifdef CONFIG_COMPANION_USE + ext->companion_con.product_name = COMPANION_NAME_73C1; + ext->companion_con.peri_info0.valid = true; + ext->companion_con.peri_info0.peri_type = SE_SPI; + ext->companion_con.peri_info0.peri_setting.spi.channel = (int) core->companion_spi_channel; + ext->companion_con.peri_info1.valid = true; + ext->companion_con.peri_info1.peri_type = SE_I2C; + ext->companion_con.peri_info1.peri_setting.i2c.channel = 0; + ext->companion_con.peri_info1.peri_setting.i2c.slave_address = 0x7A; + ext->companion_con.peri_info1.peri_setting.i2c.speed = 400000; + ext->companion_con.peri_info2.valid = true; + ext->companion_con.peri_info2.peri_type = SE_FIMC_LITE; + ext->companion_con.peri_info2.peri_setting.fimc_lite.channel = FLITE_ID_D; +#else + ext->companion_con.product_name = COMPANION_NAME_NOTHING; +#endif + +#if defined(CONFIG_OIS_USE) + ext->ois_con.product_name = OIS_NAME_IDG2030; + ext->ois_con.peri_type = SE_I2C; + ext->ois_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->ois_con.peri_setting.i2c.slave_address = 0x48; + ext->ois_con.peri_setting.i2c.speed = 400000; +#else + ext->ois_con.product_name = OIS_NAME_NOTHING; + ext->ois_con.peri_type = SE_NULL; +#endif + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.h new file mode 100644 index 000000000000..f4820cc1eee5 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_2P2_H +#define FIMC_IS_DEVICE_2P2_H + +#define SENSOR_2P2_INSTANCE 0 +#define SENSOR_2P2_NAME SENSOR_NAME_S5K2P2 + +int sensor_2p2_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.c new file mode 100644 index 000000000000..979f05ad5d62 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.c @@ -0,0 +1,346 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-2p2_12m.h" + +#define SENSOR_NAME "S5K2P2_12M" + +static struct fimc_is_sensor_cfg config_2p2_12m[] = { + /* 4624x2604@30fps */ + FIMC_IS_SENSOR_CFG(4624, 2604, 30, 22, 0), + /* 4624x2604@24fps */ + FIMC_IS_SENSOR_CFG(4624, 2604, 24, 21, 1), + /* 3472X2604@30fps */ + FIMC_IS_SENSOR_CFG(3472, 2604, 30, 17, 2), + /* 3472X2604@24fps */ + FIMC_IS_SENSOR_CFG(3472, 2604, 24, 17, 3), + /* 2608X2604@30fps */ + FIMC_IS_SENSOR_CFG(2608, 2604, 30, 17, 4), + /* 2608X2604@24fps */ + FIMC_IS_SENSOR_CFG(2608, 2604, 24, 17, 5), + /* 2312X1300@60fps */ + FIMC_IS_SENSOR_CFG(2312, 1300, 60, 13, 6), + /* 2312X1300@30fps */ + FIMC_IS_SENSOR_CFG(2312, 1300, 30, 13, 7), + /* 1156X650@120fps */ + FIMC_IS_SENSOR_CFG(1156, 650, 120, 13, 8), +}; + +static int sensor_2p2_12m_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_2p2_12m_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +#ifdef CONFIG_OF +#ifdef CONFIG_COMPANION_USE +static int sensor_2p2_12m_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_comp_en = 0, gpio_comp_rst = 0; + int gpio_none = 0; + int gpio_reset = 0; + int gpios_cam_en = -EINVAL; +#ifdef CONFIG_OIS_USE + int gpios_ois_en = 0; +#endif + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_comp_en = of_get_named_gpio(dnode, "gpios_comp_en", 0); + if (!gpio_is_valid(gpio_comp_en)) { + dev_err(dev, "failed to get main comp en gpio\n"); + } else { + gpio_request_one(gpio_comp_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_en); + } + + gpio_comp_rst = of_get_named_gpio(dnode, "gpios_comp_reset", 0); + if (!gpio_is_valid(gpio_comp_rst)) { + dev_err(dev, "failed to get main comp reset gpio\n"); + } else { + gpio_request_one(gpio_comp_rst, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_rst); + } + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + if (of_get_property(dnode, "gpios_cam_en", NULL)) { + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + dev_err(dev, "failed to get main cam en gpio\n"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + } + +#ifdef CONFIG_OIS_USE + gpios_ois_en = of_get_named_gpio(dnode, "gpios_ois_en", 0); + pdata->pin_ois_en = gpios_ois_en; + if (!gpio_is_valid(gpios_ois_en)) { + dev_err(dev, "failed to get ois en gpio\n"); + } else { + gpio_request_one(gpios_ois_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_ois_en); + } +#endif + + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); +#ifdef CONFIG_OIS_USE + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_ON); +#endif + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 7, gpio_comp_en, 0, NULL, 150, PIN_OUTPUT_HIGH); + if (pdata->companion_use_pmic) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 8, gpio_none, 0, "VDD_MIPI_1.0V_COMP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 9, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 10, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 11, gpio_none, 0, "af", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 12, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 13, gpio_none, 0, NULL, 0, PIN_END); + + /* BACK CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_LOW); + if (pdata->companion_use_pmic) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VDD_MIPI_1.0V_COMP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_comp_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 8, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 9, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); +#ifdef CONFIG_OIS_USE + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 10, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 11, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_OFF); +#endif + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 12, gpio_none, 0, NULL, 0, PIN_END); + +#ifdef CONFIG_OIS_USE + /* OIS_FACTORY - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 1, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 2, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 5, gpio_none, 0, NULL, 0, PIN_END); + + /* OIS_FACTORY - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 1, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 2, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 3, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 5, gpio_none, 0, NULL, 0, PIN_END); +#endif + + return 0; +} +#else +static int sensor_2p2_12m_power_setpin(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_COMPANION_USE */ +#endif /* CONFIG_OF */ + + +int sensor_2p2_12m_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_2P2_12M_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K2P2_12M; + module->subdev = subdev_module; + module->device = SENSOR_2P2_12M_INSTANCE; + module->client = client; + module->active_width = 4608; + module->active_height = 2594; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 300; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K2P2"; + module->setfile_name = "setfile_2p2_12m.bin"; + module->cfgs = ARRAY_SIZE(config_2p2_12m); + module->cfg = config_2p2_12m; + module->ops = NULL; + module->private_data = NULL; +#ifdef CONFIG_OF + module->power_setpin = sensor_2p2_12m_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_S5K2P2_12M; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x5A; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->actuator_con.peri_setting.i2c.slave_address = 0x5A; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#ifdef CONFIG_LEDS_MAX77804 + ext->flash_con.product_name = FLADRV_NAME_MAX77693; +#endif +#if defined(CONFIG_LEDS_LM3560) || !defined(CONFIG_USE_VENDER_FEATURE) + ext->flash_con.product_name = FLADRV_NAME_LM3560; +#endif +#ifdef CONFIG_LEDS_SKY81296 + ext->flash_con.product_name = FLADRV_NAME_SKY81296; +#endif + ext->flash_con.peri_type = SE_GPIO; +#ifdef CONFIG_USE_VENDER_FEATURE + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 1; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 2; +#else + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 2; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 3; +#endif + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + +#ifdef CONFIG_COMPANION_USE + ext->companion_con.product_name = COMPANION_NAME_73C1; + ext->companion_con.peri_info0.valid = true; + ext->companion_con.peri_info0.peri_type = SE_SPI; + ext->companion_con.peri_info0.peri_setting.spi.channel = (int) core->companion_spi_channel; + ext->companion_con.peri_info1.valid = true; + ext->companion_con.peri_info1.peri_type = SE_I2C; + ext->companion_con.peri_info1.peri_setting.i2c.channel = 0; + ext->companion_con.peri_info1.peri_setting.i2c.slave_address = 0x7A; + ext->companion_con.peri_info1.peri_setting.i2c.speed = 400000; + ext->companion_con.peri_info2.valid = true; + ext->companion_con.peri_info2.peri_type = SE_FIMC_LITE; + ext->companion_con.peri_info2.peri_setting.fimc_lite.channel = FLITE_ID_D; +#else + ext->companion_con.product_name = COMPANION_NAME_NOTHING; +#endif + + if (client) { + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + } else { + v4l2_subdev_init(subdev_module, &subdev_ops); + } + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.h new file mode 100644 index 000000000000..d28ca8d3f99e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p2_12m.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_2P2_12M_H +#define FIMC_IS_DEVICE_2P2_12M_H + +#define SENSOR_2P2_12M_INSTANCE 0 +#define SENSOR_2P2_12M_NAME SENSOR_NAME_S5K2P2_12M + +int sensor_2p2_12m_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.c new file mode 100644 index 000000000000..ed22f2f6f82d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.c @@ -0,0 +1,280 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-2p3.h" + +#define SENSOR_NAME "S5K2P3" + +static struct fimc_is_sensor_cfg config_2p3[] = { + /* 5328x3000@30fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 30, 30, 0), + /* 5328x3000@24fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 24, 24, 1), + /* 4000X3000@30fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 30, 23, 2), + /* 4000X3000@24fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 24, 19, 3), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 30, 19, 4), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 24, 14, 5), + /* 2664X1500@60fps */ + FIMC_IS_SENSOR_CFG(2664, 1500, 60, 19, 6), + /* 1328X748@120fps */ + FIMC_IS_SENSOR_CFG(1328, 748, 120, 13, 7), + /* 824X496@300fps */ +/* FIMC_IS_SENSOR_CFG(824, 496, 300, 13, 8), TODO: Temporary Disabled. */ +}; + +static int sensor_2p3_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_2p3_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +#ifdef CONFIG_OF +static int sensor_2p3_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_none = 0; + int gpio_reset = 0; + int gpios_cam_en = -EINVAL; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + dev_err(dev, "failed to get main/front cam en gpio\n"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + + /* POWER ON */ + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 7, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 8, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 9, gpio_none, 0, "af", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 10, gpio_none, 0, NULL, 0, PIN_END); + + /* POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 8, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 9, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif /* CONFIG_OF */ + +int sensor_2p3_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_2P3_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K2P3; + module->subdev = subdev_module; + module->device = SENSOR_2P3_INSTANCE; + module->client = client; + module->active_width = 5312; + module->active_height = 2990; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 300; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K2P3"; + module->setfile_name = "setfile_2p3.bin"; + module->cfgs = ARRAY_SIZE(config_2p3); + module->cfg = config_2p3; + module->ops = NULL; + module->private_data = NULL; +#ifdef CONFIG_OF + module->power_setpin = sensor_2p3_power_setpin; +#endif + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_S5K2P3; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x20; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->actuator_con.peri_setting.i2c.slave_address = 0x20; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#ifdef CONFIG_LEDS_MAX77804 + ext->flash_con.product_name = FLADRV_NAME_MAX77693; +#endif +#if defined(CONFIG_LEDS_LM3560) || !defined(CONFIG_USE_VENDER_FEATURE) + ext->flash_con.product_name = FLADRV_NAME_LM3560; +#endif +#ifdef CONFIG_LEDS_SKY81296 + ext->flash_con.product_name = FLADRV_NAME_SKY81296; +#endif + ext->flash_con.peri_type = SE_GPIO; +#ifdef CONFIG_USE_VENDER_FEATURE + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 1; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 2; +#else + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 2; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 3; +#endif + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + +#ifdef CONFIG_COMPANION_USE + ext->companion_con.product_name = COMPANION_NAME_73C1; + ext->companion_con.peri_info0.valid = true; + ext->companion_con.peri_info0.peri_type = SE_SPI; + ext->companion_con.peri_info0.peri_setting.spi.channel = (int) core->companion_spi_channel; + ext->companion_con.peri_info1.valid = true; + ext->companion_con.peri_info1.peri_type = SE_I2C; + ext->companion_con.peri_info1.peri_setting.i2c.channel = 0; + ext->companion_con.peri_info1.peri_setting.i2c.slave_address = 0x7A; + ext->companion_con.peri_info1.peri_setting.i2c.speed = 400000; + ext->companion_con.peri_info2.valid = true; + ext->companion_con.peri_info2.peri_type = SE_FIMC_LITE; + ext->companion_con.peri_info2.peri_setting.fimc_lite.channel = FLITE_ID_D; +#else + ext->companion_con.product_name = COMPANION_NAME_NOTHING; +#endif + +#if defined(CONFIG_OIS_USE) + ext->ois_con.product_name = OIS_NAME_IDG2030; + ext->ois_con.peri_type = SE_I2C; + ext->ois_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->ois_con.peri_setting.i2c.slave_address = 0x48; + ext->ois_con.peri_setting.i2c.speed = 400000; +#else + ext->ois_con.product_name = OIS_NAME_NOTHING; + ext->ois_con.peri_type = SE_NULL; +#endif + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.h new file mode 100644 index 000000000000..7e3791dd60e3 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-2p3.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_2P3_H +#define FIMC_IS_DEVICE_2P3_H + +#define SENSOR_2P3_INSTANCE 0 +#define SENSOR_2P3_NAME SENSOR_NAME_S5K2P3 + +int sensor_2p3_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.c new file mode 100644 index 000000000000..8676a22aecf0 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.c @@ -0,0 +1,154 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-3h5.h" + +#define SENSOR_NAME "S5K3H5" + +static struct fimc_is_sensor_cfg config_3h5[] = { + /* 3264x2448@30fps */ + FIMC_IS_SENSOR_CFG(3264, 2448, 30, 15, 0), +}; + +static int sensor_3h5_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_3h5_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_3h5_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K3H5_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K3H5; + module->subdev = subdev_module; + module->device = SENSOR_S5K3H5_INSTANCE; + module->client = client; + module->active_width = 3248; + module->active_height = 2438; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K3H5"; + module->setfile_name = "setfile_3h5.bin"; + module->cfgs = ARRAY_SIZE(config_3h5); + module->cfg = config_3h5; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x5A; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_HYBRIDVCA; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + + ext->flash_con.product_name = FLADRV_NAME_AAT1290A; /* == FLADRV_NAME_AAT1274 */ + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 6; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 8; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.h new file mode 100644 index 000000000000..644e1490a2e1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h5.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_3H5_H +#define FIMC_IS_DEVICE_3H5_H + +#define SENSOR_S5K3H5_INSTANCE 0 +#define SENSOR_S5K3H5_NAME SENSOR_NAME_S5K3H5 + +int sensor_3h5_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.c new file mode 100644 index 000000000000..b4bfd2f77e07 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.c @@ -0,0 +1,154 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-3h7.h" + +#define SENSOR_NAME "S5K3H7" + +static struct fimc_is_sensor_cfg config_3h7[] = { + /* 3280x2458@30fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 30, 15, 0), +}; + +static int sensor_3h7_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_3h7_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_3h7_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K3H7_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K3H7; + module->subdev = subdev_module; + module->device = SENSOR_S5K3H7_INSTANCE; + module->client = client; + module->active_width = 3248; + module->active_height = 2438; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K3H7"; + module->setfile_name = "setfile_3h7.bin"; + module->cfgs = ARRAY_SIZE(config_3h7); + module->cfg = config_3h7; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x20; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7343; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + + ext->flash_con.product_name = FLADRV_NAME_KTD267; + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 17; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 16; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.h new file mode 100644 index 000000000000..d2a13f35584b --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_3H7_H +#define FIMC_IS_DEVICE_3H7_H + +#define SENSOR_S5K3H7_INSTANCE 0 +#define SENSOR_S5K3H7_NAME SENSOR_NAME_S5K3H7 + +int sensor_3h7_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.c new file mode 100644 index 000000000000..52f6f8862164 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.c @@ -0,0 +1,164 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-3h7_sunny.h" + +#define SENSOR_NAME "S5K3H7_SUNNY" + +static struct fimc_is_sensor_cfg config_3h7_sunny[] = { + /* 3264x2448@30fps */ + FIMC_IS_SENSOR_CFG(3264, 2448, 30, 15, 0), +}; + +static int sensor_3h7_sunny_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_3h7_sunny_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_3h7_sunny_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K3H7_SUNNY_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K3H7_SUNNY; + module->subdev = subdev_module; + module->device = SENSOR_S5K3H7_SUNNY_INSTANCE; + module->client = client; + module->active_width = 3248; + module->active_height = 2438; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K3H7"; + module->setfile_name = "setfile_3h7_sunny.bin"; + module->cfgs = ARRAY_SIZE(config_3h7_sunny); + module->cfg = config_3h7_sunny; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x20; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_DWXXXX; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->actuator_con.peri_setting.i2c.slave_address = 0x20; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#if defined(CONFIG_EXYNOS5260_XYREF_REV0) + ext->flash_con.product_name = FLADRV_NAME_NOTHING; +#else + ext->flash_con.product_name = FLADRV_NAME_AS3643; +#endif + ext->flash_con.peri_type = SE_I2C; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 5; +#if defined(CONFIG_MACH_XYREF4415) + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 6; +#else + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 8; +#endif + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.h new file mode 100644 index 000000000000..3e0d1c4106a1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3h7_sunny.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_3H7_SUNNY_H +#define FIMC_IS_DEVICE_3H7_SYNNY_H + +#define SENSOR_S5K3H7_SUNNY_INSTANCE 0 +#define SENSOR_S5K3H7_SUNNY_NAME SENSOR_NAME_S5K3H7_SUNNY + +int sensor_3h7_sunny_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.c new file mode 100644 index 000000000000..21c5d04562bb --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.c @@ -0,0 +1,166 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-3l2.h" + +#define SENSOR_NAME "S5K3L2" + +static struct fimc_is_sensor_cfg config_3l2[] = { + /* 4144x3106@30fps */ + FIMC_IS_SENSOR_CFG(4144, 3106, 30, 25, 0), + /* 4144x2332@30fps */ + FIMC_IS_SENSOR_CFG(4144, 2332, 30, 25, 1), + /* 2072x1554@24fps */ + FIMC_IS_SENSOR_CFG(2072, 1554, 24, 25, 2), + /* 2072x1166@24fps */ + FIMC_IS_SENSOR_CFG(2072, 1166, 24, 25, 3), + /* 1040x584@120fps */ + FIMC_IS_SENSOR_CFG(1040, 584, 120, 17, 4), + /* 2072x1166@60fps */ + FIMC_IS_SENSOR_CFG(2072, 1166, 60, 19, 5), +}; + +static int sensor_3l2_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_3l2_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_3l2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K3L2_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K3L2; + module->subdev = subdev_module; + module->device = SENSOR_S5K3L2_INSTANCE; + module->client = client; + module->active_width = 4128; + module->active_height = 3096; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 120; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K3L2"; + module->setfile_name = "setfile_3l2.bin"; + module->cfgs = ARRAY_SIZE(config_3l2); + module->cfg = config_3l2; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_S5K3L2; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x20; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_DWXXXX; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->actuator_con.peri_setting.i2c.slave_address = 0x20; + ext->actuator_con.peri_setting.i2c.speed = 400000; + + ext->flash_con.product_name = FLADRV_NAME_AS3643; + ext->flash_con.peri_type = SE_I2C; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 2; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 3; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.h new file mode 100644 index 000000000000..816dbdd243c1 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-3l2.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_3L2_H +#define FIMC_IS_DEVICE_3L2_H + +#define SENSOR_S5K3L2_INSTANCE 0 +#define SENSOR_S5K3L2_NAME SENSOR_NAME_S5K3L2 + +int sensor_3l2_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.c new file mode 100644 index 000000000000..25b4443b2614 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.c @@ -0,0 +1,145 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-4e5.h" + +#define SENSOR_NAME "S5K4E5" + +static int sensor_4e5_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_4e5_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_4e5_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K4E5_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K4E5; + module->subdev = subdev_module; + module->device = SENSOR_S5K4E5_INSTANCE; + module->client = client; + module->pixel_width = 2560 + 16; + module->pixel_height = 1920 + 10; + module->active_width = 2560; + module->active_height = 1920; + module->max_framerate = 30; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K4E5"; + module->setfile_name = "setfile_4e5.bin"; + module->cfgs = 0; + module->cfg = NULL; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + + ext->actuator_con.product_name = ACTUATOR_NAME_DWXXXX; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + + ext->flash_con.product_name = FLADRV_NAME_KTD267; + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 17; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 16; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.h new file mode 100644 index 000000000000..4d41ed03941e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4e5.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_4E5_H +#define FIMC_IS_DEVICE_4E5_H + +#define SENSOR_S5K4E5_INSTANCE 0 +#define SENSOR_S5K4E5_NAME SENSOR_NAME_S5K4E5 + +int sensor_4e5_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.c new file mode 100644 index 000000000000..b12063b5fe71 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.c @@ -0,0 +1,168 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-4h5.h" + +#define SENSOR_NAME "S5K4H5" + +static struct fimc_is_sensor_cfg config_4h5[] = { + /* 3280x2458@30fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 30, 14, 0), + /* 3280x1846@30fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 30, 11, 1), + /* 1640x924@60fps */ + FIMC_IS_SENSOR_CFG(1640, 924, 60, 14, 2), + /* 816x460@120fps */ + FIMC_IS_SENSOR_CFG(816, 460, 120, 14, 3), +}; + +static int sensor_4h5_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_4h5_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_4h5_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K4H5_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_S5K4H5; + module->subdev = subdev_module; + module->device = SENSOR_S5K4H5_INSTANCE; + module->ops = NULL; + module->client = client; + module->active_width = 3264; + module->active_height = 2448; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 120; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K4H5"; + module->setfile_name = "setfile_4h5.bin"; + module->cfgs = ARRAY_SIZE(config_4h5); + module->cfg = config_4h5; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x6E; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_DW9804; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel + = SENSOR_CONTROL_I2C0; + + ext->flash_con.product_name = FLADRV_NAME_RT5033; + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 8; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 6; + + /* ext->from_con.product_name = FROMDRV_NAME_W25Q80BW; */ + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + ext->mclk = 0; + ext->mipi_lane_num = 0; + ext->mipi_speed = 0; + ext->fast_open_sensor = 0; + ext->self_calibration_mode = 0; + ext->I2CSclk = I2C_L0; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.h new file mode 100644 index 000000000000..240792155f7d --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-4h5.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_S5K4H5_H +#define FIMC_IS_DEVICE_S5K4H5_H + +#define SENSOR_S5K4H5_INSTANCE 0 +#define SENSOR_S5K4H5_NAME SENSOR_NAME_S5K4H5 + +int sensor_4h5_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.c new file mode 100644 index 000000000000..5693f86c1978 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.c @@ -0,0 +1,148 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-6a3.h" + +#define SENSOR_NAME "S5K6A3" + +static struct fimc_is_sensor_cfg config_6a3[] = { + /* 1412x796@30fps */ + FIMC_IS_SENSOR_CFG(1412, 796, 30, 16, 0), +}; + +static int sensor_6a3_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_6a3_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_6a3_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int enum_idx = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K6A3_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + enum_idx = atomic_read(&core->resourcemgr.rsccount_module); + module = &device->module_enum[enum_idx]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_S5K6A3_NAME; + module->subdev = subdev_module; + module->device = SENSOR_S5K6A3_INSTANCE; + module->client = client; + module->active_width = 1392; + module->active_height = 1402; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_FRONT; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_1; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K6A3"; + module->setfile_name = "setfile_6a3.bin"; + module->cfgs = ARRAY_SIZE(config_6a3); + module->cfg = config_6a3; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + ext->mipi_settle_line = 18; + + ext->sensor_con.product_name = SENSOR_S5K6A3_NAME; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->sensor_con.peri_setting.i2c.slave_address = 0x20; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.h new file mode 100644 index 000000000000..bebfd8851f84 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6a3.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_6A3_H +#define FIMC_IS_DEVICE_6A3_H + +#define SENSOR_S5K6A3_INSTANCE 1 +#define SENSOR_S5K6A3_NAME SENSOR_NAME_S5K6A3 + +int sensor_6a3_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.c new file mode 100644 index 000000000000..6db6c49182ec --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.c @@ -0,0 +1,785 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-6b2.h" + +#define SENSOR_NAME "S5K6B2" +#define DEFAULT_SENSOR_WIDTH 184 +#define DEFAULT_SENSOR_HEIGHT 104 +#define SENSOR_MEMSIZE DEFAULT_SENSOR_WIDTH * DEFAULT_SENSOR_HEIGHT + +#define SENSOR_REG_VIS_DURATION_MSB (0x6026) +#define SENSOR_REG_VIS_DURATION_LSB (0x6027) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_MSB (0x4340) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_LSB (0x4341) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_MSB (0x4342) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_LSB (0x4343) +#define SENSOR_REG_VIS_GAIN_RED (0x6029) +#define SENSOR_REG_VIS_GAIN_GREEN (0x602A) +#define SENSOR_REG_VIS_AE_TARGET (0x600A) +#define SENSOR_REG_VIS_AE_SPEED (0x5034) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_MSB (0x5030) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_LSB (0x5031) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x1_2 (0x6000) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x3_4 (0x6001) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x1_2 (0x6002) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x3_4 (0x6003) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x1_2 (0x6004) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x3_4 (0x6005) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x1_2 (0x6006) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x3_4 (0x6007) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_MSB (0x5039) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_LSB (0x503A) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_MSB (0x503B) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_LSB (0x503C) +#define SENSOR_REG_VIS_BIT_CONVERTING_MSB (0x602B) +#define SENSOR_REG_VIS_BIT_CONVERTING_LSB (0x7203) +#define SENSOR_REG_VIS_AE_OFF (0x5000) + +static struct fimc_is_sensor_cfg config_6b2[] = { + /* 1936x1090@30fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 30, 16, 0), + /* 1936x1090@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 24, 13, 1), + /* 1296x730@30fps */ + FIMC_IS_SENSOR_CFG(1296, 730, 30, 13, 2), +}; + +static int sensor_6b2_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_6b2_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_6b2_registered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); + return 0; +} + +static void sensor_6b2_unregistered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); +} + +static const struct v4l2_subdev_internal_ops internal_ops = { + .open = sensor_6b2_open, + .close = sensor_6b2_close, + .registered = sensor_6b2_registered, + .unregistered = sensor_6b2_unregistered, +}; + +static int sensor_6b2_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + struct fimc_is_module_6b2 *module_6b2; + struct i2c_client *client; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + module_6b2 = module->private_data; + client = module->client; + + module_6b2->system_clock = 146 * 1000 * 1000; + module_6b2->line_length_pck = 146 * 1000 * 1000; + + pr_info("%s\n", __func__); + /* sensor init */ + /* 8 bit mode */ + fimc_is_sensor_write8(client, 0x7203, 0x40); + fimc_is_sensor_write8(client, 0x602B, 0x02); + fimc_is_sensor_write8(client, 0x702A, 0x3D); + fimc_is_sensor_write8(client, 0x702B, 0xB0); + fimc_is_sensor_write8(client, 0x7030, 0x0E); + fimc_is_sensor_write8(client, 0x7031, 0x2F); + + /* Analog Tuning */ + fimc_is_sensor_write8(client, 0x7067, 0x00); + fimc_is_sensor_write8(client, 0x7073, 0xFF); + fimc_is_sensor_write8(client, 0x7074, 0x22); + + /* Dark Tuning */ + fimc_is_sensor_write8(client, 0x7042, 0x1F); + fimc_is_sensor_write8(client, 0x7403, 0xC0); + fimc_is_sensor_write8(client, 0x7245, 0x04); + fimc_is_sensor_write8(client, 0x7205, 0xA1); + + /* Remove Dark Band */ + fimc_is_sensor_write8(client, 0x7430, 0x07); + fimc_is_sensor_write8(client, 0x705C, 0x7E); + + /* Remove Sun spot */ + fimc_is_sensor_write8(client, 0x702C, 0x3C); + fimc_is_sensor_write8(client, 0x7075, 0x3D); + + /* Remove CFPN */ + fimc_is_sensor_write8(client, 0x7066, 0x0C); + + /* AE setting */ + fimc_is_sensor_write8(client, 0x6000, 0x44); + fimc_is_sensor_write8(client, 0x6001, 0x44); + fimc_is_sensor_write8(client, 0x6002, 0x44); + fimc_is_sensor_write8(client, 0x6003, 0x44); + fimc_is_sensor_write8(client, 0x6004, 0x44); + fimc_is_sensor_write8(client, 0x6005, 0x44); + fimc_is_sensor_write8(client, 0x6006, 0x44); + fimc_is_sensor_write8(client, 0x6007, 0x44); + + /* AE target */ + fimc_is_sensor_write8(client, 0x600A, 0xB4); + + /* speed */ + fimc_is_sensor_write8(client, 0x5034, 0x00); + + /* Cintc_min */ + fimc_is_sensor_write8(client, 0x5017, 0x01); + + /* Number of pixel */ + fimc_is_sensor_write8(client, 0x5030, 0x4A); + fimc_is_sensor_write8(client, 0x5031, 0xC0); + + /* G + R Setting */ + /* Vision Senser Data = 0.5*Gr + 0.5*R */ + fimc_is_sensor_write8(client, 0x6029, 0x02); + fimc_is_sensor_write8(client, 0x602A, 0x02); + + /* For Analog Gain 16x */ + fimc_is_sensor_write8(client, 0x7018, 0xCF); + fimc_is_sensor_write8(client, 0x7019, 0xDB); + fimc_is_sensor_write8(client, 0x702A, 0x8D); + fimc_is_sensor_write8(client, 0x702B, 0x60); + fimc_is_sensor_write8(client, 0x5035, 0x02); + + /* BIT_RATE_MBPS_alv */ + fimc_is_sensor_write8(client, 0x7351, 0x02); + fimc_is_sensor_write8(client, 0x7352, 0x48); + fimc_is_sensor_write8(client, 0x7353, 0x00); + fimc_is_sensor_write8(client, 0x7354, 0x00); + + fimc_is_sensor_write8(client, 0x7339, 0x03); + + /* Analog gain */ + fimc_is_sensor_write8(client, 0x4204, 0x00); + fimc_is_sensor_write8(client, 0x4205, 0x32); + + /* frame rate - default 10fps*/ + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_MSB, 0x00); + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_LSB, 0x6A); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_6b2_init +}; + +static int sensor_6b2_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (enable) { + ret = CALL_MOPS(sensor, stream_on, subdev); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } else { + ret = CALL_MOPS(sensor, stream_off, subdev); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } + +p_err: + return 0; +} + +static int sensor_6b2_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + u64 duration; + + BUG_ON(!subdev); + BUG_ON(!param); + + pr_info("%s\n", __func__); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + if (!tpf->denominator) { + err("denominator is 0"); + ret = -EINVAL; + goto p_err; + } + + if (!tpf->numerator) { + err("numerator is 0"); + ret = -EINVAL; + goto p_err; + } + + duration = (u64)(tpf->numerator * 1000 * 1000 * 1000) / + (u64)(tpf->denominator); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_MOPS(sensor, s_duration, subdev, duration); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +static int sensor_6b2_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + /* TODO */ + return 0; +} + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = sensor_6b2_s_stream, + .s_parm = sensor_6b2_s_param, + .s_mbus_fmt = sensor_6b2_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +int sensor_6b2_stream_on(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 1); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int sensor_6b2_stream_off(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 0); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +/* + * @ brief + * frame duration time + * @ unit + * nano second + * @ remarks + */ +int sensor_6b2_s_duration(struct v4l2_subdev *subdev, u64 duration) +{ + int ret = 0; + u8 value[2]; + u32 framerate, result; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + framerate = 1000 * 1000 * 1000 / (u32)duration; + result = 1060 / framerate; + value[0] = result & 0xFF; + value[1] = (result >> 8) & 0xFF; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_MSB, value[1]); + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_LSB, value[0]); + +p_err: + return ret; +} + +int sensor_6b2_g_min_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_g_max_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_s_exposure(struct v4l2_subdev *subdev, u64 exposure) +{ + int ret = 0; + u8 value; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s(%d)\n", __func__, (u32)exposure); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + value = exposure & 0xFF; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_AE_TARGET, value); + +p_err: + return ret; +} + +int sensor_6b2_g_min_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_g_max_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_s_again(struct v4l2_subdev *subdev, u64 sensitivity) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + return ret; +} + +int sensor_6b2_g_min_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_g_max_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_s_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_g_min_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6b2_g_max_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +struct fimc_is_sensor_ops module_6b2_ops = { + .stream_on = sensor_6b2_stream_on, + .stream_off = sensor_6b2_stream_off, + .s_duration = sensor_6b2_s_duration, + .g_min_duration = sensor_6b2_g_min_duration, + .g_max_duration = sensor_6b2_g_max_duration, + .s_exposure = sensor_6b2_s_exposure, + .g_min_exposure = sensor_6b2_g_min_exposure, + .g_max_exposure = sensor_6b2_g_max_exposure, + .s_again = sensor_6b2_s_again, + .g_min_again = sensor_6b2_g_min_again, + .g_max_again = sensor_6b2_g_max_again, + .s_dgain = sensor_6b2_s_dgain, + .g_min_dgain = sensor_6b2_g_min_dgain, + .g_max_dgain = sensor_6b2_g_max_dgain +}; + +#ifdef CONFIG_OF +static int sensor_6b2_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_none = 0; + int gpio_reset = 0, gpio_standby = 0; + int gpios_cam_en = 0; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + err("failed to get PIN_RESET"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + err("failed to get front cam en gpio"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + + gpio_standby = of_get_named_gpio(dnode, "gpio_standby", 0); + if (!gpio_is_valid(gpio_standby)) { + err("failed to get gpio_standby"); + } else { + gpio_request_one(gpio_standby, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_standby); + } + + /* FRONT CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* FRONT CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 1, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 4, gpio_standby, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 2, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 5, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif + +int sensor_6b2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K6B2_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + /* S5K6B2 */ + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_S5K6B2_NAME; + module->subdev = subdev_module; + module->device = SENSOR_S5K6B2_INSTANCE; + module->ops = &module_6b2_ops; + module->client = client; + module->active_width = 1920; + module->active_height = 1080; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_FRONT; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_1; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K6B2"; + module->setfile_name = "setfile_6b2.bin"; + module->cfgs = ARRAY_SIZE(config_6b2); + module->cfg = config_6b2; + module->private_data = kzalloc(sizeof(struct fimc_is_module_6b2), GFP_KERNEL); + if (!module->private_data) { + err("private_data is NULL"); + ret = -ENOMEM; + kfree(subdev_module); + goto p_err; + } +#ifdef CONFIG_OF + module->power_setpin = sensor_6b2_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + ext->sensor_con.product_name = SENSOR_NAME_S5K6B2; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->sensor_con.peri_setting.i2c.slave_address = 0; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) { + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + subdev_module->internal_ops = &internal_ops; + } else { + v4l2_subdev_init(subdev_module, &subdev_ops); + } + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + BUG(); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; +} + +static int sensor_6b2_remove(struct i2c_client *client) +{ + int ret = 0; + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_6b2_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-sensor-6b2", + }, + {}, +}; +#endif + +static const struct i2c_device_id sensor_6b2_idt[] = { + { SENSOR_NAME, 0 }, +}; + +static struct i2c_driver sensor_6b2_driver = { + .driver = { + .name = SENSOR_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = exynos_fimc_is_sensor_6b2_match +#endif + }, + .probe = sensor_6b2_probe, + .remove = sensor_6b2_remove, + .id_table = sensor_6b2_idt +}; + +static int __init sensor_6b2_load(void) +{ + return i2c_add_driver(&sensor_6b2_driver); +} + +static void __exit sensor_6b2_unload(void) +{ + i2c_del_driver(&sensor_6b2_driver); +} + +module_init(sensor_6b2_load); +module_exit(sensor_6b2_unload); + +MODULE_AUTHOR("Gilyeon lim"); +MODULE_DESCRIPTION("Sensor 6B2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.h new file mode 100644 index 000000000000..284f08a310b2 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6b2.h @@ -0,0 +1,28 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_6B2_H +#define FIMC_IS_DEVICE_6B2_H + +#define SENSOR_S5K6B2_INSTANCE 1 +#define SENSOR_S5K6B2_NAME SENSOR_NAME_S5K6B2 + +struct fimc_is_module_6b2 { + u16 vis_duration; + u16 frame_length_line; + u32 line_length_pck; + u32 system_clock; +}; + +int sensor_6b2_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.c new file mode 100644 index 000000000000..82ba5400c225 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.c @@ -0,0 +1,843 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-6d1.h" + +#define SENSOR_NAME "S5K6D1" +#define DEFAULT_SENSOR_WIDTH 184 +#define DEFAULT_SENSOR_HEIGHT 104 +#define SENSOR_MEMSIZE DEFAULT_SENSOR_WIDTH * DEFAULT_SENSOR_HEIGHT + +#define SENSOR_REG_VIS_DURATION_MSB (0x6026) +#define SENSOR_REG_VIS_DURATION_LSB (0x6027) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_MSB (0x4340) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_LSB (0x4341) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_MSB (0x4342) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_LSB (0x4343) +#define SENSOR_REG_VIS_GAIN_RED (0x6029) +#define SENSOR_REG_VIS_GAIN_GREEN (0x602A) +#define SENSOR_REG_VIS_AE_TARGET (0x600A) +#define SENSOR_REG_VIS_AE_SPEED (0x5034) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_MSB (0x5030) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_LSB (0x5031) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x1_2 (0x6000) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x3_4 (0x6001) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x1_2 (0x6002) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x3_4 (0x6003) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x1_2 (0x6004) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x3_4 (0x6005) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x1_2 (0x6006) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x3_4 (0x6007) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_MSB (0x5039) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_LSB (0x503A) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_MSB (0x503B) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_LSB (0x503C) +#define SENSOR_REG_VIS_BIT_CONVERTING_MSB (0x602B) +#define SENSOR_REG_VIS_BIT_CONVERTING_LSB (0x7203) +#define SENSOR_REG_VIS_AE_OFF (0x5000) + +static u16 setfile_vision_6d1[][2] = { + {0x4200, 0x02}, + {0x4201, 0xAE}, + {0x4301, 0x04}, + {0x4309, 0x04}, + {0x4345, 0x08}, + {0x4347, 0x08}, + {0x4348, 0x0A}, + {0x4349, 0x01}, + {0x434A, 0x05}, + {0x434B, 0xA0}, + {0x434C, 0x01}, + {0x434D, 0x40}, + {0x434E, 0x00}, + {0x434F, 0xB4}, + {0x4381, 0x01}, + {0x4383, 0x07}, + {0x4385, 0x08}, + {0x4387, 0x08}, + {0x5004, 0x01}, + {0x5005, 0x1E}, + {0x5014, 0x11}, + {0x5015, 0x9E}, + {0x5016, 0x00}, + {0x5017, 0x02}, + {0x5030, 0x1C}, + {0x5031, 0x20}, + {0x5034, 0x00}, + {0x5035, 0x02}, + {0x5036, 0x00}, + {0x5037, 0x06}, + {0x5038, 0xC0}, + {0x5039, 0x00}, + {0x503A, 0x00}, + {0x503B, 0x00}, + {0x503C, 0x00}, + {0x503D, 0x20}, + {0x503E, 0x70}, + {0x503F, 0x02}, + {0x600A, 0x2A}, + {0x600E, 0x05}, + {0x6014, 0x27}, + {0x6015, 0x1D}, + {0x6018, 0x01}, + {0x6026, 0x00}, + {0x6027, 0x68}, + {0x6029, 0x08}, + {0x602A, 0x08}, + {0x602B, 0x00}, + {0x602C, 0x00}, + {0x7008, 0x00}, + {0x7009, 0x10}, + {0x700A, 0x00}, + {0x700B, 0x10}, + {0x7014, 0x2B}, + {0x7015, 0x91}, + {0x7016, 0x82}, + {0x701B, 0x16}, + {0x701D, 0x0B}, + {0x701F, 0x0B}, + {0x7026, 0x1A}, + {0x7027, 0x46}, + {0x7029, 0x14}, + {0x702A, 0x02}, + {0x7038, 0x01}, + {0x7039, 0x14}, + {0x703A, 0x32}, + {0x703B, 0x22}, + {0x7040, 0x01}, + {0x7041, 0x14}, + {0x7042, 0x32}, + {0x7043, 0x22}, + {0x7050, 0x0A}, + {0x7051, 0xA8}, + {0x7052, 0x35}, + {0x7053, 0x54}, + {0x7054, 0x00}, + {0x7055, 0x00}, + {0x7056, 0x00}, + {0x7057, 0x00}, + {0x705E, 0x0E}, + {0x705F, 0x10}, + {0x7060, 0x01}, + {0x7064, 0x05}, + {0x7065, 0x3C}, + {0x7066, 0x00}, + {0x7067, 0x00}, + {0x7068, 0x4A}, + {0x706C, 0x01}, + {0x7077, 0x88}, + {0x7078, 0x88}, + {0x7082, 0x90}, + {0x7091, 0x05}, + {0x7098, 0x00}, + {0x7112, 0x01}, + {0x720A, 0x06}, + {0x720B, 0x80}, + {0x7245, 0xC1}, + {0x7301, 0x01}, + {0x7305, 0x13}, + {0x7306, 0x01}, + {0x7323, 0x01}, + {0x7339, 0x07}, + {0x7351, 0x01}, + {0x7352, 0x24}, + {0x7405, 0x28}, + {0x7406, 0x28}, + {0x7407, 0xC0}, + {0x7454, 0x01}, + {0x7460, 0x01}, + {0x7461, 0x20}, + {0x7462, 0xC0}, + {0x7463, 0x1E}, + {0x7464, 0x02}, + {0x7465, 0x4B}, + {0x7467, 0x20}, + {0x7468, 0x20}, + {0x7469, 0x20}, + {0x746A, 0x20}, + {0x746B, 0x20}, + {0x746C, 0x20}, + {0x746D, 0x09}, + {0x746E, 0xFF}, + {0x746F, 0x01}, + {0x7472, 0x00}, + {0x7473, 0x02}, + {0x7474, 0xC1}, + {0x7475, 0x00}, + {0x7476, 0x00}, + {0x7477, 0x00}, + {0x7478, 0x00}, + {0x4100, 0x01}, +}; + +static struct fimc_is_sensor_cfg config_6d1[] = { + /* 2576x1456@30fps */ + FIMC_IS_SENSOR_CFG(2576, 1456, 30, 14, 0), + /* 2576x1456@24fps */ + FIMC_IS_SENSOR_CFG(2576, 1456, 24, 14, 1), +#if 0 + /* 1924x1082@30fps */ + FIMC_IS_SENSOR_CFG(1924, 1082, 30, 16, 1), + /* 1444x1082@30fps */ + FIMC_IS_SENSOR_CFG(1444, 1082, 30, 16, 2), + /* 1084x1082@30fps */ + FIMC_IS_SENSOR_CFG(1084, 1082, 30, 16, 3), + /* 964x542@30fps */ + FIMC_IS_SENSOR_CFG(964, 542, 30, 16, 4), + /* 724x542@30fps */ + FIMC_IS_SENSOR_CFG(724, 542, 30, 16, 5), + /* 544x542@30fps */ + FIMC_IS_SENSOR_CFG(544, 542, 30, 16, 6), + /* 320x180@10fps : only for vision(settle) */ + FIMC_IS_SENSOR_CFG(320, 180, 10, 4, 6), + /* 1936x1090@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 24, 13, 7), +#endif +}; + +static int sensor_6d1_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_6d1_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_6d1_registered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); + return 0; +} + +static void sensor_6d1_unregistered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); +} + +static const struct v4l2_subdev_internal_ops internal_ops = { + .open = sensor_6d1_open, + .close = sensor_6d1_close, + .registered = sensor_6d1_registered, + .unregistered = sensor_6d1_unregistered, +}; + +static int sensor_6d1_init(struct v4l2_subdev *subdev, u32 val) +{ + int i, ret = 0; + struct fimc_is_module_enum *module; + struct fimc_is_module_6d1 *module_6d1; + struct i2c_client *client; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + module_6d1 = module->private_data; + client = module->client; + + module_6d1->system_clock = 146 * 1000 * 1000; + module_6d1->line_length_pck = 146 * 1000 * 1000; + + pr_info("%s\n", __func__); + + /* sensor init */ + for (i = 0; i < ARRAY_SIZE(setfile_vision_6d1); i++) { + fimc_is_sensor_write8(client, setfile_vision_6d1[i][0], + (u8)setfile_vision_6d1[i][1]); + } + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_6d1_init +}; + +static int sensor_6d1_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (enable) { + ret = CALL_MOPS(sensor, stream_on, subdev); + if (ret < 0) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } else { + ret = CALL_MOPS(sensor, stream_off, subdev); + if (ret < 0) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } + +p_err: + return 0; +} + +static int sensor_6d1_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + u64 duration; + + BUG_ON(!subdev); + BUG_ON(!param); + + pr_info("%s\n", __func__); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + if (!tpf->denominator) { + err("denominator is 0"); + ret = -EINVAL; + goto p_err; + } + + if (!tpf->numerator) { + err("numerator is 0"); + ret = -EINVAL; + goto p_err; + } + + duration = (u64)(tpf->numerator * 1000 * 1000 * 1000) / + (u64)(tpf->denominator); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_MOPS(sensor, s_duration, subdev, duration); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +static int sensor_6d1_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + /* TODO */ + return 0; +} + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = sensor_6d1_s_stream, + .s_parm = sensor_6d1_s_param, + .s_mbus_fmt = sensor_6d1_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +int sensor_6d1_stream_on(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 1); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int sensor_6d1_stream_off(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 0); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +/* + * @ brief + * frame duration time + * @ unit + * nano second + * @ remarks + */ +int sensor_6d1_s_duration(struct v4l2_subdev *subdev, u64 duration) +{ + int ret = 0; + u8 value[2]; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + /* + * forcely set 10fps for 6d1, + */ + value[0] = 0x52; + value[1] = 0x0; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_MSB, value[1]); + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_LSB, value[0]); + +p_err: + return ret; +} + +int sensor_6d1_g_min_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_g_max_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_s_exposure(struct v4l2_subdev *subdev, u64 exposure) +{ + int ret = 0; + u8 value; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s(%d)\n", __func__, (u32)exposure); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + value = exposure & 0xFF; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_AE_TARGET, value); + +p_err: + return ret; +} + +int sensor_6d1_g_min_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_g_max_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_s_again(struct v4l2_subdev *subdev, u64 sensitivity) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + return ret; +} + +int sensor_6d1_g_min_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_g_max_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_s_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_g_min_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_6d1_g_max_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +struct fimc_is_sensor_ops module_6d1_ops = { + .stream_on = sensor_6d1_stream_on, + .stream_off = sensor_6d1_stream_off, + .s_duration = sensor_6d1_s_duration, + .g_min_duration = sensor_6d1_g_min_duration, + .g_max_duration = sensor_6d1_g_max_duration, + .s_exposure = sensor_6d1_s_exposure, + .g_min_exposure = sensor_6d1_g_min_exposure, + .g_max_exposure = sensor_6d1_g_max_exposure, + .s_again = sensor_6d1_s_again, + .g_min_again = sensor_6d1_g_min_again, + .g_max_again = sensor_6d1_g_max_again, + .s_dgain = sensor_6d1_s_dgain, + .g_min_dgain = sensor_6d1_g_min_dgain, + .g_max_dgain = sensor_6d1_g_max_dgain +}; + +#ifdef CONFIG_OF +static int sensor_6d1_power_setpin(struct device *dev) +{ + int gpio_none = 0, gpio_reset = 0, gpio_standby = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + struct pinctrl *pinctrl_ch = NULL; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpio_standby = of_get_named_gpio(dnode, "gpio_standby", 0); + if (!gpio_is_valid(gpio_standby)) { + dev_err(dev, "failed to get gpio_standby\n"); + } else { + gpio_request_one(gpio_standby, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_standby); + } + + /* initial - i2c off */ + pinctrl_ch = devm_pinctrl_get_select(dev, "off1"); + if (IS_ERR_OR_NULL(pinctrl_ch)) { + pr_err("%s: cam %s pins are not configured\n", __func__, "off1"); + } else { + devm_pinctrl_put(pinctrl_ch); + } + + /* FRONT CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* FRONT CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 4, gpio_standby, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 2, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif + +int sensor_6d1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K6D1_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + /* S5K6D1 */ + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_S5K6D1_NAME; + module->subdev = subdev_module; + module->device = SENSOR_S5K6D1_INSTANCE; + module->ops = &module_6d1_ops; + module->client = client; + module->active_width = 2560; + module->active_height = 1446; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_FRONT; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_2; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K6D1"; + module->setfile_name = "setfile_6d1.bin"; + module->cfgs = ARRAY_SIZE(config_6d1); + module->cfg = config_6d1; + module->private_data = kzalloc(sizeof(struct fimc_is_module_6d1), GFP_KERNEL); + if (!module->private_data) { + err("private_data is NULL"); + ret = -ENOMEM; + kfree(subdev_module); + goto p_err; + } +#ifdef CONFIG_OF + module->power_setpin = sensor_6d1_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + ext->sensor_con.product_name = SENSOR_NAME_S5K6D1; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->sensor_con.peri_setting.i2c.slave_address = 0; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) { + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + subdev_module->internal_ops = &internal_ops; + } else { + v4l2_subdev_init(subdev_module, &subdev_ops); + } + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + BUG(); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; +} + +static int sensor_6d1_remove(struct i2c_client *client) +{ + int ret = 0; + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_6d1_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-sensor-6d1", + }, + {}, +}; +#endif + +static const struct i2c_device_id sensor_6d1_idt[] = { + { SENSOR_NAME, 0 }, +}; + +static struct i2c_driver sensor_6d1_driver = { + .driver = { + .name = SENSOR_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = exynos_fimc_is_sensor_6d1_match +#endif + }, + .probe = sensor_6d1_probe, + .remove = sensor_6d1_remove, + .id_table = sensor_6d1_idt +}; + +static int __init sensor_6d1_load(void) +{ + return i2c_add_driver(&sensor_6d1_driver); +} + +static void __exit sensor_6d1_unload(void) +{ + i2c_del_driver(&sensor_6d1_driver); +} + +module_init(sensor_6d1_load); +module_exit(sensor_6d1_unload); + +MODULE_AUTHOR("Gilyeon lim"); +MODULE_DESCRIPTION("Sensor 6D1 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.h new file mode 100644 index 000000000000..c6c46caab178 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-6d1.h @@ -0,0 +1,28 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_6D1_H +#define FIMC_IS_DEVICE_6D1_H + +#define SENSOR_S5K6D1_INSTANCE 1 +#define SENSOR_S5K6D1_NAME SENSOR_NAME_S5K6D1 + +struct fimc_is_module_6d1 { + u16 vis_duration; + u16 frame_length_line; + u32 line_length_pck; + u32 system_clock; +}; + +int sensor_6d1_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.c new file mode 100644 index 000000000000..936bf4cec37f --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.c @@ -0,0 +1,850 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-8b1.h" + +#define SENSOR_NAME "S5K8B1" +#define DEFAULT_SENSOR_WIDTH 184 +#define DEFAULT_SENSOR_HEIGHT 104 +#define SENSOR_MEMSIZE DEFAULT_SENSOR_WIDTH * DEFAULT_SENSOR_HEIGHT + +#define SENSOR_REG_VIS_DURATION_MSB (0x6026) +#define SENSOR_REG_VIS_DURATION_LSB (0x6027) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_MSB (0x4340) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_LSB (0x4341) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_MSB (0x4342) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_LSB (0x4343) +#define SENSOR_REG_VIS_GAIN_RED (0x6029) +#define SENSOR_REG_VIS_GAIN_GREEN (0x602A) +#define SENSOR_REG_VIS_AE_TARGET (0x600A) +#define SENSOR_REG_VIS_AE_SPEED (0x5034) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_MSB (0x5030) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_LSB (0x5031) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x1_2 (0x6000) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x3_4 (0x6001) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x1_2 (0x6002) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x3_4 (0x6003) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x1_2 (0x6004) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x3_4 (0x6005) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x1_2 (0x6006) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x3_4 (0x6007) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_MSB (0x5039) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_LSB (0x503A) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_MSB (0x503B) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_LSB (0x503C) +#define SENSOR_REG_VIS_BIT_CONVERTING_MSB (0x602B) +#define SENSOR_REG_VIS_BIT_CONVERTING_LSB (0x7203) +#define SENSOR_REG_VIS_AE_OFF (0x5000) + +static struct fimc_is_sensor_cfg config_8b1[] = { + /* 1936x1090@30fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 30, 16, 0), + /* 1924x1082@30fps */ + FIMC_IS_SENSOR_CFG(1924, 1082, 30, 16, 1), + /* 1444x1082@30fps */ + FIMC_IS_SENSOR_CFG(1444, 1082, 30, 16, 2), + /* 1084x1082@30fps */ + FIMC_IS_SENSOR_CFG(1084, 1082, 30, 16, 3), + /* 964x542@30fps */ + FIMC_IS_SENSOR_CFG(964, 542, 30, 16, 4), + /* 724x542@30fps */ + FIMC_IS_SENSOR_CFG(724, 542, 30, 16, 5), + /* 544x542@30fps */ + FIMC_IS_SENSOR_CFG(544, 542, 30, 16, 6), + /* 320x180@10fps : only for vision(settle) */ + FIMC_IS_SENSOR_CFG(320, 180, 10, 4, 6), + /* 1936x1090@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 24, 13, 7), +}; + +static int sensor_8b1_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_8b1_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_8b1_registered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); + return 0; +} + +static void sensor_8b1_unregistered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); +} + +static const struct v4l2_subdev_internal_ops internal_ops = { + .open = sensor_8b1_open, + .close = sensor_8b1_close, + .registered = sensor_8b1_registered, + .unregistered = sensor_8b1_unregistered, +}; + +static int sensor_8b1_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + struct fimc_is_module_8b1 *module_8b1; + struct i2c_client *client; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + module_8b1 = module->private_data; + client = module->client; + + module_8b1->system_clock = 146 * 1000 * 1000; + module_8b1->line_length_pck = 146 * 1000 * 1000; + + pr_info("%s\n", __func__); + /* sensor init */ + fimc_is_sensor_write8(client, 0x4200, 0x01); + fimc_is_sensor_write8(client, 0x4201, 0x24); + fimc_is_sensor_write8(client, 0x4305, 0x06); + fimc_is_sensor_write8(client, 0x4307, 0xC0); + fimc_is_sensor_write8(client, 0x4342, 0x03); + + fimc_is_sensor_write8(client, 0x4343, 0x90); + fimc_is_sensor_write8(client, 0x4345, 0x08); + fimc_is_sensor_write8(client, 0x4347, 0x08); + fimc_is_sensor_write8(client, 0x4348, 0x07); + fimc_is_sensor_write8(client, 0x4349, 0x83); /* 10 */ + + fimc_is_sensor_write8(client, 0x434A, 0x04); + fimc_is_sensor_write8(client, 0x434B, 0x3A); + fimc_is_sensor_write8(client, 0x434C, 0x01); + fimc_is_sensor_write8(client, 0x434D, 0x40); + fimc_is_sensor_write8(client, 0x434E, 0x00); + + fimc_is_sensor_write8(client, 0x434F, 0xB4); + fimc_is_sensor_write8(client, 0x4381, 0x01); + fimc_is_sensor_write8(client, 0x4383, 0x05); + fimc_is_sensor_write8(client, 0x4385, 0x06); + fimc_is_sensor_write8(client, 0x4387, 0x06); /* 20 */ + + fimc_is_sensor_write8(client, 0x5004, 0x01); + fimc_is_sensor_write8(client, 0x5005, 0x1E); +#ifdef VISION_30FPS + fimc_is_sensor_write8(client, 0x5014, 0x05); + fimc_is_sensor_write8(client, 0x5015, 0x73); +#else + fimc_is_sensor_write8(client, 0x5014, 0x13); + fimc_is_sensor_write8(client, 0x5015, 0x33); +#endif + fimc_is_sensor_write8(client, 0x5016, 0x00); + + fimc_is_sensor_write8(client, 0x5017, 0x02); + fimc_is_sensor_write8(client, 0x5030, 0x0E); + fimc_is_sensor_write8(client, 0x5031, 0x10); + fimc_is_sensor_write8(client, 0x5034, 0x00); + fimc_is_sensor_write8(client, 0x5035, 0x02); /* 30 */ + + fimc_is_sensor_write8(client, 0x5036, 0x00); + fimc_is_sensor_write8(client, 0x5037, 0x04); + fimc_is_sensor_write8(client, 0x5038, 0xC0); + fimc_is_sensor_write8(client, 0x503D, 0x20); + fimc_is_sensor_write8(client, 0x503E, 0x70); + + fimc_is_sensor_write8(client, 0x503F, 0x02); + fimc_is_sensor_write8(client, 0x600A, 0x3A); + fimc_is_sensor_write8(client, 0x600E, 0x05); + fimc_is_sensor_write8(client, 0x6014, 0x27); + fimc_is_sensor_write8(client, 0x6015, 0x1D); /* 40 */ + + fimc_is_sensor_write8(client, 0x6018, 0x01); + fimc_is_sensor_write8(client, 0x6026, 0x00); +#ifdef VISION_30FPS + fimc_is_sensor_write8(client, 0x6027, 0x1B); +#else + fimc_is_sensor_write8(client, 0x6027, 0x52); +#endif + fimc_is_sensor_write8(client, 0x6029, 0x08); + fimc_is_sensor_write8(client, 0x602A, 0x08); + + fimc_is_sensor_write8(client, 0x602B, 0x00); + fimc_is_sensor_write8(client, 0x602c, 0x00); + fimc_is_sensor_write8(client, 0x6032, 0x63); + fimc_is_sensor_write8(client, 0x6033, 0x94); + fimc_is_sensor_write8(client, 0x7007, 0x18); /* 50 */ + + fimc_is_sensor_write8(client, 0x7015, 0x28); + fimc_is_sensor_write8(client, 0x7016, 0x2C); + fimc_is_sensor_write8(client, 0x7027, 0x14); + fimc_is_sensor_write8(client, 0x7028, 0x3C); + fimc_is_sensor_write8(client, 0x7029, 0x02); + + fimc_is_sensor_write8(client, 0x702A, 0x02); + fimc_is_sensor_write8(client, 0x703A, 0x04); + fimc_is_sensor_write8(client, 0x703B, 0x36); + fimc_is_sensor_write8(client, 0x7042, 0x04); + fimc_is_sensor_write8(client, 0x7043, 0x36); /* 60 */ + + fimc_is_sensor_write8(client, 0x7058, 0x6F); + fimc_is_sensor_write8(client, 0x705A, 0x01); + fimc_is_sensor_write8(client, 0x705C, 0x40); + fimc_is_sensor_write8(client, 0x7060, 0x07); + fimc_is_sensor_write8(client, 0x7061, 0x40); + + fimc_is_sensor_write8(client, 0x7064, 0x43); + fimc_is_sensor_write8(client, 0x706D, 0x77); + fimc_is_sensor_write8(client, 0x706E, 0xFA); + fimc_is_sensor_write8(client, 0x7070, 0x0A); + fimc_is_sensor_write8(client, 0x7073, 0x04); /* 70 */ + + fimc_is_sensor_write8(client, 0x7087, 0x00); + fimc_is_sensor_write8(client, 0x7090, 0x01); + fimc_is_sensor_write8(client, 0x7115, 0x01); + fimc_is_sensor_write8(client, 0x7209, 0xF5); + fimc_is_sensor_write8(client, 0x720B, 0xF5); + + fimc_is_sensor_write8(client, 0x7245, 0xC4); + fimc_is_sensor_write8(client, 0x7301, 0x02); + fimc_is_sensor_write8(client, 0x7306, 0x02); + fimc_is_sensor_write8(client, 0x7339, 0x03); + fimc_is_sensor_write8(client, 0x7351, 0x00); /* 80 */ + + fimc_is_sensor_write8(client, 0x7352, 0xC0); + fimc_is_sensor_write8(client, 0x7405, 0x28); + fimc_is_sensor_write8(client, 0x7406, 0x28); + fimc_is_sensor_write8(client, 0x7407, 0xC0); + fimc_is_sensor_write8(client, 0x740C, 0x60); + + fimc_is_sensor_write8(client, 0x740D, 0x00); + fimc_is_sensor_write8(client, 0x7436, 0x03); + fimc_is_sensor_write8(client, 0x7437, 0x03); + fimc_is_sensor_write8(client, 0x7454, 0x01); + fimc_is_sensor_write8(client, 0x7460, 0x00); /* 90 */ + + fimc_is_sensor_write8(client, 0x7461, 0x01); + fimc_is_sensor_write8(client, 0x7462, 0x68); + fimc_is_sensor_write8(client, 0x7463, 0x1E); + fimc_is_sensor_write8(client, 0x7464, 0x03); + fimc_is_sensor_write8(client, 0x7465, 0x4B); + + fimc_is_sensor_write8(client, 0x7467, 0x20); + fimc_is_sensor_write8(client, 0x7468, 0x20); + fimc_is_sensor_write8(client, 0x7469, 0x20); + fimc_is_sensor_write8(client, 0x746A, 0x20); + fimc_is_sensor_write8(client, 0x746B, 0x20); /* 100 */ + + fimc_is_sensor_write8(client, 0x746C, 0x20); + fimc_is_sensor_write8(client, 0x746D, 0x02); + fimc_is_sensor_write8(client, 0x746E, 0x80); + fimc_is_sensor_write8(client, 0x746F, 0x01); + fimc_is_sensor_write8(client, 0x4100, 0x01); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_8b1_init +}; + +static int sensor_8b1_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (enable) { + ret = CALL_MOPS(sensor, stream_on, subdev); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } else { + ret = CALL_MOPS(sensor, stream_off, subdev); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } + +p_err: + return 0; +} + +static int sensor_8b1_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + u64 duration; + + BUG_ON(!subdev); + BUG_ON(!param); + + pr_info("%s\n", __func__); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + if (!tpf->denominator) { + err("denominator is 0"); + ret = -EINVAL; + goto p_err; + } + + if (!tpf->numerator) { + err("numerator is 0"); + ret = -EINVAL; + goto p_err; + } + + duration = (u64)(tpf->numerator * 1000 * 1000 * 1000) / + (u64)(tpf->denominator); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_MOPS(sensor, s_duration, subdev, duration); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +static int sensor_8b1_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + /* TODO */ + return 0; +} + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = sensor_8b1_s_stream, + .s_parm = sensor_8b1_s_param, + .s_mbus_fmt = sensor_8b1_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +int sensor_8b1_stream_on(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 1); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int sensor_8b1_stream_off(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 0); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +/* + * @ brief + * frame duration time + * @ unit + * nano second + * @ remarks + */ +int sensor_8b1_s_duration(struct v4l2_subdev *subdev, u64 duration) +{ + int ret = 0; + u8 value[2]; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + /* + * forcely set 10fps for 8b1, + */ + value[0] = 0x52; + value[1] = 0x0; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_MSB, value[1]); + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_LSB, value[0]); + +p_err: + return ret; +} + +int sensor_8b1_g_min_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_g_max_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_s_exposure(struct v4l2_subdev *subdev, u64 exposure) +{ + int ret = 0; + u8 value; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s(%d)\n", __func__, (u32)exposure); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + value = exposure & 0xFF; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_AE_TARGET, value); + +p_err: + return ret; +} + +int sensor_8b1_g_min_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_g_max_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_s_again(struct v4l2_subdev *subdev, u64 sensitivity) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + return ret; +} + +int sensor_8b1_g_min_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_g_max_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_s_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_g_min_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_8b1_g_max_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +struct fimc_is_sensor_ops module_8b1_ops = { + .stream_on = sensor_8b1_stream_on, + .stream_off = sensor_8b1_stream_off, + .s_duration = sensor_8b1_s_duration, + .g_min_duration = sensor_8b1_g_min_duration, + .g_max_duration = sensor_8b1_g_max_duration, + .s_exposure = sensor_8b1_s_exposure, + .g_min_exposure = sensor_8b1_g_min_exposure, + .g_max_exposure = sensor_8b1_g_max_exposure, + .s_again = sensor_8b1_s_again, + .g_min_again = sensor_8b1_g_min_again, + .g_max_again = sensor_8b1_g_max_again, + .s_dgain = sensor_8b1_s_dgain, + .g_min_dgain = sensor_8b1_g_min_dgain, + .g_max_dgain = sensor_8b1_g_max_dgain +}; + +#ifdef CONFIG_OF +static int sensor_8b1_power_setpin(struct device *dev) +{ + int gpio_none = 0, gpio_reset = 0, gpio_standby = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpios_cam_en = 0; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + dev_err(dev, "failed to get main/front cam en gpio\n"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + + gpio_standby = of_get_named_gpio(dnode, "gpio_standby", 0); + if (!gpio_is_valid(gpio_standby)) { + dev_err(dev, "failed to get gpio_standby\n"); + } else { + gpio_request_one(gpio_standby, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_standby); + } + + /* FRONT CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpios_cam_en, 0, NULL, 1000, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* FRONT CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 3, gpios_cam_en, 0, NULL, 1000, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 4, gpio_standby, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 2, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 3, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif /* CONFIG_OF */ + +int sensor_8b1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_S5K8B1_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + /* S5K8B1 */ + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_S5K8B1_NAME; + module->subdev = subdev_module; + module->device = SENSOR_S5K8B1_INSTANCE; + module->ops = &module_8b1_ops; + module->client = client; + module->active_width = 1920; + module->active_height = 1080; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 30; + module->position = SENSOR_POSITION_FRONT; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_1; + module->sensor_maker = "SLSI"; + module->sensor_name = "S5K8B1"; + module->setfile_name = "setfile_8b1.bin"; + module->cfgs = ARRAY_SIZE(config_8b1); + module->cfg = config_8b1; + module->private_data = kzalloc(sizeof(struct fimc_is_module_8b1), GFP_KERNEL); + if (!module->private_data) { + err("private_data is NULL"); + ret = -ENOMEM; + kfree(subdev_module); + goto p_err; + } +#ifdef CONFIG_OF + module->power_setpin = sensor_8b1_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + ext->sensor_con.product_name = SENSOR_NAME_S5K8B1; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->sensor_con.peri_setting.i2c.slave_address = 0; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) { + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + subdev_module->internal_ops = &internal_ops; + } else { + v4l2_subdev_init(subdev_module, &subdev_ops); + } + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + BUG(); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; +} + +static int sensor_8b1_remove(struct i2c_client *client) +{ + int ret = 0; + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_8b1_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-sensor-8b1", + }, + {}, +}; +#endif + +static const struct i2c_device_id sensor_8b1_idt[] = { + { SENSOR_NAME, 0 }, +}; + +static struct i2c_driver sensor_8b1_driver = { + .driver = { + .name = SENSOR_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = exynos_fimc_is_sensor_8b1_match +#endif + }, + .probe = sensor_8b1_probe, + .remove = sensor_8b1_remove, + .id_table = sensor_8b1_idt +}; + +static int __init sensor_8b1_load(void) +{ + return i2c_add_driver(&sensor_8b1_driver); +} + +static void __exit sensor_8b1_unload(void) +{ + i2c_del_driver(&sensor_8b1_driver); +} + +module_init(sensor_8b1_load); +module_exit(sensor_8b1_unload); + +MODULE_AUTHOR("Gilyeon lim"); +MODULE_DESCRIPTION("Sensor 8B1 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.h new file mode 100644 index 000000000000..b6a76e4f21fd --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-8b1.h @@ -0,0 +1,28 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_8B1_H +#define FIMC_IS_DEVICE_8B1_H + +#define SENSOR_S5K8B1_INSTANCE 1 +#define SENSOR_S5K8B1_NAME SENSOR_NAME_S5K8B1 + +struct fimc_is_module_8b1 { + u16 vis_duration; + u16 frame_length_line; + u32 line_length_pck; + u32 system_clock; +}; + +int sensor_8b1_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.c new file mode 100644 index 000000000000..af121b36d996 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.c @@ -0,0 +1,265 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "fimc-is-device-imx134.h" + + +#include +#include "../fimc-is-hw.h" + + + +#define SENSOR_NAME "IMX134" + +static struct fimc_is_sensor_cfg config_imx134[] = { + /* 3280X2458@30fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 30, 15, 0), + /* 3280X1846@30fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 30, 11, 1), + /* 3280X2458@24fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 24, 12, 2), + /* 3280X1846@24fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 24, 9, 3), + /* 1936X1450@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1450, 24, 12, 4), + /* 1936X1090@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 24, 9, 5), + /* 816X460@120fps */ + FIMC_IS_SENSOR_CFG(816, 460, 120, 7, 6), + /* 1640X924@60fps */ + FIMC_IS_SENSOR_CFG(1640, 924, 60, 6, 7), +}; + + +static int sensor_imx134_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_imx134_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +#ifdef CONFIG_OF +#ifdef CONFIG_COMPANION_USE +static int sensor_imx134_power_setpin(struct device *dev) +{ + return 0; +} +#else +static int sensor_imx134_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_none = 0; + int gpio_reset = 0, gpios_cam_en = 0; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + err("failed to get PIN_RESET"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + err("failed to get main cam en gpio"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + + /* BACK CAMERA - POWER ON */ + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 2000, PIN_REGULATOR_ON); + + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, "af", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 7, gpio_none, 0, NULL, 0, PIN_END); + + /* BACK CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif /* CONFIG_COMPANION_USE */ +#endif /* CONFIG_OF */ + +int sensor_imx134_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + info("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_IMX134_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + info("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_IMX134; + module->subdev = subdev_module; + module->device = SENSOR_IMX134_INSTANCE; + module->ops = NULL; + module->client = client; + module->active_width = 3264; + module->active_height = 2448; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 300; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SONY"; + module->sensor_name = "IMX134"; + module->setfile_name = "setfile_imx134.bin"; + module->cfgs = ARRAY_SIZE(config_imx134); + module->cfg = config_imx134; + module->ops = NULL; + module->private_data = NULL; +#ifdef CONFIG_OF + module->power_setpin = sensor_imx134_power_setpin; +#endif + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_IMX134; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x34; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345;//ACTUATOR_NAME_NOTHING; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->actuator_con.peri_setting.i2c.slave_address = 0x34; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#ifdef CONFIG_LEDS_MAX77804 + ext->flash_con.product_name = FLADRV_NAME_MAX77693; +#endif +#ifdef CONFIG_LEDS_LM3560 + ext->flash_con.product_name = FLADRV_NAME_LM3560; +#endif +#ifdef CONFIG_LEDS_SKY81296 + ext->flash_con.product_name = FLADRV_NAME_SKY81296; +#endif +#ifdef CONFIG_LEDS_KTD2692 + ext->flash_con.product_name = FLADRV_NAME_KTD2692; +#endif + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 1; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 2; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} + diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.h new file mode 100644 index 000000000000..e189c182d232 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx134.h @@ -0,0 +1,22 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_IMX134_H +#define FIMC_IS_DEVICE_IMX134_H + +#define SENSOR_IMX134_INSTANCE 0 +#define SENSOR_IMX134_NAME SENSOR_NAME_IMX134 + +int sensor_imx134_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif + diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.c new file mode 100644 index 000000000000..53dccf5f8a7f --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.c @@ -0,0 +1,167 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-imx135.h" + +#define SENSOR_NAME "IMX135" + +static struct fimc_is_sensor_cfg config_imx135[] = { + /* 4144x3106@30fps */ + FIMC_IS_SENSOR_CFG(4144, 3106, 30, 23, 0), + /* 4144x2332@30fps */ + FIMC_IS_SENSOR_CFG(4144, 2332, 30, 18, 1), + /* 1936x1450@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1450, 24, 9, 2), + /* 1936x1090@24fps */ + FIMC_IS_SENSOR_CFG(1936, 1090, 24, 7, 3), + /* 1024x576@120fps */ + FIMC_IS_SENSOR_CFG(1024, 576, 120, 9, 4), + /* 2072x1166@60fps */ + FIMC_IS_SENSOR_CFG(2072, 1166, 60, 18, 5), +}; + +static int sensor_imx135_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_imx135_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_imx135_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_IMX135_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_IMX135; + module->subdev = subdev_module; + module->device = SENSOR_IMX135_INSTANCE; + module->ops = NULL; + module->client = client; + module->active_width = 4128; + module->active_height = 3096; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 120; + module->sensor_maker = "SONY"; + module->sensor_name = "IMX135"; + module->setfile_name = "setfile_imx135.bin"; + module->cfgs = ARRAY_SIZE(config_imx135); + module->cfg = config_imx135; + module->private_data = NULL; + module->lanes = CSI_DATA_LANES_4; + + ext = &module->ext; + memset(ext, 0x0, sizeof(struct sensor_open_extended)); + ext->mipi_lane_num = 4; + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + //ext->sensor_con.peri_setting.i2c.channel = sensor_info->i2c_channel; + //ext->sensor_con.peri_setting.i2c.slave_address = sensor_info->sensor_slave_address; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345; + ext->actuator_con.peri_type = SE_I2C; + //ext->actuator_con.peri_setting.i2c.channel = sensor_info->actuator_i2c; + + //ext->flash_con.product_name = sensor_info->flash_id; + ext->flash_con.peri_type = SE_GPIO; + //ext->flash_con.peri_setting.gpio.first_gpio_port_no = sensor_info->flash_first_gpio; + //ext->flash_con.peri_setting.gpio.second_gpio_port_no = sensor_info->flash_second_gpio; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + ext->mclk = 0; + ext->mipi_lane_num = 0; + ext->mipi_speed = 0; + ext->fast_open_sensor = 0; + ext->self_calibration_mode = 0; + ext->I2CSclk = I2C_L0; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.h new file mode 100644 index 000000000000..6051f4d47c2e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx135.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_IMX135_H +#define FIMC_IS_DEVICE_IMX135_H + +#define SENSOR_IMX135_INSTANCE 0 +#define SENSOR_IMX135_NAME SENSOR_NAME_IMX135 + +int sensor_imx135_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.c new file mode 100644 index 000000000000..3e64e62d2b7f --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.c @@ -0,0 +1,173 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-imx175.h" + +#define SENSOR_NAME "IMX175" + +static struct fimc_is_sensor_cfg config_imx175[] = { + /* 3280X2458@30fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 30, 14, 0), + /* 3280X1846@30fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 30, 11, 1), + /* 3280X2458@24fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 24, 11, 2), + /* 3280X1846@24fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 24, 8, 3), + /* 1640X924@60fps */ + FIMC_IS_SENSOR_CFG(1640, 924, 60, 11, 4), + /* 816X460@120fps */ + FIMC_IS_SENSOR_CFG(816, 460, 120, 11, 5), +}; + +static int sensor_imx175_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_imx175_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +int sensor_imx175_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_IMX175_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_IMX175; + module->subdev = subdev_module; + module->device = SENSOR_IMX175_INSTANCE; + module->ops = NULL; + module->client = client; + module->active_width = 3264; + module->active_height = 2448; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 120; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SONY"; + module->sensor_name = "IMX175"; + module->setfile_name = "setfile_imx175.bin"; + module->cfgs = ARRAY_SIZE(config_imx175); + module->cfg = config_imx175; + module->ops = NULL; + module->private_data = NULL; + + ext = &module->ext; + ext->mipi_lane_num = 4; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = 0; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x18; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7343; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel + = SENSOR_CONTROL_I2C0; + ext->actuator_con.peri_setting.i2c.slave_address = 0x18; + + ext->flash_con.product_name = FLADRV_NAME_MAX77693; + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 0; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 1; + + /* ext->from_con.product_name = FROMDRV_NAME_W25Q80BW; */ + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + ext->mclk = 0; + ext->mipi_lane_num = 0; + ext->mipi_speed = 0; + ext->fast_open_sensor = 0; + ext->self_calibration_mode = 0; + ext->I2CSclk = I2C_L0; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.h new file mode 100644 index 000000000000..2e04dcfe82a7 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx175.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_IMX175_H +#define FIMC_IS_DEVICE_IMX175_H + +#define SENSOR_IMX175_INSTANCE 0 +#define SENSOR_IMX175_NAME SENSOR_NAME_IMX175 + +int sensor_imx175_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.c new file mode 100644 index 000000000000..e63024435bfd --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.c @@ -0,0 +1,829 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-imx219.h" + +#define SENSOR_NAME "IMX219" +#define DEFAULT_SENSOR_WIDTH 184 +#define DEFAULT_SENSOR_HEIGHT 104 +#define SENSOR_MEMSIZE DEFAULT_SENSOR_WIDTH * DEFAULT_SENSOR_HEIGHT + +#define SENSOR_REG_VIS_DURATION_MSB (0x6026) +#define SENSOR_REG_VIS_DURATION_LSB (0x6027) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_MSB (0x4340) +#define SENSOR_REG_VIS_FRAME_LENGTH_LINE_ALV_LSB (0x4341) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_MSB (0x4342) +#define SENSOR_REG_VIS_LINE_LENGTH_PCLK_ALV_LSB (0x4343) +#define SENSOR_REG_VIS_GAIN_RED (0x6029) +#define SENSOR_REG_VIS_GAIN_GREEN (0x602A) +#define SENSOR_REG_VIS_AE_TARGET (0x600A) +#define SENSOR_REG_VIS_AE_SPEED (0x5034) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_MSB (0x5030) +#define SENSOR_REG_VIS_AE_NUMBER_OF_PIXEL_LSB (0x5031) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x1_2 (0x6000) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_1x3_4 (0x6001) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x1_2 (0x6002) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_2x3_4 (0x6003) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x1_2 (0x6004) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_3x3_4 (0x6005) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x1_2 (0x6006) +#define SENSOR_REG_VIS_AE_WINDOW_WEIGHT_4x3_4 (0x6007) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_MSB (0x5039) +#define SENSOR_REG_VIS_AE_MANUAL_EXP_LSB (0x503A) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_MSB (0x503B) +#define SENSOR_REG_VIS_AE_MANUAL_ANG_LSB (0x503C) +#define SENSOR_REG_VIS_BIT_CONVERTING_MSB (0x602B) +#define SENSOR_REG_VIS_BIT_CONVERTING_LSB (0x7203) +#define SENSOR_REG_VIS_AE_OFF (0x5000) + +static u16 setfile_vision_imx219[][2] = { + {0x4200, 0x02}, + {0x4201, 0xAE}, + {0x4301, 0x04}, + {0x4309, 0x04}, + {0x4345, 0x08}, + {0x4347, 0x08}, + {0x4348, 0x0A}, + {0x4349, 0x01}, + {0x434A, 0x05}, + {0x434B, 0xA0}, + {0x434C, 0x01}, + {0x434D, 0x40}, + {0x434E, 0x00}, + {0x434F, 0xB4}, + {0x4381, 0x01}, + {0x4383, 0x07}, + {0x4385, 0x08}, + {0x4387, 0x08}, + {0x5004, 0x01}, + {0x5005, 0x1E}, + {0x5014, 0x11}, + {0x5015, 0x9E}, + {0x5016, 0x00}, + {0x5017, 0x02}, + {0x5030, 0x1C}, + {0x5031, 0x20}, + {0x5034, 0x00}, + {0x5035, 0x02}, + {0x5036, 0x00}, + {0x5037, 0x06}, + {0x5038, 0xC0}, + {0x5039, 0x00}, + {0x503A, 0x00}, + {0x503B, 0x00}, + {0x503C, 0x00}, + {0x503D, 0x20}, + {0x503E, 0x70}, + {0x503F, 0x02}, + {0x600A, 0x2A}, + {0x600E, 0x05}, + {0x6014, 0x27}, + {0x6015, 0x1D}, + {0x6018, 0x01}, + {0x6026, 0x00}, + {0x6027, 0x68}, + {0x6029, 0x08}, + {0x602A, 0x08}, + {0x602B, 0x00}, + {0x602C, 0x00}, + {0x7008, 0x00}, + {0x7009, 0x10}, + {0x700A, 0x00}, + {0x700B, 0x10}, + {0x7014, 0x2B}, + {0x7015, 0x91}, + {0x7016, 0x82}, + {0x701B, 0x16}, + {0x701D, 0x0B}, + {0x701F, 0x0B}, + {0x7026, 0x1A}, + {0x7027, 0x46}, + {0x7029, 0x14}, + {0x702A, 0x02}, + {0x7038, 0x01}, + {0x7039, 0x14}, + {0x703A, 0x32}, + {0x703B, 0x22}, + {0x7040, 0x01}, + {0x7041, 0x14}, + {0x7042, 0x32}, + {0x7043, 0x22}, + {0x7050, 0x0A}, + {0x7051, 0xA8}, + {0x7052, 0x35}, + {0x7053, 0x54}, + {0x7054, 0x00}, + {0x7055, 0x00}, + {0x7056, 0x00}, + {0x7057, 0x00}, + {0x705E, 0x0E}, + {0x705F, 0x10}, + {0x7060, 0x01}, + {0x7064, 0x05}, + {0x7065, 0x3C}, + {0x7066, 0x00}, + {0x7067, 0x00}, + {0x7068, 0x4A}, + {0x706C, 0x01}, + {0x7077, 0x88}, + {0x7078, 0x88}, + {0x7082, 0x90}, + {0x7091, 0x05}, + {0x7098, 0x00}, + {0x7112, 0x01}, + {0x720A, 0x06}, + {0x720B, 0x80}, + {0x7245, 0xC1}, + {0x7301, 0x01}, + {0x7305, 0x13}, + {0x7306, 0x01}, + {0x7323, 0x01}, + {0x7339, 0x07}, + {0x7351, 0x01}, + {0x7352, 0x24}, + {0x7405, 0x28}, + {0x7406, 0x28}, + {0x7407, 0xC0}, + {0x7454, 0x01}, + {0x7460, 0x01}, + {0x7461, 0x20}, + {0x7462, 0xC0}, + {0x7463, 0x1E}, + {0x7464, 0x02}, + {0x7465, 0x4B}, + {0x7467, 0x20}, + {0x7468, 0x20}, + {0x7469, 0x20}, + {0x746A, 0x20}, + {0x746B, 0x20}, + {0x746C, 0x20}, + {0x746D, 0x09}, + {0x746E, 0xFF}, + {0x746F, 0x01}, + {0x7472, 0x00}, + {0x7473, 0x02}, + {0x7474, 0xC1}, + {0x7475, 0x00}, + {0x7476, 0x00}, + {0x7477, 0x00}, + {0x7478, 0x00}, + {0x4100, 0x01}, +}; + +static struct fimc_is_sensor_cfg config_imx219[] = { + /* 3280X2458@20fps */ + FIMC_IS_SENSOR_CFG(3280, 2458, 20, 19, 0), + /* 3280X1846@20fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 20, 15, 1), + /* 1640X1228@24fps */ + FIMC_IS_SENSOR_CFG(1640, 1228, 24, 12, 2), + /* 3280X1846@24fps */ + FIMC_IS_SENSOR_CFG(3280, 1846, 24, 17, 3), +}; + +static int sensor_imx219_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_imx219_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + pr_info("%s\n", __func__); + return 0; +} +static int sensor_imx219_registered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); + return 0; +} + +static void sensor_imx219_unregistered(struct v4l2_subdev *sd) +{ + pr_info("%s\n", __func__); +} + +static const struct v4l2_subdev_internal_ops internal_ops = { + .open = sensor_imx219_open, + .close = sensor_imx219_close, + .registered = sensor_imx219_registered, + .unregistered = sensor_imx219_unregistered, +}; + +static int sensor_imx219_init(struct v4l2_subdev *subdev, u32 val) +{ + int i, ret = 0; + struct fimc_is_module_enum *module; + struct fimc_is_module_imx219 *module_imx219; + struct i2c_client *client; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + module_imx219 = module->private_data; + client = module->client; + + module_imx219->system_clock = 146 * 1000 * 1000; + module_imx219->line_length_pck = 146 * 1000 * 1000; + + pr_info("%s\n", __func__); + + /* sensor init */ + for (i = 0; i < ARRAY_SIZE(setfile_vision_imx219); i++) { + fimc_is_sensor_write8(client, setfile_vision_imx219[i][0], + (u8)setfile_vision_imx219[i][1]); + } + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_imx219_init +}; + +static int sensor_imx219_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + if (enable) { + ret = CALL_MOPS(sensor, stream_on, subdev); + if (ret < 0) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } else { + ret = CALL_MOPS(sensor, stream_off, subdev); + if (ret < 0) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + } + +p_err: + return 0; +} + +static int sensor_imx219_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct v4l2_captureparm *cp; + struct v4l2_fract *tpf; + u64 duration; + + BUG_ON(!subdev); + BUG_ON(!param); + + pr_info("%s\n", __func__); + + cp = ¶m->parm.capture; + tpf = &cp->timeperframe; + + if (!tpf->denominator) { + err("denominator is 0"); + ret = -EINVAL; + goto p_err; + } + + if (!tpf->numerator) { + err("numerator is 0"); + ret = -EINVAL; + goto p_err; + } + + duration = (u64)(tpf->numerator * 1000 * 1000 * 1000) / + (u64)(tpf->denominator); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (!sensor) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = CALL_MOPS(sensor, s_duration, subdev, duration); + if (ret) { + err("s_duration is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +static int sensor_imx219_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt) +{ + /* TODO */ + return 0; +} + +static const struct v4l2_subdev_video_ops video_ops = { + .s_stream = sensor_imx219_s_stream, + .s_parm = sensor_imx219_s_param, + .s_mbus_fmt = sensor_imx219_s_format +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops, + .video = &video_ops +}; + +int sensor_imx219_stream_on(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 1); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +int sensor_imx219_stream_off(struct v4l2_subdev *subdev) +{ + int ret = 0; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + ret = fimc_is_sensor_write8(client, 0x4100, 0); + if (ret < 0) { + err("fimc_is_sensor_write8 is fail(%d)", ret); + goto p_err; + } + +p_err: + return ret; +} + +/* + * @ brief + * frame duration time + * @ unit + * nano second + * @ remarks + */ +int sensor_imx219_s_duration(struct v4l2_subdev *subdev, u64 duration) +{ + int ret = 0; + u8 value[2]; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s\n", __func__); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + /* + * forcely set 10fps for IMX219, + */ + value[0] = 0x52; + value[1] = 0x0; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_MSB, value[1]); + fimc_is_sensor_write8(client, SENSOR_REG_VIS_DURATION_LSB, value[0]); + +p_err: + return ret; +} + +int sensor_imx219_g_min_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_g_max_duration(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_s_exposure(struct v4l2_subdev *subdev, u64 exposure) +{ + int ret = 0; + u8 value; + struct fimc_is_module_enum *sensor; + struct i2c_client *client; + + BUG_ON(!subdev); + + pr_info("%s(%d)\n", __func__, (u32)exposure); + + sensor = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + if (unlikely(!sensor)) { + err("sensor is NULL"); + ret = -EINVAL; + goto p_err; + } + + client = sensor->client; + if (unlikely(!client)) { + err("client is NULL"); + ret = -EINVAL; + goto p_err; + } + + value = exposure & 0xFF; + + fimc_is_sensor_write8(client, SENSOR_REG_VIS_AE_TARGET, value); + +p_err: + return ret; +} + +int sensor_imx219_g_min_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_g_max_exposure(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_s_again(struct v4l2_subdev *subdev, u64 sensitivity) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + return ret; +} + +int sensor_imx219_g_min_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_g_max_again(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_s_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_g_min_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +int sensor_imx219_g_max_dgain(struct v4l2_subdev *subdev) +{ + int ret = 0; + return ret; +} + +struct fimc_is_sensor_ops module_imx219_ops = { + .stream_on = sensor_imx219_stream_on, + .stream_off = sensor_imx219_stream_off, + .s_duration = sensor_imx219_s_duration, + .g_min_duration = sensor_imx219_g_min_duration, + .g_max_duration = sensor_imx219_g_max_duration, + .s_exposure = sensor_imx219_s_exposure, + .g_min_exposure = sensor_imx219_g_min_exposure, + .g_max_exposure = sensor_imx219_g_max_exposure, + .s_again = sensor_imx219_s_again, + .g_min_again = sensor_imx219_g_min_again, + .g_max_again = sensor_imx219_g_max_again, + .s_dgain = sensor_imx219_s_dgain, + .g_min_dgain = sensor_imx219_g_min_dgain, + .g_max_dgain = sensor_imx219_g_max_dgain +}; + +#ifdef CONFIG_OF +static int sensor_imx219_power_setpin(struct device *dev) +{ + int gpio_none = 0, gpio_reset = 0, gpio_standby = 0; + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + struct pinctrl *pinctrl_ch = NULL; + + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + gpio_standby = of_get_named_gpio(dnode, "gpio_standby", 0); + if (!gpio_is_valid(gpio_standby)) { + dev_err(dev, "failed to get gpio_standby\n"); + } else { + gpio_request_one(gpio_standby, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_standby); + } + + /* initial - i2c off */ + pinctrl_ch = devm_pinctrl_get_select(dev, "off1"); + if (IS_ERR_OR_NULL(pinctrl_ch)) { + pr_err("%s: cam %s pins are not configured\n", __func__, "off1"); + } else { + devm_pinctrl_put(pinctrl_ch); + } + + /* FRONT CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* FRONT CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 1, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 2, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 3, gpio_none, 0, "VT_CAM_1.2V", 1000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 4, gpio_standby, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 5, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, 6, gpio_none, 0, NULL, 0, PIN_END); + + /* VISION CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 0, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 2, gpio_standby, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 3, gpio_none, 0, "VT_CAM_1.2V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VT_CAM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 5, gpio_none, 0, "VT_CAM_1.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, 6, gpio_none, 0, NULL, 0, PIN_END); + + return 0; +} +#endif + +int sensor_imx219_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + static bool probe_retried = false; + + if (!fimc_is_dev) + goto probe_defer; + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_IMX219_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + /* IMX219 */ + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_IMX219_NAME; + module->subdev = subdev_module; + module->device = SENSOR_IMX219_INSTANCE; + module->ops = &module_imx219_ops; + module->client = client; + module->active_width = 3264; + module->active_height = 2448; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 24; + module->position = SENSOR_POSITION_FRONT; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_2; + module->sensor_maker = "SONY"; + module->sensor_name = "IMX219"; + module->setfile_name = "setfile_imx219.bin"; + module->cfgs = ARRAY_SIZE(config_imx219); + module->cfg = config_imx219; + module->private_data = kzalloc(sizeof(struct fimc_is_module_imx219), GFP_KERNEL); + if (!module->private_data) { + err("private_data is NULL"); + ret = -ENOMEM; + kfree(subdev_module); + goto p_err; + } +#ifdef CONFIG_OF + module->power_setpin = sensor_imx219_power_setpin; +#endif + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + ext->sensor_con.product_name = SENSOR_NAME_IMX219; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->sensor_con.peri_setting.i2c.slave_address = 0x34; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + + ext->companion_con.product_name = COMPANION_NAME_NOTHING; + + if (client) { + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + subdev_module->internal_ops = &internal_ops; + } else { + v4l2_subdev_init(subdev_module, &subdev_ops); + } + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; + +probe_defer: + if (probe_retried) { + err("probe has already been retried!!"); + BUG(); + } + + probe_retried = true; + err("core device is not yet probed"); + return -EPROBE_DEFER; +} + +static int sensor_imx219_remove(struct i2c_client *client) +{ + int ret = 0; + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id exynos_fimc_is_sensor_imx219_match[] = { + { + .compatible = "samsung,exynos5-fimc-is-sensor-imx219", + }, + {}, +}; +#endif + +static const struct i2c_device_id sensor_imx219_idt[] = { + { SENSOR_NAME, 0 }, +}; + +static struct i2c_driver sensor_imx219_driver = { + .driver = { + .name = SENSOR_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = exynos_fimc_is_sensor_imx219_match +#endif + }, + .probe = sensor_imx219_probe, + .remove = sensor_imx219_remove, + .id_table = sensor_imx219_idt +}; + +static int __init sensor_imx219_load(void) +{ + return i2c_add_driver(&sensor_imx219_driver); +} + +static void __exit sensor_imx219_unload(void) +{ + i2c_del_driver(&sensor_imx219_driver); +} + +module_init(sensor_imx219_load); +module_exit(sensor_imx219_unload); + +MODULE_AUTHOR("Gilyeon lim"); +MODULE_DESCRIPTION("Sensor IMX219 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.h new file mode 100644 index 000000000000..6772939915a2 --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx219.h @@ -0,0 +1,28 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_IMX219_H +#define FIMC_IS_DEVICE_IMX219_H + +#define SENSOR_IMX219_INSTANCE 1 +#define SENSOR_IMX219_NAME SENSOR_NAME_IMX219 + +struct fimc_is_module_imx219 { + u16 vis_duration; + u16 frame_length_line; + u32 line_length_pck; + u32 system_clock; +}; + +int sensor_imx219_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.c b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.c new file mode 100644 index 000000000000..872885898bef --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.c @@ -0,0 +1,356 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fimc-is-core.h" +#include "../fimc-is-device-sensor.h" +#include "../fimc-is-resourcemgr.h" +#include "../fimc-is-hw.h" +#include "fimc-is-device-imx240.h" + +#define SENSOR_NAME "IMX240" + +static struct fimc_is_sensor_cfg config_imx240[] = { + /* 5328x3000@30fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 30, 30, 0), + /* 5328x3000@24fps */ + FIMC_IS_SENSOR_CFG(5328, 3000, 24, 29, 1), + /* 4000X3000@30fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 30, 23, 2), + /* 4000X3000@24fps */ + FIMC_IS_SENSOR_CFG(4000, 3000, 24, 19, 3), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 30, 19, 4), + /* 3008X3000@30fps */ + FIMC_IS_SENSOR_CFG(3008, 3000, 24, 14, 5), + /* 2664X1500@60fps */ + FIMC_IS_SENSOR_CFG(2664, 1500, 60, 15, 6), + /* 2664X1500@30fps */ + FIMC_IS_SENSOR_CFG(2664, 1500, 30, 15, 7), + /* 1328X748@120fps */ + FIMC_IS_SENSOR_CFG(1328, 748, 120, 13, 8), + /* 824X496@300fps */ + //FIMC_IS_SENSOR_CFG(824, 496, 300, 13, 8), +}; + +static int sensor_imx240_init(struct v4l2_subdev *subdev, u32 val) +{ + int ret = 0; + struct fimc_is_module_enum *module; + + BUG_ON(!subdev); + + module = (struct fimc_is_module_enum *)v4l2_get_subdevdata(subdev); + + pr_info("[MOD:D:%d] %s(%d)\n", module->id, __func__, val); + + return ret; +} + +static const struct v4l2_subdev_core_ops core_ops = { + .init = sensor_imx240_init +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &core_ops +}; + +#ifdef CONFIG_OF +#ifdef CONFIG_COMPANION_USE +static int sensor_imx240_power_setpin(struct device *dev) +{ + struct exynos_platform_fimc_is_sensor *pdata; + struct device_node *dnode; + int gpio_comp_en = 0, gpio_comp_rst = 0; + int gpio_none = 0; + int gpio_reset = 0; + int gpios_cam_en = -EINVAL; +#ifdef CONFIG_OIS_USE + int gpios_ois_en = 0; +#endif + BUG_ON(!dev); + BUG_ON(!dev->platform_data); + + dnode = dev->of_node; + pdata = dev->platform_data; + + gpio_comp_en = of_get_named_gpio(dnode, "gpios_comp_en", 0); + if (!gpio_is_valid(gpio_comp_en)) { + dev_err(dev, "failed to get main comp en gpio\n"); + } else { + gpio_request_one(gpio_comp_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_en); + } + + gpio_comp_rst = of_get_named_gpio(dnode, "gpios_comp_reset", 0); + if (!gpio_is_valid(gpio_comp_rst)) { + dev_err(dev, "failed to get main comp reset gpio\n"); + } else { + gpio_request_one(gpio_comp_rst, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_comp_rst); + } + + gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0); + if (!gpio_is_valid(gpio_reset)) { + dev_err(dev, "failed to get PIN_RESET\n"); + return -EINVAL; + } else { + gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpio_reset); + } + + if (of_get_property(dnode, "gpios_cam_en", NULL)) { + gpios_cam_en = of_get_named_gpio(dnode, "gpios_cam_en", 0); + if (!gpio_is_valid(gpios_cam_en)) { + dev_err(dev, "failed to get main cam en gpio\n"); + } else { + gpio_request_one(gpios_cam_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_cam_en); + } + } + +#ifdef CONFIG_OIS_USE + gpios_ois_en = of_get_named_gpio(dnode, "gpios_ois_en", 0); + pdata->pin_ois_en = gpios_ois_en; + if (!gpio_is_valid(gpios_ois_en)) { + dev_err(dev, "failed to get ois en gpio\n"); + } else { + gpio_request_one(gpios_ois_en, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW"); + gpio_free(gpios_ois_en); + } +#endif + + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 1, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 2, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); +#ifdef CONFIG_OIS_USE + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 3, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 4, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_ON); +#endif + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 5, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 6, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 7, gpio_comp_en, 0, NULL, 150, PIN_OUTPUT_HIGH); + if (pdata->companion_use_pmic) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 8, gpio_none, 0, "VDD_MIPI_1.0V_COMP", 0, PIN_REGULATOR_ON); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 9, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 10, gpio_none, 0, "ch", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 11, gpio_none, 0, "af", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 12, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, 13, gpio_none, 0, NULL, 0, PIN_END); + + /* BACK CAMERA - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 1, gpio_none, 0, "off", 0, PIN_FUNCTION); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 2, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 3, gpio_comp_rst, 0, NULL, 0, PIN_OUTPUT_LOW); + if (pdata->companion_use_pmic) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "VDD_MIPI_1.0V_COMP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 5, gpio_comp_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 6, gpio_none, 0, "VDDA_1.8V_COMP", 0, PIN_REGULATOR_OFF); + if (gpio_is_valid(gpios_cam_en)) { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpios_cam_en, 0, NULL, 0, PIN_OUTPUT_LOW); + } else { + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 7, gpio_none, 0, "CAM_SEN_A2.8V_AP", 0, PIN_REGULATOR_OFF); + } + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 8, gpio_none, 0, "CAM_SEN_CORE_1.2V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 9, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); +#ifdef CONFIG_OIS_USE + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 10, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 11, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_OFF); +#endif + SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, 12, gpio_none, 0, NULL, 0, PIN_END); + +#ifdef CONFIG_OIS_USE + /* OIS_FACTORY - POWER ON */ + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 1, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 2, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 3, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_ON); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 4, gpio_reset, 0, NULL, 0, PIN_OUTPUT_HIGH); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_ON, 5, gpio_none, 0, NULL, 0, PIN_END); + + /* OIS_FACTORY - POWER OFF */ + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 0, gpio_none, 0, "CAM_AF_2.8V_AP", 2000, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 1, gpio_reset, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 2, gpio_none, 0, "CAM_IO_1.8V_AP", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 3, gpios_ois_en, 0, NULL, 0, PIN_OUTPUT_LOW); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 4, gpio_none, 0, "OIS_VM_2.8V", 0, PIN_REGULATOR_OFF); + SET_PIN(pdata, SENSOR_SCENARIO_OIS_FACTORY, GPIO_SCENARIO_OFF, 5, gpio_none, 0, NULL, 0, PIN_END); +#endif + + return 0; +} +#else +static int sensor_imx240_power_setpin(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_COMPANION_USE */ +#endif /* CONFIG_OF */ + +int sensor_imx240_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct fimc_is_core *core; + struct v4l2_subdev *subdev_module; + struct fimc_is_module_enum *module; + struct fimc_is_device_sensor *device; + struct sensor_open_extended *ext; + + BUG_ON(!fimc_is_dev); + + core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev); + if (!core) { + err("core device is not yet probed"); + return -EPROBE_DEFER; + } + + device = &core->sensor[SENSOR_IMX240_INSTANCE]; + + subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (!subdev_module) { + err("subdev_module is NULL"); + ret = -ENOMEM; + goto p_err; + } + + module = &device->module_enum[atomic_read(&core->resourcemgr.rsccount_module)]; + atomic_inc(&core->resourcemgr.rsccount_module); + module->id = SENSOR_NAME_IMX240; + module->subdev = subdev_module; + module->device = SENSOR_IMX240_INSTANCE; + module->client = client; + module->active_width = 5312; + module->active_height = 2990; + module->pixel_width = module->active_width + 16; + module->pixel_height = module->active_height + 10; + module->max_framerate = 300; + module->position = SENSOR_POSITION_REAR; + module->mode = CSI_MODE_CH0_ONLY; + module->lanes = CSI_DATA_LANES_4; + module->sensor_maker = "SONY"; + module->sensor_name = "IMX240"; + module->setfile_name = "setfile_imx240.bin"; + module->cfgs = ARRAY_SIZE(config_imx240); + module->cfg = config_imx240; + module->ops = NULL; + module->private_data = NULL; +#ifdef CONFIG_OF + module->power_setpin = sensor_imx240_power_setpin; +#endif + + ext = &module->ext; + ext->mipi_lane_num = module->lanes; + ext->I2CSclk = I2C_L0; + + ext->sensor_con.product_name = SENSOR_NAME_IMX240; + ext->sensor_con.peri_type = SE_I2C; + ext->sensor_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0; + ext->sensor_con.peri_setting.i2c.slave_address = 0x34; + ext->sensor_con.peri_setting.i2c.speed = 400000; + + ext->actuator_con.product_name = ACTUATOR_NAME_AK7345; + ext->actuator_con.peri_type = SE_I2C; + ext->actuator_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->actuator_con.peri_setting.i2c.slave_address = 0x34; + ext->actuator_con.peri_setting.i2c.speed = 400000; + +#ifdef CONFIG_LEDS_MAX77804 + ext->flash_con.product_name = FLADRV_NAME_MAX77693; +#endif +#ifdef CONFIG_LEDS_LM3560 + ext->flash_con.product_name = FLADRV_NAME_LM3560; +#endif +#ifdef CONFIG_LEDS_SKY81296 + ext->flash_con.product_name = FLADRV_NAME_SKY81296; +#endif +#ifdef CONFIG_LEDS_KTD2692 + ext->flash_con.product_name = FLADRV_NAME_KTD2692; +#endif + ext->flash_con.peri_type = SE_GPIO; + ext->flash_con.peri_setting.gpio.first_gpio_port_no = 1; + ext->flash_con.peri_setting.gpio.second_gpio_port_no = 2; + + ext->from_con.product_name = FROMDRV_NAME_NOTHING; + +#ifdef CONFIG_COMPANION_USE + ext->companion_con.product_name = COMPANION_NAME_73C1; + ext->companion_con.peri_info0.valid = true; + ext->companion_con.peri_info0.peri_type = SE_SPI; + ext->companion_con.peri_info0.peri_setting.spi.channel = (int) core->companion_spi_channel; + ext->companion_con.peri_info1.valid = true; + ext->companion_con.peri_info1.peri_type = SE_I2C; + ext->companion_con.peri_info1.peri_setting.i2c.channel = 0; + ext->companion_con.peri_info1.peri_setting.i2c.slave_address = 0x7A; + ext->companion_con.peri_info1.peri_setting.i2c.speed = 400000; + ext->companion_con.peri_info2.valid = true; + ext->companion_con.peri_info2.peri_type = SE_FIMC_LITE; + ext->companion_con.peri_info2.peri_setting.fimc_lite.channel = FLITE_ID_D; +#else + ext->companion_con.product_name = COMPANION_NAME_NOTHING; +#endif + +#if defined(CONFIG_OIS_USE) + ext->ois_con.product_name = OIS_NAME_IDG2030; + ext->ois_con.peri_type = SE_I2C; + ext->ois_con.peri_setting.i2c.channel = SENSOR_CONTROL_I2C1; + ext->ois_con.peri_setting.i2c.slave_address = 0x48; + ext->ois_con.peri_setting.i2c.speed = 400000; +#else + ext->ois_con.product_name = OIS_NAME_NOTHING; + ext->ois_con.peri_type = SE_NULL; +#endif + + if (client) + v4l2_i2c_subdev_init(subdev_module, client, &subdev_ops); + else + v4l2_subdev_init(subdev_module, &subdev_ops); + + v4l2_set_subdevdata(subdev_module, module); + v4l2_set_subdev_hostdata(subdev_module, device); + snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->id); + +p_err: + info("%s(%d)\n", __func__, ret); + return ret; +} diff --git a/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.h b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.h new file mode 100644 index 000000000000..8b26f65b7c5e --- /dev/null +++ b/drivers/media/platform/exynos/fimc-is/sensor/fimc-is-device-imx240.h @@ -0,0 +1,21 @@ +/* + * Samsung Exynos5 SoC series Sensor driver + * + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_IS_DEVICE_IMX240_H +#define FIMC_IS_DEVICE_IMX240_H + +#define SENSOR_IMX240_INSTANCE 0 +#define SENSOR_IMX240_NAME SENSOR_NAME_IMX240 + +int sensor_imx240_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +#endif