diff options
author | Beata Michalska <b.michalska@samsung.com> | 2014-07-14 19:32:39 +0200 |
---|---|---|
committer | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2014-11-27 03:41:31 -0800 |
commit | 6e00228239f29b965b72b0de228a2e576d45df6a (patch) | |
tree | bb80796f7676f55da1b83c28e30730c76db2f7da /drivers | |
parent | 39f5c1f8e85d0d2348494745905f2b550107b04f (diff) | |
download | linux-3.10-6e00228239f29b965b72b0de228a2e576d45df6a.tar.gz linux-3.10-6e00228239f29b965b72b0de228a2e576d45df6a.tar.bz2 linux-3.10-6e00228239f29b965b72b0de228a2e576d45df6a.zip |
exynos3: fimc-is: Add support for FIMC IS on Exynos3250
The changes introduced provide support for FIMC-IS
subsystem avalilable on Exynos3250.
They cover the differences in both: the hardware itself
as well as the subtle discrepancies between the firmwares
controlling the FIMC IS on both SoCs.
Regarding the above mentioned firmware variations:
the initial driver has been extended with a simplified
interface handling those variations thus living the core
driver independent of the actual firmware version.
Missing code paths have been added to cover handling some of
the most probable failures/errors.
Signed-off-by: Beata Michalska <b.michalska@samsung.com>
Change-Id: Ie07312013a6a0ee14b88803360f0832d0ad4038d
Diffstat (limited to 'drivers')
21 files changed, 3462 insertions, 922 deletions
diff --git a/drivers/media/platform/exynos5-is/Makefile b/drivers/media/platform/exynos5-is/Makefile index d928569ac75..0d02747c767 100644 --- a/drivers/media/platform/exynos5-is/Makefile +++ b/drivers/media/platform/exynos5-is/Makefile @@ -4,7 +4,7 @@ exynos-iss-objs := exynos5-mdev.o ifeq ($(CONFIG_VIDEO_SAMSUNG_EXYNOS5_FIMC_IS),y) exynos-iss-objs += fimc-is-core.o fimc-is-isp.o fimc-is-scaler.o -exynos-iss-objs += fimc-is-pipeline.o fimc-is-interface.o fimc-is-sensor.o +exynos-iss-objs += fimc-is-pipeline.o fimc-is-interface.o fimc-is-sensor.o fimc-is-backend.o fimc-is-fw.o endif obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS5_CAMERA) += exynos-iss.o diff --git a/drivers/media/platform/exynos5-is/exynos5-mdev.c b/drivers/media/platform/exynos5-is/exynos5-mdev.c index d9b9f4a4e41..9f933f7b516 100644 --- a/drivers/media/platform/exynos5-is/exynos5-mdev.c +++ b/drivers/media/platform/exynos5-is/exynos5-mdev.c @@ -613,33 +613,35 @@ static int register_fimc_is_entity(struct fimc_md *fmd, return ret; /* FIMC SCP */ - subdev = fimc_is_scp_get_sd(is, i); - subdev->grp_id = GRP_ID_FIMC_IS; - v4l2_set_subdev_hostdata(subdev, ep); - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, subdev); - if (ret) - v4l2_err(&fmd->v4l2_dev, - "Failed to register SCP subdev\n"); - p->subdevs[IDX_SCP] = subdev; - /* Create default links: SCP -> vdev */ - vdev = fimc_is_scp_get_vfd(is, i); - ret = media_entity_create_link(&subdev->entity, - SCALER_SD_PAD_SRC_DMA, - &vdev->entity, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - /* Link SCC -> SCP */ - ret = media_entity_create_link(&p->subdevs[IDX_SCC]->entity, + if (is->pipeline[i].subip_state[IS_SCP] != COMP_INVALID) { + subdev = fimc_is_scp_get_sd(is, i); + subdev->grp_id = GRP_ID_FIMC_IS; + v4l2_set_subdev_hostdata(subdev, ep); + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, subdev); + if (ret) + v4l2_err(&fmd->v4l2_dev, + "Failed to register SCP subdev\n"); + p->subdevs[IDX_SCP] = subdev; + /* Create default links: SCP -> vdev */ + vdev = fimc_is_scp_get_vfd(is, i); + ret = media_entity_create_link(&subdev->entity, + SCALER_SD_PAD_SRC_DMA, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + /* Link SCC -> SCP */ + ret = media_entity_create_link(&p->subdevs[IDX_SCC]->entity, SCALER_SD_PAD_SRC_FIFO, &subdev->entity, SCALER_SD_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; + if (ret) + return ret; + } } fmd->is = is; @@ -817,8 +819,10 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) subdev = fimc_is_scc_get_sd(is, i); v4l2_device_unregister_subdev(subdev); - subdev = fimc_is_scp_get_sd(is, i); - v4l2_device_unregister_subdev(subdev); + if (is->pipeline[i].subip_state[IS_SCP] != COMP_INVALID){ + subdev = fimc_is_scp_get_sd(is, i); + v4l2_device_unregister_subdev(subdev); + } } v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); @@ -1159,6 +1163,15 @@ unlock: return ret; } +void fimc_md_capture_event_handler(struct v4l2_subdev *subdev, unsigned int event_id, + void *arg) +{ + struct fimc_md *fmd; + + fmd = entity_to_fimc_mdev(&subdev->entity); + fimc_is_dispatch_events(fmd->is, event_id, arg); +} + int exynos_camera_register(struct device *dev, struct fimc_md **md) { struct v4l2_device *v4l2_dev; @@ -1182,6 +1195,7 @@ int exynos_camera_register(struct device *dev, struct fimc_md **md) v4l2_dev = &fmd->v4l2_dev; v4l2_dev->mdev = &fmd->media_dev; + v4l2_dev->notify = fimc_md_capture_event_handler; strlcpy(v4l2_dev->name, "exynos5-fimc-md", sizeof(v4l2_dev->name)); ret = v4l2_device_register(dev, v4l2_dev); diff --git a/drivers/media/platform/exynos5-is/fimc-is-backend.c b/drivers/media/platform/exynos5-is/fimc-is-backend.c new file mode 100644 index 00000000000..cdcf0c29db0 --- /dev/null +++ b/drivers/media/platform/exynos5-is/fimc-is-backend.c @@ -0,0 +1,301 @@ +/* + * Samsung EXYNOS5/EXYNOS3250 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013-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 "fimc-is-backend.h" +#include "fimc-is.h" +#include "fimc-is-cmd.h" +#include "fimc-is-fw.h" + +/** + * @brief Default set of MCUCTL_HIC_REG and MCUCTL_IHC_REG + * + * This is required as those two sets of registers are necessary + * to properly initialize the communcication with FIMC IS + * Those to are basic (mandatory) registers and their offsets + * should ramin the same between differen firmware revisions + */ +static const struct mcuctl_sreg_desc mcuctl_sregs_default[] = { + [MCUCTL_HIC_REG ] = + MCUCTL_SHARED_REG(0x00, 6, 4,MCUCTL_SHARED_REG_RW), + [MCUCTL_IHC_REG ] = + MCUCTL_SHARED_REG(0x024, 7, 4, MCUCTL_SHARED_REG_RW), +}; + +/** + * @breif Set of supported MCUCTL Shared registers + * for FW FIMC_IS_FW_V120-driven FIMC IS + */ +static const struct mcuctl_sreg_desc fw_120_mcuctl_sregs[] = { + [MCUCTL_HIC_REG ] = + MCUCTL_SHARED_REG(0x000, 6, 4, MCUCTL_SHARED_REG_RW), + [MCUCTL_IHC_REG ] = + MCUCTL_SHARED_REG(0x024, 7, 4, MCUCTL_SHARED_REG_RW), + [MCUCTL_ISP_REG ] = + MCUCTL_SHARED_REG(0x04c, 4, 2, MCUCTL_SHARED_REG_RW), + [MCUCTL_SCC_REG ] = + MCUCTL_SHARED_REG(0x06c, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_DNR_REG ] = + MCUCTL_SHARED_REG(0x08c, 4, 2, MCUCTL_SHARED_REG_RW), + [MCUCTL_SCP_REG ] = + MCUCTL_SHARED_REG(0x0ac, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_YUV_REG ] = + MCUCTL_SHARED_REG(0x0c4, 4, 2, MCUCTL_SHARED_REG_RW), + [MCUCTL_SHOT_REG ] = + MCUCTL_SHARED_REG(0x0d8, 4, 2, MCUCTL_SHARED_REG_RW), + [MCUCTL_META_REG ] = + MCUCTL_SHARED_REG(0x0ec, 3, 1, MCUCTL_SHARED_REG_RW), + [MCUCTL_FRAME_COUNT_REG ] = + MCUCTL_SHARED_REG(0x0fc, 1, 1, MCUCTL_SHARED_REG_RW), +}; + +/** + * @brief Set of supported MCUCTL Shared registers + * for FW FIMC_IS_FW_V130-driven FIMC IS + */ +static const struct mcuctl_sreg_desc fw_130_mcuctl_sregs[] = { + [MCUCTL_HIC_REG ] = + MCUCTL_SHARED_REG(0x00, 6, 4, MCUCTL_SHARED_REG_RW), + [MCUCTL_IHC_REG ] = + MCUCTL_SHARED_REG(0x024, 7, 4, MCUCTL_SHARED_REG_RW), + [MCUCTL_3AA0C_REG ] = + MCUCTL_SHARED_REG(0x04c, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_3AA1C_REG ] = + MCUCTL_SHARED_REG(0x064, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_SCC_REG ] = + MCUCTL_SHARED_REG(0x080, 6, 4, MCUCTL_SHARED_REG_RW), + [MCUCTL_DIS_REG ] = + MCUCTL_SHARED_REG(0x08c, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_SCP_REG ] = + MCUCTL_SHARED_REG(0x0ac, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_SHOT_REG ] = + MCUCTL_SHARED_REG(0x0d8, 5, 3, MCUCTL_SHARED_REG_RW), + [MCUCTL_FRAME_COUNT_REG ] = + MCUCTL_SHARED_REG(0x0f0, 4, 4, MCUCTL_SHARED_REG_RW), +}; + + +#define MCUCTL_SHARED_REG_VALIDATE(regs_desc, reg_range_id, reg_type) \ + (((reg_range_id) < MCUCTL_END_REG) \ + && (regs_desc[reg_range_id].type®_type)) + + +/** + * @brief Retrieve the descriptor for given set of + * MCUCTL shared registers. + * + * @param[in] mcuctl_desc_id - MCUCTL shared registers map ID + * @param[in] mcuctl_sreg_id - the ID of shared regs set + * @parma[in/out] mcuctl_sreg_desc - requested registers descriptor + * + * @return - 0 upon success, EINVAL otherwise + */ + +int mcuctl_sreg_get_desc(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + struct mcuctl_sreg_desc *mcuctl_shared_desc) +{ + const struct mcuctl_sreg_desc *regs_map = fw_data->mcuctl_sreg_map; + + if (!mcuctl_shared_desc || !regs_map) + return -EINVAL; + + if ((MCUCTL_SHARED_REG_VALIDATE(regs_map, mcuctl_sreg_id, + MCUCTL_SHARED_REG_RW))){ + memcpy(mcuctl_shared_desc, ®s_map[mcuctl_sreg_id], + sizeof(struct mcuctl_sreg_desc)); + return 0; + } + return -EINVAL; +} + +static int mcuctl_sreg_get_info(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned int info_offset, + unsigned long *info) +{ + const struct mcuctl_sreg_desc *regs_desc = fw_data->mcuctl_sreg_map; + + if (!info || !regs_desc) + return -EINVAL; + + if (MCUCTL_SHARED_REG_VALIDATE(regs_desc, + mcuctl_sreg_id, MCUCTL_SHARED_REG_RW)){ + *info = *( ((char*)®s_desc[mcuctl_sreg_id] )+ info_offset); + return 0; + } + return -EINVAL; +} + +/** + * @breif Retrive the base offset of given set of registers + * relative to the begining of MCUCTL shared area + * + * @param[in] mcuctl_desc_id - MCUCTL shared registers map ID + * @param[in] mcuctl_sreg_id - the ID of shared regs set + * @param[in/out] offset - base offset of requested set of registers + * + * @return - 0 upon success, EINVAL otherwise + */ +int mcuctl_sreg_get_offset(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned long * offset) +{ + return mcuctl_sreg_get_info(fw_data,mcuctl_sreg_id, + offsetof(struct mcuctl_sreg_desc, base_offset), offset); +} + +/** + * @brief Get the range of requested set of MCUCTL shared registers + * + * @parma[in] mcuctl_desc_id - MCUCTL shared registers map ID + * @param[in] mcuctl_sreg_id - the ID of shared regs set + * @param[in/out] range - range of requested set of registers + * + * @return - 0 upon success, EINVAL otherwise + */ + +int mcuctl_sreg_get_range(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned long *range) +{ + + return mcuctl_sreg_get_info(fw_data,mcuctl_sreg_id, + offsetof(struct mcuctl_sreg_desc, range), + range); + +} + +/** + * @brief Get the range of data registers + * for given set of MCUCTL shared registers + * + * @param[in] mcuctl_desc_id - MCUCTL shared registers map ID + * @param[in] mcuctl_sreg_id - the ID of shared regs set + * @param[in/out] data_range - range of data registers from within + * requested set + * + * @return - 0 upon success, EINVAL otherwise + */ + +int mcuctl_sreg_get_data_range(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned int *data_range) +{ + return mcuctl_sreg_get_info(fw_data,mcuctl_sreg_id, + offsetof(struct mcuctl_sreg_desc, data_range), + (unsigned long*)data_range); +} + +/** + * @breif Validate requested shared registers ser + * + * @param[in] mcuctl_desc_id - MCUCTL shared registers map ID + * @param[in] mcuctl_sreg_id - the ID of shared regs set + * + * @return - 1 case given registers set is valid (readable/writable) + * 0 otherwise + */ + +int mcuctl_sreg_is_valid(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id) +{ + const struct mcuctl_sreg_desc *regs_desc = fw_data->mcuctl_sreg_map; + if (regs_desc) { + return MCUCTL_SHARED_REG_VALIDATE(regs_desc, + mcuctl_sreg_id, MCUCTL_SHARED_REG_RW); + } + return 0; +} + +static const unsigned int fw_intrsrc_map_default[] = { + INTR_GENERAL, +}; + +static const unsigned int fw_120_intrsrc_map[] = { + INTR_GENERAL, + INTR_ISP_DONE, + INTR_SCC_DONE, + INTR_DNR_DONE, + INTR_SCP_DONE, + INTR_ISP_YUV_DONE, + INTR_META_DONE, + INTR_SHOT_DONE +}; + +static const unsigned int fw_130_intrsrc_map[] = { + INTR_GENERAL, + INTR_ISP_DONE, + INTR_3A0C_DONE, + INTR_3A1C_DONE, + INTR_SCC_DONE, + INTR_DIS_DONE, + INTR_SCP_DONE, + INTR_SHOT_DONE, +}; + +static struct fimc_is_fw_data fw_internal[] = { + { + .version = FIMC_IS_FW_VER_UNKNOWN, + .mcuctl_sreg_map = mcuctl_sregs_default, + .instruction_set = { + .hic_bitmap = 0x3ffffffffULL, + .ihc_bitmap = 0xffULL, + }, + .interrupt_map = fw_intrsrc_map_default, + .interrupt_map_size = ARRAY_SIZE(fw_intrsrc_map_default), + + + }, + { + .version = FIMC_IS_FW_V120, + .instruction_set = { + .hic_bitmap = 0x7ff7fffULL, + .ihc_bitmap = 0x7fULL, + }, + .mcuctl_sreg_map = fw_120_mcuctl_sregs, + .interrupt_map = fw_120_intrsrc_map, + .interrupt_map_size = ARRAY_SIZE(fw_120_intrsrc_map), + },{ + .version = FIMC_IS_FW_V130, + .instruction_set = { + .hic_bitmap = 0x3ffffffffULL, + .ihc_bitmap = 0xffULL, + }, + .mcuctl_sreg_map = fw_130_mcuctl_sregs, + .interrupt_map = fw_130_intrsrc_map, + .interrupt_map_size = ARRAY_SIZE(fw_130_intrsrc_map), + }, + {} +}; + + +void fimc_is_fw_set_default(struct fimc_is_fw_data **fw_data) +{ + BUG_ON(!fw_data); + *fw_data = &fw_internal[0]; +} + +int fimc_is_fw_config(struct fimc_is_fw_data **fw_data, unsigned int fw_version) +{ + int i; + /* + * Drop the last digit from the firmware version : + * e.g.: ver. 130-139 should fall back to 130 + */ + fw_version -= fw_version%10; + + for (i = 0; i < ARRAY_SIZE(fw_internal); ++i) { + if (fw_version == fw_internal[i].version) { + *fw_data = &fw_internal[i]; + return 0; + } + } + return -EINVAL; +} diff --git a/drivers/media/platform/exynos5-is/fimc-is-backend.h b/drivers/media/platform/exynos5-is/fimc-is-backend.h new file mode 100644 index 00000000000..40a4d329996 --- /dev/null +++ b/drivers/media/platform/exynos5-is/fimc-is-backend.h @@ -0,0 +1,212 @@ +/* + * Samsung EXYNOS5/EXYNOS3250 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013-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_BACKEND_H_ +#define FIMC_IS_BACKEND_H_ + +#include "fimc-is-fw.h" +#include <linux/types.h> + + +/* + * MCUCTL Shared register types + */ +#define MCUCTL_SHARED_REG_INVAL 0 +#define MCUCTL_SHARED_REG_R (1 << 0) +#define MCUCTL_SHARED_REG_W (1 << 1) +#define MCUCTL_SHARED_REG_RW (MCUCTL_SHARED_REG_R | MCUCTL_SHARED_REG_W) + +/* + * Set of supported MCUCTL shared registers. + * Might be extended if needed. + */ +enum{ + /* | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_HIC_REG, + /* | NUMBER | */ + MCUCTL_PW_DONW_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_IHC_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_3AA0C_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_3AA1C_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_ISP_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_SCC_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_DNR_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_DIS_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_SCP_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_YUV_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_SHOT_REG, + /* | IFLAGS | COMMAND ID | SENSOR/GROUP ID | PARAMS | */ + MCUCTL_META_REG, + /* | SENSOR3 | SENSOR2 | SENSOR1 | SENSOR0 | */ + MCUCTL_FRAME_COUNT_REG, + MCUCTL_END_REG +}; + +#define MCUCTL_SREG_SIZE 4 + +/** + * struct mcuctl_sreg_desc - MCUCTL Shared register descriptor + * @base_offset: Base offset of logical MCUCTL shared registers range + * relative to the begining of MCUCTL shared area + * @range: Number of 32-bits registers within the range + * @data_range: Number of 32-bits registers dedicated for communication + * parameters + * @type: type of the registers set: MCUCTL_SHARED_REG_INVAL denotes given set + * is not supported. MCUCTL_SHARED_REG_R, MCUCTL_SHARED_REG_W indicate + * readable / writable registers. + */ +struct mcuctl_sreg_desc { + unsigned long base_offset; + unsigned int range; + unsigned int data_range; + unsigned int type; +}; + +/* + * MCUCTL Shared regs layout + * Single set of logically related registers: + * OFFSET: | 0x00 | : INTERRUPT FLAGS *OPTIONAL* + * OFFSET: | 0x04 | : COMMAND ID + * OFFSET: | 0x08 | : SENSOR ID / GROUP ID + * OFFSET: | 0x0c | : COMMAND PARAMETERS (max.4) + */ + +#define MCUCTL_SHARED_REG(offset, size, data_size, reg_type)\ +{ \ + .base_offset = offset, \ + .range = size, \ + .data_range = data_size, \ + .type = reg_type, \ +} + +/** + * @brief Generic set of FIMC IS interrupts + */ +enum fimc_is_interrupt { + INTR_GENERAL, + INTR_ISP_DONE, + INTR_3A0C_DONE, + INTR_3A1C_DONE, + INTR_SCC_DONE, + INTR_DNR_DONE, + INTR_DIS_DONE, + INTR_SCP_DONE, + INTR_ISP_YUV_DONE, + INTR_META_DONE, + INTR_SHOT_DONE, + INTR_MAX_MAP + +}; + +/** + * @brief Unspecified firmware version + */ +#define FIMC_IS_FW_VER_UNKNOWN 0 +/** + * @brief Generic error code + */ +#define FIMC_IS_FW_INVALID (-EINVAL) + +/** + * @brief Map firmware specific interrupt source id + * to generic one + */ +#define FIMC_IS_FW_GET_INTR_SRC(fw_data, __intr_src) \ + ((__intr_src) < (fw_data)->interrupt_map_size \ + ? (fw_data)->interrupt_map[(__intr_src)]\ + : FIMC_IS_FW_INVALID) + +/** + * @brief Verify if given command is supported by current firmware + */ +#define FIMC_IS_FW_CMD_SUPPORT(fw_data, __cmd) \ +({ \ + u64 __cmd_bitmap = 0; \ + unsigned int __bit = 0; \ + if ((__cmd) <= HIC_COMMAND_END) { \ + __cmd_bitmap = (fw_data)->instruction_set.hic_bitmap; \ + __bit = (__cmd) -1; \ + } \ + else if ((__cmd) <= IHC_COMMAND_END) { \ + __cmd_bitmap = (fw_data)->instruction_set.ihc_bitmap; \ + __bit = (__cmd) - IHC_COMMAND_BEGIN; \ + } \ + __cmd_bitmap & (1ULL << __bit) ? 1 : 0; \ +}) + +/** + * struct fimc_is_fw_commands - internal info on firmware support + * for FIMC IS <-> Host communication commands + * @hic_bitmap: bitmap of supported HIC commands + * (each command (ID) is mapped to a corresponding bit) + * @ihc_bitmap: bitmap of supported IHC commands + * (each command (ID) is mapped to a corresponding bit, + * starting with IHC_COMMAND_BEGIN mapped to first bit) + * + */ +struct fimc_is_fw_commands{ + u64 hic_bitmap; + u64 ihc_bitmap; +}; + + +/** + * struct fimc_is_fw_dta - set of internal data used to control + * proper information flow between the + * driver itself and currently used FIMC IS + * firmware + * @fw_version: reference firmware version + * @mcuctl_sreg_map: map of MCUCTL shared registers + * @instruction_set: bitmap of supported instructions + * @interrupt_map: FIMC IS fw specific interrupts src map + */ +struct fimc_is_fw_data { + unsigned int version; + const struct fimc_is_fw_commands instruction_set; + const struct mcuctl_sreg_desc const *mcuctl_sreg_map; + const unsigned int const *interrupt_map; + unsigned int interrupt_map_size; +}; + +void fimc_is_fw_set_default(struct fimc_is_fw_data **fw_data); +int fimc_is_fw_config(struct fimc_is_fw_data **fw_data, unsigned int fw_version); + + +int mcuctl_sreg_get_desc(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + struct mcuctl_sreg_desc *mcuctl_sreg_desc); + +int mcuctl_sreg_get_offset(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned long * offset); + +int mcuctl_sreg_get_range(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned long *range); + +int mcuctl_sreg_get_data_range(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id, + unsigned int *data_range); + +int mcuctl_sreg_is_valid(struct fimc_is_fw_data *fw_data, + unsigned int mcuctl_sreg_id); + + +#endif /*FIMC_IS_BACKEND_H_*/ diff --git a/drivers/media/platform/exynos5-is/fimc-is-cmd.h b/drivers/media/platform/exynos5-is/fimc-is-cmd.h index 62502803935..4c58acff00d 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-cmd.h +++ b/drivers/media/platform/exynos5-is/fimc-is-cmd.h @@ -16,7 +16,8 @@ enum is_cmd { /* HOST -> IS */ - HIC_PREVIEW_STILL = 0x1, + HIC_COMMAND_BEGIN = 0x1, + HIC_PREVIEW_STILL = HIC_COMMAND_BEGIN, HIC_PREVIEW_VIDEO, HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO, @@ -31,10 +32,13 @@ enum is_cmd { HIC_SET_PARAMETER, HIC_GET_PARAMETER, HIC_SET_A5_MEM_ACCESS, + HIC_SET_A5_MAP = HIC_SET_A5_MEM_ACCESS, RESERVED2, + /* HIC_SET_A5_UNMAP supported by FW v.130 nad higher */ + HIC_SET_A5_UNMAP = RESERVED2, HIC_GET_STATUS, /* SENSOR PART*/ - HIC_OPEN_SENSOR, + HIC_OPEN_SENSOR , HIC_CLOSE_SENSOR, HIC_SIMMIAN_INIT, HIC_SIMMIAN_WRITE, @@ -45,7 +49,17 @@ enum is_cmd { HIC_MSG_CONFIG, HIC_MSG_TEST, /* IS -> HOST */ - IHC_GET_SENSOR_NUMBER = 0x1000, + HIC_ISP_I2C_CONTROL, + HIC_CALIBRATE_ACTUATOR, + HIC_GET_IP_STATUS, /* 30 */ + HIC_I2C_CONTROL_LOCK, + HIC_SYSTEM_CONTROL, + HIC_SENSOR_MODE_CHANGE, + HIC_MSG_SENSOR_END, + HIC_COMMAND_END = HIC_MSG_SENSOR_END, + /* IS -> HOST */ + IHC_COMMAND_BEGIN = 0x1000, + IHC_GET_SENSOR_NUMBER = IHC_COMMAND_BEGIN, /* Parameter1 : Address of space to copy a setfile */ /* Parameter2 : Space szie */ IHC_SET_SHOT_MARK, @@ -60,7 +74,10 @@ enum is_cmd { /* PARAM2 : frame count */ IHC_AA_DONE, IHC_NOT_READY, - IHC_FLASH_READY + IHC_FLASH_READY, + /* F/W version >= 1.32 */ + IHC_REPORT_ERR, + IHC_COMMAND_END }; enum is_reply { @@ -80,9 +97,28 @@ enum is_subscenario_id { ISS_SUB_SCENARIO_STILL, ISS_SUB_SCENARIO_VIDEO, ISS_SUB_SCENARIO_SCENE1, + /* 2: dual still preview */ + ISS_SUB_SCENARIO_DUAL_STILL = ISS_SUB_SCENARIO_SCENE1, ISS_SUB_SCENARIO_SCENE2, + /* 3: dual video */ + ISS_SUB_SCENARIO_DUAL_VIDEO = ISS_SUB_SCENARIO_SCENE2, ISS_SUB_SCENARIO_SCENE3, - ISS_SUB_END + /* 4: video high speed */ + ISS_SUB_SCENARIO_VIDEO_HIGH_SPEED = ISS_SUB_SCENARIO_SCENE3, + /* 5: still capture */ + ISS_SUB_SCENARIO_STILL_CAPTURE = 5, + + ISS_SUB_END, +}; + +enum is_system_control_id { + IS_SYS_CLOCK_GATE = 0, + IS_SYS_END, +}; + +enum is_system_control_cmd { + SYS_CONTROL_DISABLE = 0, + SYS_CONTROL_ENABLE = 1, }; struct is_setfile_header_element { @@ -96,92 +132,4 @@ struct is_setfile_header { struct is_setfile_header_element fd[ISS_END]; }; -struct is_common_reg { - u32 hicmd; - u32 hic_sensorid; - u32 hic_param[4]; - - u32 reserved1[3]; - - u32 ihcmd_iflag; - u32 ihcmd; - u32 ihc_sensorid; - u32 ihc_param[4]; - - u32 reserved2[3]; - - u32 isp_bayer_iflag; - u32 isp_bayer_sensor_id; - u32 isp_bayer_param[2]; - - u32 reserved3[4]; - - u32 scc_iflag; - u32 scc_sensor_id; - u32 scc_param[3]; - - u32 reserved4[3]; - - u32 dnr_iflag; - u32 dnr_sensor_id; - u32 dnr_param[2]; - - u32 reserved5[4]; - - u32 scp_iflag; - u32 scp_sensor_id; - u32 scp_param[3]; - - u32 reserved6[1]; - - u32 isp_yuv_iflag; - u32 isp_yuv_sensor_id; - u32 isp_yuv_param[2]; - - u32 reserved7[1]; - - u32 shot_iflag; - u32 shot_sensor_id; - u32 shot_param[2]; - - u32 reserved8[1]; - - u32 meta_iflag; - u32 meta_sensor_id; - u32 meta_param1; - - u32 reserved9[1]; - - u32 fcount; -}; - -struct is_mcuctl_reg { - u32 mcuctl; - u32 bboar; - - u32 intgr0; - u32 intcr0; - u32 intmr0; - u32 intsr0; - u32 intmsr0; - - u32 intgr1; - u32 intcr1; - u32 intmr1; - u32 intsr1; - u32 intmsr1; - - u32 intcr2; - u32 intmr2; - u32 intsr2; - u32 intmsr2; - - u32 gpoctrl; - u32 cpoenctlr; - u32 gpictlr; - - u32 pad[0xD]; - - struct is_common_reg common_reg; -}; #endif diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.c b/drivers/media/platform/exynos5-is/fimc-is-core.c index 5fc077f616f..592479d9ec0 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-core.c +++ b/drivers/media/platform/exynos5-is/fimc-is-core.c @@ -1,7 +1,7 @@ /* * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd. * Arun Kumar K <arun.kk@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -26,11 +26,17 @@ #include "fimc-is-i2c.h" #include "exynos5-mdev.h" -#define CLK_MCU_ISP_DIV0_FREQ (200 * 1000000) -#define CLK_MCU_ISP_DIV1_FREQ (100 * 1000000) -#define CLK_ISP_DIV0_FREQ (134 * 1000000) -#define CLK_ISP_DIV1_FREQ (68 * 1000000) -#define CLK_ISP_DIVMPWM_FREQ (34 * 1000000) +#define CLK_MCU_ISP_DIV0_FREQ (200 * 1000000) +#define CLK_MCU_ISP_DIV1_FREQ (100 * 1000000) +#define CLK_ISP_DIV0_FREQ (150 * 1000000) +#define CLK_ISP_DIV1_FREQ (50 * 1000000) +#define CLK_ISP_DIVMPWM_FREQ (50 /*75*/ * 1000000) +#define CLK_MCU_ISP_ACLK_400_FREQ (400 * 1000000) +#define CLK_DIV_ACLK_266_FREQ (/*300*/ 100 * 1000000) +#define EXYNOS3250_CLK_ISP_DIV_FREQ (50 * 1000000) + +#define FIMC_IS_EXYNOS5_CLK_MASK 0x7f +#define FIMC_IS_EXYNOS3250_CLK_MASK 0xff static const char * const fimc_is_clock_name[] = { [IS_CLK_ISP] = "isp", @@ -40,74 +46,233 @@ static const char * const fimc_is_clock_name[] = { [IS_CLK_ISP_DIVMPWM] = "isp_divmpwm", [IS_CLK_MCU_ISP_DIV0] = "mcu_isp_div0", [IS_CLK_MCU_ISP_DIV1] = "mcu_isp_div1", + /* Exynos 3250 */ + [IS_CLK_CAM1] = "cam1", + /* ACLK 400 MCUISP */ + [IS_CLK_MOUT_ACLK400_MCUISP_SUB] = "mout_aclk400_mcuisp_sub", + [IS_CLK_DIV_ACLK400_MCUISP] = "div_aclk400_mcuisp", + [IS_CLK_MOUT_ACLK400_MCUISP] = "mout_aclk400_mcuisp", + /* ACLK 266 */ + [IS_CLK_MOUT_ACLK266_0] = "mout_aclk266_0", + [IS_CLK_MOUT_ACLK266] = "mout_aclk266", + [IS_CLK_DIV_ACLK266] = "div_aclk266", + [IS_CLK_MOUT_ACLK266_SUB] = "mout_aclk266_sub", + [IS_CLK_DIV_MPLL_PRE] = "div_mpll_pre", + [IS_CLK_FIN_PLL] = "fin_pll", }; -static void fimc_is_put_clocks(struct fimc_is *is) -{ - int i; +#define FIMC_IS_CLK_ERR(dev, i , action) \ + dev_err(dev, "Failed to "#action" for %s clock\n", \ + fimc_is_clock_name[i]); - for (i = 0; i < IS_CLK_MAX_NUM; i++) { - if (IS_ERR(is->clock[i])) - continue; - clk_unprepare(is->clock[i]); - clk_put(is->clock[i]); - is->clock[i] = ERR_PTR(-EINVAL); +static int fimc_is_exyno3250_clk_cfg(struct fimc_is *is) +{ + int ret; + + /* DIV 400 MCUISP -> MUX ACLK 400 MCUISP SUB */ + ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK400_MCUISP_SUB], + is->clocks[IS_CLK_DIV_ACLK400_MCUISP]); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK400_MCUISP_SUB, + set parentness); + return ret; } -} -static int fimc_is_get_clocks(struct fimc_is *is) -{ - struct device *dev = &is->pdev->dev; - int i, ret; - - for (i = 0; i < IS_CLK_MAX_NUM; i++) { - is->clock[i] = clk_get(dev, fimc_is_clock_name[i]); - if (IS_ERR(is->clock[i])) - goto err; - ret = clk_prepare(is->clock[i]); - if (ret < 0) { - clk_put(is->clock[i]); - is->clock[i] = ERR_PTR(-EINVAL); - goto err; - } + ret = clk_set_rate(is->clocks[IS_CLK_DIV_ACLK400_MCUISP], + CLK_MCU_ISP_ACLK_400_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_DIV_ACLK400_MCUISP, + set rate); + return ret; + } + + ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV0], + CLK_MCU_ISP_DIV0_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MCU_ISP_DIV0, + set rate); + return ret; } - return 0; -err: - fimc_is_put_clocks(is); - dev_err(dev, "Failed to get clock: %s\n", fimc_is_clock_name[i]); - return -ENXIO; -} -static int fimc_is_configure_clocks(struct fimc_is *is) -{ - int i, ret; + ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV1], + CLK_MCU_ISP_DIV1_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MCU_ISP_DIV1, + set rate); + return ret; + } + + /* ACLK 266 */ + clk_set_parent( is->clocks[IS_CLK_MOUT_ACLK266_0], + is->clocks[IS_CLK_DIV_MPLL_PRE]); + + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266_0, + ser rate); + return ret; + } + + ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266], + is->clocks[IS_CLK_MOUT_ACLK266_0]); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266, + set parentness); + return ret; + } + + ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266_SUB], + is->clocks[IS_CLK_DIV_ACLK266]); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266_SUB, + set parentness); + return ret; + } + + ret = clk_set_rate(is->clocks[IS_CLK_DIV_ACLK266], + CLK_DIV_ACLK_266_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_DIV_ACLK266, + set rate); + return ret; + } + + ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV0], + EXYNOS3250_CLK_ISP_DIV_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIV0, + set rate); + return ret; + } + + ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV1], + EXYNOS3250_CLK_ISP_DIV_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIV1, + set rate); + return ret; + } + + ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIVMPWM], CLK_ISP_DIVMPWM_FREQ); + if (ret) { + FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIVMPWM, + set rate); + return ret; + } + + return ret; - for (i = 0; i < IS_CLK_MAX_NUM; i++) - is->clock[i] = ERR_PTR(-EINVAL); +} - ret = fimc_is_get_clocks(is); - if (ret) - return ret; +static int fimc_is_exynos5_clk_cfg(struct fimc_is *is) +{ + int ret; /* Set rates */ - ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV0], + ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV0], CLK_MCU_ISP_DIV0_FREQ); if (ret) return ret; - ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV1], + ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV1], CLK_MCU_ISP_DIV1_FREQ); if (ret) return ret; - ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV0], CLK_ISP_DIV0_FREQ); + ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV0], CLK_ISP_DIV0_FREQ); if (ret) return ret; - ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV1], CLK_ISP_DIV1_FREQ); + ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV1], CLK_ISP_DIV1_FREQ); if (ret) return ret; - return clk_set_rate(is->clock[IS_CLK_ISP_DIVMPWM], + return clk_set_rate(is->clocks[IS_CLK_ISP_DIVMPWM], CLK_ISP_DIVMPWM_FREQ); } +static void fimc_is_put_clocks(struct fimc_is *is) +{ + int i; + + for (i = IS_CLK_GATE_MAX; i < IS_CLKS_MAX; ++i) { + if (!IS_ERR(is->clocks[i])) + clk_unprepare(is->clocks[i]); + + } + for (i = 0; i < IS_CLKS_MAX; ++i) { + if(!IS_ERR(is->clocks[i])) { + clk_put(is->clocks[i]); + is->clocks[i] = ERR_PTR(-EINVAL); + } + } +} + +static int fimc_is_get_clocks(struct fimc_is *is) +{ + int i, ret; + + for (i = 0; i < IS_CLKS_MAX; ++i) + is->clocks[i] = ERR_PTR(-EINVAL); + + for (i = 0; i < IS_CLK_GATE_MAX; ++i) { + if (!(is->drvdata->clk_mask & (0x1 << i))) + continue; + is->clocks[i] = clk_get(&is->pdev->dev, + fimc_is_clock_name[i]); + if (IS_ERR(is->clocks[i])) { + dev_err(&is->pdev->dev, + "Failed to acquire clock : %s\n", + fimc_is_clock_name[i]); + ret = PTR_ERR(is->clocks[i]); + goto rollback; + } + } + + if (is->drvdata->variant == FIMC_IS_EXYNOS3250) { + for (i = IS_CLK_GATE_MAX; i < IS_CLKS_MAX ; ++i) { + is->clocks[i] = clk_get(&is->pdev->dev, + fimc_is_clock_name[i]); + + if (IS_ERR(is->clocks[i])) { + dev_err(&is->pdev->dev, + "Failed to acquire clock: %s\n", + fimc_is_clock_name[i]); + ret = PTR_ERR(is->clocks[i]); + goto rollback; + } + + ret = clk_prepare(is->clocks[i]); + + if (ret) { + dev_err(&is->pdev->dev, + "Failed to prepare clock %s\n", + fimc_is_clock_name[i]); + goto rollback; + } + } + } + + return 0; + +rollback: + fimc_is_put_clocks(is); + return ret; +} + +static int fimc_is_configure_clocks(struct fimc_is* is) +{ + int ret = 0; + + switch(is->drvdata->variant){ + case FIMC_IS_EXYNOS5: + ret = fimc_is_exynos5_clk_cfg(is); + break; + case FIMC_IS_EXYNOS3250: + ret = fimc_is_exyno3250_clk_cfg(is); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + static void fimc_is_pipelines_destroy(struct fimc_is *is) { int i; @@ -129,6 +294,13 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, node->full_name); return -EINVAL; } + ret = of_property_read_u32(node, "reg", &tmp); + if (ret < 0) { + dev_err(&is->pdev->dev, "reg property not found at: %s\n", + node->full_name); + return ret; + } + sensor->i2c_slave_addr = tmp; node = v4l2_of_get_next_endpoint(node, NULL); if (!node) @@ -142,7 +314,7 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, ret = of_property_read_u32(node, "reg", &tmp); if (ret < 0) { dev_err(&is->pdev->dev, "reg property not found at: %s\n", - node->full_name); + node->full_name); return ret; } @@ -169,17 +341,25 @@ static int fimc_is_parse_sensor(struct fimc_is *is) return 0; } -static struct fimc_is_drvdata exynos5250_drvdata = { - .num_instances = 1, - .fw_name = "exynos5_fimc_is_fw.bin", -}; - static struct fimc_is_drvdata exynos3250_drvdata = { .num_instances = 1, .fw_name = "exynos3_fimc_is_fw.bin", + .fw_version = FIMC_IS_FW_V130, + .clk_mask = FIMC_IS_EXYNOS3250_CLK_MASK, + .subip_mask = FIMC_IS_EXYNOS3250_SUBBLOCKS, + .variant = FIMC_IS_EXYNOS3250, .master_node = 1, }; +static struct fimc_is_drvdata exynos5250_drvdata = { + .num_instances = 1, + .fw_name = "exynos5_fimc_is_fw.bin", + .fw_version = FIMC_IS_FW_V120, + .clk_mask = FIMC_IS_EXYNOS5_CLK_MASK, + .subip_mask = FIMC_IS_EXYNOS5_SUBBLOCKS, + .variant = FIMC_IS_EXYNOS5, +}; + static const struct of_device_id fimc_is_of_match[] = { { .compatible = "samsung,exynos5250-fimc-is", @@ -192,6 +372,92 @@ static const struct of_device_id fimc_is_of_match[] = { }; MODULE_DEVICE_TABLE(of, fimc_is_of_match); +int fimc_is_clk_enable(struct fimc_is *is) +{ + int i, ret = 0; + + for (i = 0; i < IS_CLK_GATE_MAX; ++i){ + if (IS_ERR(is->clocks[i])) + continue; + + ret = clk_prepare_enable(is->clocks[i]); + if (ret) { + dev_err(&is->pdev->dev, + "Failed to prepare and enable %s clock\n", + fimc_is_clock_name[i]); + for (; i >= 0; --i) + clk_disable_unprepare(is->clocks[i]); + + } + } + return ret; +} + +void fimc_is_clk_disable(struct fimc_is *is) +{ + int i; + for (i = 0; i < IS_CLK_GATE_MAX; ++i) + if (!IS_ERR(is->clocks[i])) + clk_disable_unprepare(is->clocks[i]); + if (is->drvdata->variant == FIMC_IS_EXYNOS3250) { + if (clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK400_MCUISP_SUB], + is->clocks[IS_CLK_FIN_PLL])) + FIMC_IS_CLK_ERR(&is->pdev->dev, + IS_CLK_MOUT_ACLK400_MCUISP_SUB, + change parentness); + if (clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266_SUB], + is->clocks[IS_CLK_FIN_PLL])) + FIMC_IS_CLK_ERR(&is->pdev->dev, + IS_CLK_MOUT_ACLK266_SUB, + change parentness); + } +} + +static int fimc_is_pm_resume(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + int ret; + + fimc_is_configure_clocks(is); + ret = fimc_is_clk_enable(is); + if (ret < 0) { + dev_err(dev, "Could not enable clocks\n"); + return ret; + } + return 0; +} + +static int fimc_is_pm_suspend(struct device *dev) +{ + struct fimc_is *is = dev_get_drvdata(dev); + fimc_is_clk_disable(is); + return 0; +} + +static int fimc_is_runtime_resume(struct device *dev) +{ + return fimc_is_pm_resume(dev); +} + +static int fimc_is_runtime_suspend(struct device *dev) +{ + return fimc_is_pm_suspend(dev); +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_is_resume(struct device *dev) +{ + /* TODO */ + return 0; +} + +static int fimc_is_suspend(struct device *dev) +{ + /* TODO */ + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + static int fimc_is_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -239,7 +505,7 @@ static int fimc_is_probe(struct platform_device *pdev) return irq; } - ret = fimc_is_configure_clocks(is); + ret = fimc_is_get_clocks(is); if (ret < 0) { dev_err(dev, "clocks configuration failed\n"); goto err_clk; @@ -247,6 +513,14 @@ static int fimc_is_probe(struct platform_device *pdev) platform_set_drvdata(pdev, is); pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = fimc_is_runtime_resume(dev); + if (ret) { + dev_err(dev, "Runtime resume failed\n"); + goto err_clk; + } + + } is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { @@ -282,6 +556,9 @@ static int fimc_is_probe(struct platform_device *pdev) goto err_cam; } + INIT_LIST_HEAD(&is->event_listeners); + spin_lock_init(&is->events_lock); + dev_dbg(dev, "FIMC-IS registered successfully\n"); return 0; @@ -297,70 +574,6 @@ err_clk: return ret; } -int fimc_is_clk_enable(struct fimc_is *is) -{ - int ret; - - ret = clk_enable(is->clock[IS_CLK_ISP]); - if (ret) - return ret; - ret = clk_enable(is->clock[IS_CLK_MCU_ISP]); - if (ret) - clk_disable(is->clock[IS_CLK_ISP]); - return ret; -} - -void fimc_is_clk_disable(struct fimc_is *is) -{ - clk_disable(is->clock[IS_CLK_ISP]); - clk_disable(is->clock[IS_CLK_MCU_ISP]); -} - -static int fimc_is_pm_resume(struct device *dev) -{ - struct fimc_is *is = dev_get_drvdata(dev); - int ret; - - ret = fimc_is_clk_enable(is); - if (ret < 0) { - dev_err(dev, "Could not enable clocks\n"); - return ret; - } - return 0; -} - -static int fimc_is_pm_suspend(struct device *dev) -{ - struct fimc_is *is = dev_get_drvdata(dev); - - fimc_is_clk_disable(is); - return 0; -} - -static int fimc_is_runtime_resume(struct device *dev) -{ - return fimc_is_pm_resume(dev); -} - -static int fimc_is_runtime_suspend(struct device *dev) -{ - return fimc_is_pm_suspend(dev); -} - -#ifdef CONFIG_PM_SLEEP -static int fimc_is_resume(struct device *dev) -{ - /* TODO */ - return 0; -} - -static int fimc_is_suspend(struct device *dev) -{ - /* TODO */ - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - static int fimc_is_remove(struct platform_device *pdev) { struct fimc_is *is = platform_get_drvdata(pdev); diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.h b/drivers/media/platform/exynos5-is/fimc-is-core.h index aba1e81a2c2..1e8abe3588d 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-core.h +++ b/drivers/media/platform/exynos5-is/fimc-is-core.h @@ -1,7 +1,7 @@ /* - * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver + * Samsung EXYNOS5/EXYNOS3 FIMC-IS (Imaging Subsystem) driver * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd. * Arun Kumar K <arun.kk@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -27,13 +27,16 @@ #define FIMC_IS_DRV_NAME "exynos5-fimc-is" +#define FIMC_IS_EXYNOS3250 0 +#define FIMC_IS_EXYNOS5 1 + #define FIMC_IS_COMMAND_TIMEOUT (10 * HZ) #define FIMC_IS_STARTUP_TIMEOUT (3 * HZ) #define FIMC_IS_SHUTDOWN_TIMEOUT (10 * HZ) +/* EXYNOS5 */ #define FW_SHARED_OFFSET (0x8c0000) #define DEBUG_CNT (500 * 1024) -#define DEBUG_OFFSET (0x840000) #define DEBUGCTL_OFFSET (0x8bd000) #define DEBUG_FCOUNT (0x8c64c0) @@ -45,7 +48,7 @@ #define FIMC_IS_MAX_PLANES 3 #define FIMC_IS_NUM_SCALERS 2 -enum fimc_is_clks { +enum fimc_is_gate_clks { IS_CLK_ISP, IS_CLK_MCU_ISP, IS_CLK_ISP_DIV0, @@ -53,7 +56,21 @@ enum fimc_is_clks { IS_CLK_ISP_DIVMPWM, IS_CLK_MCU_ISP_DIV0, IS_CLK_MCU_ISP_DIV1, - IS_CLK_MAX_NUM + IS_CLK_CAM1, + IS_CLK_GATE_MAX, +}; + +enum fimc_is_exynos3250_parent_clks { + IS_CLK_MOUT_ACLK400_MCUISP_SUB = IS_CLK_GATE_MAX, + IS_CLK_DIV_ACLK400_MCUISP, + IS_CLK_MOUT_ACLK400_MCUISP, + IS_CLK_MOUT_ACLK266_0, + IS_CLK_MOUT_ACLK266, + IS_CLK_DIV_ACLK266, + IS_CLK_MOUT_ACLK266_SUB, + IS_CLK_DIV_MPLL_PRE, + IS_CLK_FIN_PLL, + IS_CLKS_MAX, }; /* Video capture states */ @@ -98,6 +115,10 @@ struct fimc_is_meminfo { struct fimc_is_drvdata { unsigned int num_instances; char *fw_name; + unsigned int fw_version; + unsigned int clk_mask; + unsigned int subip_mask; + unsigned long variant; unsigned int master_node:1; }; @@ -115,6 +136,21 @@ struct fimc_is_fmt { unsigned int num_planes; }; +/** + * struct fimc_is_event_listener - internal representation + * of frame-related events listener + * @private_data: Listener's private data + * @event_handler: Event handler to be triggered upon upcoming + * events + * @link: list link + */ +struct fimc_is_event_listener { + void * private_data; + void (*event_handler)(struct fimc_is_event_listener *, unsigned int, void*); + struct list_head link; +}; + + #ifdef CONFIG_VIDEO_SAMSUNG_EXYNOS5_FIMC_IS int fimc_is_init(void); void fimc_is_cleanup(void); diff --git a/drivers/media/platform/exynos5-is/fimc-is-fw.c b/drivers/media/platform/exynos5-is/fimc-is-fw.c new file mode 100644 index 00000000000..acd5a781a14 --- /dev/null +++ b/drivers/media/platform/exynos5-is/fimc-is-fw.c @@ -0,0 +1,500 @@ +/* + * Samsung EXYNOS FIMC-IS (Imaging Subsystem) 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 "fimc-is-fw.h" +#include "fimc-is.h" +#include "fimc-is-metadata.h" + +#define V120 FIMC_IS_FW_V120 +#define V130 FIMC_IS_FW_V130 + +#define DECLARE_FW_TYPE(v, type) \ + struct param_ ## v ## _ ## type +#define DECLARE_FW_SPECIFIC_TYPE(v,t) \ + DECLARE_FW_TYPE(v, t) +#define FW_TYPE(v,t) DECLARE_FW_SPECIFIC_TYPE(v, t) + +#define SENSOR_WIDTH_PADDING 16 +#define SENSOR_HEIGHT_PADDING 10 + +static const struct fimc_is_fw_ops fimc_is_fw_def_ops; + +const struct fimc_is_fw_ops *fimc_is_fw_ops = &fimc_is_fw_def_ops; + +/** + * @defgroup FIMC_IS_FW_V120_SUPPORT + * FIMC-IS firmware versrion 12x support + * + * @{ + */ + +/** + * @brief FIMC IS sub-block OTF input confgiuration + * parameters specific for given firmware version + */ +DECLARE_FW_SPECIFIC_TYPE(V120,otf_input_ext) { + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 frametime_min; + u32 frametime_max; +}; + +/** + * @brief FIMC IS sub-block DMA input confgiuration + * parameters specific for given firmware version + */ +DECLARE_FW_SPECIFIC_TYPE(V120, dma_input_ext) { + u32 bayer_crop_offset_x; + u32 bayer_crop_offset_y; + u32 bayer_crop_width; + u32 bayer_crop_height; + u32 dma_crop_offset_x; + u32 dma_crop_offset_y; + u32 dma_crop_width; + u32 dma_crop_height; + u32 user_min_frametime; + u32 user_max_frametime; + u32 wide_frame_gap; + u32 frame_gap; + u32 line_gap; + u32 reserved[2]; +}; + +/** + * @brief FIMC IS ISP sub-block confgiuration + * parameters specific for given firmware version + * + * @note FIMC-IS frimware version 120+ combines + * configuration parameters for ISP with + * 3AA sub-block + */ +DECLARE_FW_SPECIFIC_TYPE(V120, isp_ext) { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma1_input; + struct param_dma_input dma2_input; + struct param_isp_aa aa; + struct param_isp_flash flash; + struct param_isp_awb awb; + struct param_isp_imageeffect effect; + struct param_isp_iso iso; + struct param_isp_adjust adjust; + struct param_isp_metering metering; + struct param_isp_afc afc; + struct param_otf_output otf_output; + struct param_dma_output dma1_output; + struct param_dma_output dma2_output; +}; + +/** + * @brief FIMC-IS firmware version 12x specific data + * Informative purposes only + */ +#define FW_V120_SETFILE_SIZE 0xc0d8 +#define FW_V120_TDNR_MEM_SIZE (1920 * 1080 * 4) +#define FW_V120_SHARED_REG_ADDR 0x008c0000 + +/** + * @brief Generic info on FIMC-IS shared memory layout + */ +static const struct fimc_is_fw_mem fimc_is_fw_120_mem = { + .fw_size = 0xb00000, /* FW: 0x00a00000 + SENSOR: 0x00100000 */ + .region_offset = 0x9fb000, /* FW:0x00a00000 - REGION:0x5000 */ + .dbg_offset = 0x840000, + .shot_size = sizeof(struct camera2_shot_base), + .meta_type = CAMERA2_SHOT_BASE_MODE, +}; + +/** + * @brief Get the memory layout for given FIMC-IS firmware version + */ +static int fimc_is_fw_120_mem_cfg(struct fimc_is_fw_mem *memcfg) +{ + if(!memcfg) + return -EINVAL; + memcpy(memcfg, &fimc_is_fw_120_mem, sizeof(fimc_is_fw_120_mem)); + return 0; +} + +/** + * @brief Tweak FIMC-IS sub-block OTF input settings + */ +static int fimc_is_fw_120_tweak_dma(struct param_dma_input *dma_input, + struct v4l2_rect* crop_reg) +{ + FW_TYPE(V120, dma_input_ext) *params; + params = (FW_TYPE(V120, dma_input_ext)*)dma_input->reserved; + + params->dma_crop_offset_x = 0; + params->dma_crop_offset_y = 0; + params->dma_crop_width = dma_input->width; + params->dma_crop_height = dma_input->height; + /* Bayer crop */ + params->bayer_crop_offset_x = crop_reg->left; + params->bayer_crop_offset_y = crop_reg->top; + params->bayer_crop_width = crop_reg->width; + params->bayer_crop_height = crop_reg->height; + + params->user_min_frametime = 0; + params->user_max_frametime = 1000000; + + params->wide_frame_gap = 1; + params->frame_gap = 4096; + params->line_gap = 45; + /* + * [0] : sensor size same as dma input size + * [x] : reserved field for sensor size + */ + params->reserved[1] = 0; + params->reserved[2] = 0; + return 0; +} + +/** + * @brief Tweak FIMC-IS ISP sub-block settings + */ +static int fimc_is_fw_120_tweak_isp(struct taa_param *taa_param, + struct v4l2_rect* crop_reg) +{ + FW_TYPE(V120, isp_ext) *params; + params = (FW_TYPE(V120, isp_ext)*)(taa_param); + + params->aa.cmd = ISP_AA_COMMAND_START; + params->flash.cmd = ISP_FLASH_COMMAND_DISABLE; + params->flash.redeye = ISP_FLASH_REDEYE_DISABLE; + params->awb.cmd = ISP_AWB_COMMAND_AUTO; + params->effect.cmd = ISP_IMAGE_EFFECT_DISABLE; + params->iso.cmd = ISP_ISO_COMMAND_AUTO; + params->adjust.cmd = ISP_ADJUST_COMMAND_AUTO; + params->metering.cmd = ISP_METERING_COMMAND_CENTER; + params->metering.win_width = params->dma1_input.width; + params->metering.win_height = params->dma1_input.height; + params->afc.cmd = ISP_AFC_COMMAND_AUTO; + + return 0; +} + +/** + * @brief Tweak configuration parameters for requested FIMC-IS sub-block + */ +static int fimc_is_fw_120_tweak_params(unsigned int type, void* base_addr, void *data) +{ + int ret = -EINVAL; + + if (!base_addr) + return ret; + if(PARAM_DMA_INPUT == type) + ret = fimc_is_fw_120_tweak_dma(base_addr, data); + else if (PARAM_ISP_CAM == type) + ret = fimc_is_fw_120_tweak_isp(base_addr, data); + return ret; +} + +/** + * @brief Setup inittial camera shot configuration + */ +static int fimc_is_fw_120_config_shot(void * addr) +{ + struct camera2_shot_base *shot = (struct camera2_shot_base*)addr; + if (!addr) + return -EINVAL; + shot->magicnumber = FIMC_IS_MAGIC_NUMBER; + shot->ctl.aa.mode = AA_CONTROL_AUTO; + shot->ctl.aa.ae_mode = AA_AEMODE_ON; + return 0; +} + +static const struct fimc_is_fw_ops fimc_is_fw_120_ops = { + .mem_config = fimc_is_fw_120_mem_cfg, + .tweak_param = fimc_is_fw_120_tweak_params, + .config_shot = fimc_is_fw_120_config_shot, +}; +/** @} */ /* End of FIMC_IS_FW_V120_SUPPORT */ + +/** + * @defgroup FIMC_IS_FW_V130_SUPPORT + * FIMC-IS firmware versrion 13x support + * + * @{ + */ + +/** + * @brief FIMC IS sub-block OTF input confgiuration + * parameters specific for given firmware version + */ +DECLARE_FW_SPECIFIC_TYPE(V130,otf_input_ext) { + u32 sensor_binning_ratio_x; + u32 sensor_binning_ratio_y; + u32 bns_binning_enable; + u32 bns_binning_ratio_x; + u32 bns_binning_ratio_y; + u32 bns_margin_left; + u32 bns_binning_top; + u32 bns_output_width; + u32 bns_output_height; + u32 crop_enable; + u32 crop_offset_x; + u32 crop_offset_y; + u32 crop_width; + u32 crop_height; + u32 bds_out_enable; + u32 bds_out_width; + u32 bds_out_height; + u32 frametime_min; + u32 frametime_max; + u32 scaler_path_sel; +}; + +/** + * @brief FIMC IS sub-block DMA input confgiuration + * parameters specific for given firmware version + */ +DECLARE_FW_SPECIFIC_TYPE(V130, dma_input_ext) { + u32 sensor_binning_ratio_x; /* ex(x1: 1000, x0.5: 2000) */ + u32 sensor_binning_ratio_y; + u32 dma_crop_enable; + u32 dma_crop_offset_x; + u32 dma_crop_offset_y; + u32 dma_crop_width; + u32 dma_crop_height; + u32 bayer_crop_enable; + u32 bayer_crop_offset_x; + u32 bayer_crop_offset_y; + u32 bayer_crop_width; + u32 bayer_crop_height; + u32 bds_out_enable; + u32 bds_out_width; + u32 bds_out_height; + u32 user_min_frametime; + u32 user_max_frametime; + u32 reserved[2]; +}; + +/** + * @brief FIMC-IS firmware version 13x specific data + * Informative purposes only + */ +#define FW_V130_SETFILE_SIZE 0x00140000 +#define FW_V130_SHARED_REG_ADDR 0x013C0000 + +/** + * @brief Generic info on FIMC-IS shared memory layout + */ +static const struct fimc_is_fw_mem fimc_is_fw_130_mem = { + .fw_size = 0x01400000, + .region_offset = 0x13fb000, /* FW: 0x01400000 - REGION: 0x00005000 */ + .dbg_offset = 0x01340000, + .shot_size = sizeof(struct camera2_shot), + .meta_type = CAMERA2_SHOT_EXT_MODE, +}; + +/** + * @brief Get the memory layout for given FIMC-IS firmware version + */ +static int fimc_is_fw_130_mem_cfg(struct fimc_is_fw_mem *memcfg) +{ + if(!memcfg) + return -EINVAL; + memcpy(memcfg, &fimc_is_fw_130_mem, sizeof(fimc_is_fw_130_mem)); + return 0; +} + +/** + * @brief Tweak FIMC-IS sub-block OTF input settings + */ +static int fimc_is_fw_130_tweak_otf(struct param_otf_input * otf_input, + struct v4l2_rect* crop_reg) +{ + FW_TYPE(V130, otf_input_ext) *params; + params = (FW_TYPE(V130,otf_input_ext)*)otf_input->reserved; + + params->crop_enable = 1; + params->crop_offset_x = crop_reg->left; + params->crop_offset_y = crop_reg->top; + params->crop_width = crop_reg->width; + params->crop_height = crop_reg->height; + + params->frametime_min = 0; + params->frametime_max = 1000000; + + return 0; +} + +/** + * @brief Tweak FIMC-IS sub-block DMA input settings + */ +static int fimc_is_fw_130_tweak_dma(struct param_dma_input *dma_input, + struct v4l2_rect* crop_reg) +{ + /* Currently only ISP dma input is supported */ + FW_TYPE(V130, dma_input_ext) *params; + params = (FW_TYPE(V130, dma_input_ext)*)dma_input->reserved; + + params->dma_crop_enable = 1; + params->dma_crop_offset_x = 0; + params->dma_crop_offset_y = 0; + params->dma_crop_width = crop_reg->width; + params->dma_crop_height = crop_reg->height; + /* Bayer crop */ + params->bayer_crop_enable = 1; + params->bayer_crop_offset_x = crop_reg->left; + params->bayer_crop_offset_y = crop_reg->top; + params->bayer_crop_width = crop_reg->width; + params->bayer_crop_height = crop_reg->height; + /* Bundle destination size */ + params->bds_out_enable = 1; + params->bds_out_width = crop_reg->width; + params->bds_out_height = crop_reg->height; + + params->user_min_frametime = 0; + params->user_max_frametime = 1000000; + /* + * [0] : sensor size same as dma input size + * [x] : reserved field for sensor size + */ + params->reserved[1] = 0; + params->reserved[2] = 0; + + return 0; +} + +/** + * @brief Tweak configuration parameters for requested FIMC-IS sub-block + */ +static int fimc_is_fw_130_tweak_params(unsigned int type, void* base_addr, void *data) +{ + int ret = -EINVAL; + + if (!base_addr) + return ret; + if (PARAM_OTF_INPUT == type) + ret = fimc_is_fw_130_tweak_otf(base_addr, data); + else + if(PARAM_DMA_INPUT == type) + ret = fimc_is_fw_130_tweak_dma(base_addr, data); + return ret; +} + +/** + * @brief Setup inittial camera shot configuration + */ +static int fimc_is_fw_130_config_shot(void * addr) +{ + struct camera2_shot *shot = (struct camera2_shot*)addr; + if (!addr) + return -EINVAL; + /* CAM CONTROL */ + /* REQUEST */ + shot->ctl.request.base.id = 1; + shot->ctl.request.base.metadatamode = METADATA_MODE_FULL; + shot->ctl.request.base.framecount = 0; + /* LENS */ + shot->ctl.lens.optical_stabilization_mode = OPTICAL_STABILIZATION_MODE_ON; + shot->ctl.lens.aperture = 1; + /* SENSOR */ + shot->ctl.sensor.exposure_time = 150000000; + shot->ctl.sensor.sensitivity = 100; + /* FLASH */ + /* HOTPIXEL */ + shot->ctl.hotpixel.mode = PROCESSING_MODE_HIGH_QUALITY; + /* DEMOSAIC*/ + shot->ctl.demosaic.mode = PROCESSING_MODE_HIGH_QUALITY; + /* NOISE */ + shot->ctl.noise.mode = PROCESSING_MODE_HIGH_QUALITY; + shot->ctl.noise.strength = 1; + /* SHADING */ + shot->ctl.shading.mode = PROCESSING_MODE_HIGH_QUALITY; + /* GEOMETRIC */ + /* COLOR */ + shot->ctl.color.base.mode = COLOR_CORRECTION_MODE_HIGH_QUALITY; + shot->ctl.color.base.saturation = 1; + /* TONEMAP */ + /* EDGE */ + /* SCALER */ + /* JPEG */ + /* STATS */ + /* AA */ + shot->ctl.aa.capture_intent = AA_CAPTURE_INTENT_PREVIEW; + shot->ctl.aa.mode = AA_CONTROL_AUTO; + shot->ctl.aa.ae_mode = AA_AEMODE_ON; + + shot->dm.aa.iso_mode = AA_ISOMODE_MANUAL; + shot->dm.aa.iso_value = 100; + shot->ctl.aa.ae_anti_banding_mode = AA_AE_ANTIBANDING_AUTO; + shot->ctl.aa.ae_flash_mode = AA_FLASHMODE_ON; + shot->ctl.aa.awb_mode = AA_AWBMODE_WB_AUTO; + shot->ctl.aa.af_mode = AA_AFMODE_OFF; + shot->ctl.aa.ae_target_fps_range[0] = 15; + shot->ctl.aa.ae_target_fps_range[1] = 30; + /* USER CONTROL */ + shot->uctl.u_update_bitmap = 1 /* SENSOR */ | (1<<1) /* FLASH*/; + shot->uctl.flash_ud.ctl.flash_mode = CAM2_FLASH_MODE_SINGLE; + + shot->magicnumber = FIMC_IS_MAGIC_NUMBER; + + return 0; +} + +static const struct fimc_is_fw_ops fimc_is_fw_130_ops = { + .mem_config = fimc_is_fw_130_mem_cfg, + .tweak_param = fimc_is_fw_130_tweak_params, + .config_shot = fimc_is_fw_130_config_shot, +}; +/** @} */ /* End of FIMC_IS_FW_V130_SUPPORT */ + + +/** + * @defgroup FIMC_IS_FW_SUPPORT + * FIMC-IS firmware support + * + * @{ + */ +/** + * @brief Supported FIMC-IS firmwares + */ +static const struct fimc_is_fw_config { + + unsigned int version; + const struct fimc_is_fw_ops *ops; + +} fimc_is_fw_configs[] = { + { + .version = FIMC_IS_FW_V120, + .ops = &fimc_is_fw_120_ops, + }, + { + .version = FIMC_IS_FW_V130, + .ops = &fimc_is_fw_130_ops, + }, +}; + +/** + * @brief Set-up appropriate FIMC-IS firmware + * configuration based on given fimrawre + * verson + */ +int fimc_is_fw_init_ops(unsigned int fw_version) +{ + int i; + /* + * Drop the last digit from the firmware version : + * e.g.: ver. 130-139 should fall back to 130 + */ + fw_version -= fw_version%10; + for (i=0; i < ARRAY_SIZE(fimc_is_fw_configs); ++i) { + if (fimc_is_fw_configs[i].version == fw_version) { + fimc_is_fw_ops = fimc_is_fw_configs[i].ops; + return 0; + } + } + return -EINVAL; +} +/** @} */ /* End of FIMC_IS_FW_SUPPORT */ diff --git a/drivers/media/platform/exynos5-is/fimc-is-fw.h b/drivers/media/platform/exynos5-is/fimc-is-fw.h new file mode 100644 index 00000000000..fb289d32f46 --- /dev/null +++ b/drivers/media/platform/exynos5-is/fimc-is-fw.h @@ -0,0 +1,73 @@ +/* + * Samsung EXYNOS FIMC-IS (Imaging Subsystem) 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_FW_CONFIG_H +#define FIMC_IS_FW_CONFIG_H + +/** + * @brief Supported FIMC IS FW versions + */ +#define FIMC_IS_FW_V120 120 /* And higher */ +#define FIMC_IS_FW_V130 130 /* And higher */ + + +/** + * @brief FIMC-IS firmware specific sub-blocks + * configuration options + */ +#define PARAM_OTF_INPUT 0x01 +#define PARAM_DMA_INPUT 0x02 +#define PARAM_ISP_CAM 0x03 + +/** + * struct fimc_is_fw_mem - FW shared memory layout + * @fw_size - total size of FW memory + * @region_offset - sub-block configuration region offset + * @shared_offset - MCUCTL shared memory offset + * @dbg_offset - FW debug region offset + * @camshot_size - camera shot configuration size + * @meta_type - Metadata layout type (basic vs extended) + */ +struct fimc_is_fw_mem { + unsigned long fw_size; + unsigned long region_offset; + unsigned long shared_offset; + unsigned long dbg_offset; + unsigned long shot_size; + unsigned int meta_type; +}; + +/** + * @brief FIMC-IS supported firmware-specific operations + */ +struct fimc_is_fw_ops { + /* Get firmware specific memory layout information */ + int (*mem_config) (struct fimc_is_fw_mem*); + /* Tweak FIMC-IS subblock parameters specific for given firmware */ + int (*tweak_param) (unsigned int, void *, void*); + /* Setup initial shot configuration */ + int (*config_shot) (void*); +}; + +extern const struct fimc_is_fw_ops *fimc_is_fw_ops; + +/** + * @brief Perform requested operation on current firmware + * (if supported) + */ +#define FIMC_IS_FW_CALL_OP(op, args...) \ + ((fimc_is_fw_ops && fimc_is_fw_ops->op) \ + ? fimc_is_fw_ops->op(args) : -EINVAL) + +/** + * @brief Init supported operations for requested firmware version + */ +int fimc_is_fw_init_ops(unsigned int); + +#endif /*FIMC_IS_FW_CONFIG_H*/ diff --git a/drivers/media/platform/exynos5-is/fimc-is-interface.c b/drivers/media/platform/exynos5-is/fimc-is-interface.c index c5da6ff8154..662821dc869 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-interface.c +++ b/drivers/media/platform/exynos5-is/fimc-is-interface.c @@ -1,7 +1,7 @@ /* * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd. * Kil-yeon Lim <kilyeon.im@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -14,65 +14,183 @@ #include "fimc-is.h" #include "fimc-is-cmd.h" #include "fimc-is-regs.h" +#include "fimc-is-backend.h" +#include "fimc-is-fw.h" + +#define FIMC_IS_GROUP_ID_3A0 0 /* hardware: CH0 */ +#define FIMC_IS_GROUP_ID_3A1 1 /* hardware: 3AA */ +#define FIMC_IS_GROUP_ID_ISP 2 /* hardware: CH1 */ +#define FIMC_IS_GROUP_ID_DIS 3 +#define FIMC_IS_GROUP_ID_SHIFT 16 +#define FIMC_IS_GROUP_ID \ + ((FIMC_IS_GROUP_ID_3A1 | (1 <<FIMC_IS_GROUP_ID_ISP)) \ + << FIMC_IS_GROUP_ID_SHIFT) #define init_request_barrier(itf) mutex_init(&itf->request_barrier) #define enter_request_barrier(itf) mutex_lock(&itf->request_barrier) #define exit_request_barrier(itf) mutex_unlock(&itf->request_barrier) -static inline void itf_get_cmd(struct fimc_is_interface *itf, - struct fimc_is_msg *msg, unsigned int index) +struct workqueue_struct *fimc_is_workqueue; + +#ifdef FIMC_IS_DEBUG +#define read_shared_reg(__r, __offset) ({ \ + pr_info("[FIMC IS ITF] Reading shared reg: base: 0x%08X"\ + " offset: 0x%08X\n", \ + (unsigned int)(__r), (unsigned int)(__offset)); \ + readl((__r) + (__offset)); \ +}) + +#define write_shared_reg(__r, __offset, __v) ({ \ + pr_info("[FIMC IS ITF] Writting shared reg: 0x%08x" \ + " offset:0x%08x (%d)\n", \ + (unsigned int)(__r), (unsigned int)(__offset), __v); \ + writel((__v), (__r) + (__offset)); \ +}) + +#define dump_message(msg) \ +do{ \ + struct fimc_is_msg* __m =(struct fimc_is_msg*) msg; \ + printk("[FIMC IS MSG] >> %s: \n", __func__); \ + printk("[FIMC IS MSG] ID [%d] COMMAND [%d] INSTANCE [%d]\n", \ + __m->id, __m->command, __m->instance); \ + printk("[FIMC IS MSG] PARAMS: {%d, %d, %d, %d}\n", \ + __m->param[0], __m->param[1], __m->param[2], __m->param[3]); \ +}while(0) + +#else +#define read_shared_reg(__r, __offset) \ + readl((__r) +(__offset)) +#define write_shared_reg(__r, __offset, __v) \ + writel(__v, (__r) + (__offset)) + +#define dump_message(msg) + +#endif + + +static inline void setup_cmd(struct fimc_is_msg *msg, + void __iomem *base_reg, + u32 offset, + u32 param_count) { - struct is_common_reg __iomem *com_regs = itf->com_regs; + /* + * Base command layout ( consecutive 32 bits registers ) + * | REG_0 | -> | command ID | + * | REG_1 | -> | sesnor ID (instance)| + * | REG_2 | -> | commads params | + */ + memset(msg, 0, sizeof(msg)); + msg->command = read_shared_reg(base_reg, offset); + msg->instance = read_shared_reg(base_reg, offset + MCUCTL_SREG_SIZE); + memcpy(msg->param, base_reg + offset + 2*MCUCTL_SREG_SIZE, + param_count * sizeof(msg->param[0])); +} + +static inline void write_hic_shared_reg(struct fimc_is_interface *itf, + struct fimc_is_msg *msg) +{ + struct mcuctl_sreg_desc shared_reg_desc; + void __iomem *base_reg = itf->shared_regs; + /* It is safe to assume that the calls to get the + * MCUCTL_HIC_REG data will always succeede despite the + * actual firmware version + */ + mcuctl_sreg_get_desc(itf->fw_data, MCUCTL_HIC_REG, + &shared_reg_desc); + write_shared_reg(base_reg, shared_reg_desc.base_offset, msg->command); + write_shared_reg(base_reg, + shared_reg_desc.base_offset + MCUCTL_SREG_SIZE, + msg->instance); + memcpy(base_reg + shared_reg_desc.base_offset + 2*MCUCTL_SREG_SIZE, + msg->param, shared_reg_desc.data_range * sizeof(u32)); + +} - memset(msg, 0, sizeof(*msg)); +static inline int itf_get_cmd(struct fimc_is_interface *itf, + struct fimc_is_msg *msg, unsigned int index) +{ + void __iomem *regs = itf->shared_regs; + unsigned int reg_range_id; + struct mcuctl_sreg_desc shared_reg_desc; switch (index) { case INTR_GENERAL: - msg->command = com_regs->ihcmd; - msg->instance = com_regs->ihc_sensorid; - memcpy(msg->param, com_regs->ihc_param, - 4 * sizeof(msg->param[0])); + reg_range_id = MCUCTL_IHC_REG; + break; + case INTR_3A0C_DONE: + reg_range_id = MCUCTL_3AA0C_REG; break; - case INTR_SCC_FDONE: - msg->command = IHC_FRAME_DONE; - msg->instance = com_regs->scc_sensor_id; - memcpy(msg->param, com_regs->scc_param, - 3 * sizeof(msg->param[0])); + case INTR_SCC_DONE: + reg_range_id = MCUCTL_SCC_REG; break; - case INTR_SCP_FDONE: - msg->command = IHC_FRAME_DONE; - msg->instance = com_regs->scp_sensor_id; - memcpy(msg->param, com_regs->scp_param, - 3 * sizeof(msg->param[0])); + case INTR_SCP_DONE: + reg_range_id = MCUCTL_SCP_REG; break; case INTR_META_DONE: - msg->command = IHC_FRAME_DONE; - msg->instance = com_regs->meta_sensor_id; - msg->param[0] = com_regs->meta_param1; + reg_range_id = MCUCTL_META_REG; break; case INTR_SHOT_DONE: - msg->command = IHC_FRAME_DONE; - msg->instance = com_regs->shot_sensor_id; - memcpy(msg->param, com_regs->shot_param, - 2 * sizeof(msg->param[0])); + reg_range_id = MCUCTL_SHOT_REG; break; default: + reg_range_id = MCUCTL_END_REG; dev_err(itf->dev, "%s Unknown command\n", __func__); break; } + + if (!mcuctl_sreg_get_desc(itf->fw_data, reg_range_id, + &shared_reg_desc)) { + if (index == INTR_GENERAL) { + /* + * Move to the very next register + * @see generic MCUCTL shared reg layout + */ + shared_reg_desc.base_offset += MCUCTL_SREG_SIZE; + } + setup_cmd(msg, regs, shared_reg_desc.base_offset, + shared_reg_desc.data_range); + switch(index) { + case INTR_3A0C_DONE: + case INTR_SCC_DONE: + case INTR_SCP_DONE: + case INTR_SHOT_DONE: + msg->command = IHC_FRAME_DONE; + default: + break; + } + return 0; + } + return -EINVAL; } static inline unsigned int itf_get_intr(struct fimc_is_interface *itf) { unsigned int status; - struct is_common_reg __iomem *com_regs = itf->com_regs; - - status = readl(itf->regs + INTMSR1) | com_regs->ihcmd_iflag | - com_regs->scc_iflag | - com_regs->scp_iflag | - com_regs->meta_iflag | - com_regs->shot_iflag; - + void __iomem *shared_regs = itf->shared_regs; + unsigned long offset; + struct fimc_is_fw_data *fw_data = itf->fw_data; + + status = read_shared_reg(itf->regs, INTMSR1); + /* + * Again it is safe to assume that the following registers + * are always valid despite the actual firmware version + */ + mcuctl_sreg_get_offset(fw_data, MCUCTL_IHC_REG, &offset); + status |= read_shared_reg(shared_regs, offset); + + mcuctl_sreg_get_offset(fw_data, MCUCTL_SHOT_REG, &offset); + status |= read_shared_reg(shared_regs, offset); + + mcuctl_sreg_get_offset(fw_data, MCUCTL_SCC_REG, &offset); + status |= read_shared_reg(shared_regs, offset); + + mcuctl_sreg_get_offset(fw_data, MCUCTL_SCP_REG, &offset); + status |= read_shared_reg(shared_regs, offset); + + if (mcuctl_sreg_is_valid(fw_data, MCUCTL_META_REG)) { + mcuctl_sreg_get_offset(fw_data, MCUCTL_META_REG, &offset); + status |= read_shared_reg(shared_regs, offset); + } return status; } @@ -125,6 +243,7 @@ static int itf_wait_hw_ready(struct fimc_is_interface *itf) unsigned int cfg = readl(itf->regs + INTMSR0); if (INTMSR0_GET_INTMSD(0, cfg) == 0) return 0; + } dev_err(itf->dev, "INTMSR0's 0 bit is not cleared.\n"); return -EINVAL; @@ -174,10 +293,7 @@ static int itf_send_sensor_number(struct fimc_is_interface *itf) unsigned long flags; spin_lock_irqsave(&itf->slock, flags); - itf->com_regs->hicmd = msg.command; - itf->com_regs->hic_sensorid = msg.instance; - memcpy(itf->com_regs->hic_param, msg.param, - 4 * sizeof(itf->com_regs->hic_param[0])); + write_hic_shared_reg(itf, &msg); itf_hic_interrupt(itf); spin_unlock_irqrestore(&itf->slock, flags); @@ -229,10 +345,7 @@ static int fimc_is_itf_set_cmd(struct fimc_is_interface *itf, spin_lock_irqsave(&itf->slock, flags); itf_set_state(itf, IS_IF_STATE_BUSY); - itf->com_regs->hicmd = msg->command; - itf->com_regs->hic_sensorid = msg->instance; - memcpy(itf->com_regs->hic_param, msg->param, - 4 * sizeof(itf->com_regs->hic_param[0])); + write_hic_shared_reg(itf, msg); itf_hic_interrupt(itf); spin_unlock_irqrestore(&itf->slock, flags); @@ -241,13 +354,15 @@ static int fimc_is_itf_set_cmd(struct fimc_is_interface *itf, ret = itf_wait_idlestate(itf); if (ret) { - dev_err(itf->dev, "%d command is timeout\n", msg->command); + dev_err(itf->dev, "Timeout on command id: %d\n", msg->command); itf_clr_state(itf, IS_IF_STATE_BUSY); ret = -ETIME; goto exit; } if (itf->reply.command == ISR_DONE) { + + /* @TODO : locking ? */ switch (msg->command) { case HIC_STREAM_ON: itf->streaming = IS_IF_STREAMING_ON; @@ -270,8 +385,12 @@ static int fimc_is_itf_set_cmd(struct fimc_is_interface *itf, itf->pdown_ready = IS_IF_POWER_DOWN_READY; ret = -ECANCELED; goto exit; - } else + } else { + /* Fix possible state-machine inconsistency */ + itf->streaming = IS_IF_STREAMING_OFF; + itf->processing = IS_IF_PROCESSING_OFF; itf->pdown_ready = IS_IF_POWER_DOWN_NREADY; + } break; default: break; @@ -293,19 +412,37 @@ static int fimc_is_itf_set_cmd_shot(struct fimc_is_interface *itf, struct fimc_is_msg *msg) { unsigned long flags; - + struct mcuctl_sreg_desc shared_reg_desc; spin_lock_irqsave(&itf->slock, flags); - itf->com_regs->hicmd = msg->command; - itf->com_regs->hic_sensorid = msg->instance; - memcpy(itf->com_regs->hic_param, msg->param, - 4 * sizeof(itf->com_regs->hic_param[0])); - itf->com_regs->fcount = msg->param[2]; + write_hic_shared_reg(itf, msg); + + if (!mcuctl_sreg_get_desc(itf->fw_data, MCUCTL_FRAME_COUNT_REG, + &shared_reg_desc)) { + unsigned long offset = shared_reg_desc.base_offset; + /* Frame count for sensor:0 */ + offset += (--shared_reg_desc.data_range) * MCUCTL_SREG_SIZE; + write_shared_reg(itf->shared_regs, offset, msg->param[2]); + } + itf_hic_interrupt(itf); spin_unlock_irqrestore(&itf->slock, flags); - return 0; } +static int itf_init_workqueue(void) +{ + fimc_is_workqueue = alloc_workqueue("fimc-is", WQ_FREEZABLE,0); + return fimc_is_workqueue ? 0 : -ENOMEM; +} + +static void shot_done_work_fn(struct work_struct* work) +{ + struct fimc_is_interface *itf = container_of(work, + struct fimc_is_interface, shot_done_work); + struct fimc_is *is = fimc_interface_to_is(itf); + fimc_is_pipeline_shot(&is->pipeline[0]); +} + static void itf_handle_general(struct fimc_is_interface *itf, struct fimc_is_msg *msg) { @@ -315,7 +452,10 @@ static void itf_handle_general(struct fimc_is_interface *itf, case IHC_GET_SENSOR_NUMBER: pr_debug("IS version : %d.%d\n", - ISDRV_VERSION, msg->param[0]); + itf->drv_version, msg->param[0]); + if (fimc_is_fw_config(&itf->fw_data, msg->param[0])) + dev_err(itf->dev, + "Failed to initialize fw data. Using default settings"); /* Respond with sensor number */ itf_send_sensor_number(itf); itf_init_wakeup(itf); @@ -325,6 +465,9 @@ static void itf_handle_general(struct fimc_is_interface *itf, case HIC_OPEN_SENSOR: pr_debug("open done\n"); break; + case HIC_CLOSE_SENSOR: + pr_debug("close done\n"); + break; case HIC_GET_SET_FILE_ADDR: pr_debug("saddr(%p) done\n", (void *)msg->param[1]); @@ -369,6 +512,11 @@ static void itf_handle_general(struct fimc_is_interface *itf, is_blocking = false; dev_err(itf->dev, "camctrl is not acceptable\n"); break; + case HIC_SENSOR_MODE_CHANGE: + pr_debug("Sensor mode changed\n"); + break; + case HIC_GET_IP_STATUS: + break; default: is_blocking = false; dev_err(itf->dev, "unknown done is invokded\n"); @@ -391,6 +539,8 @@ static void itf_handle_general(struct fimc_is_interface *itf, dev_err(itf->dev, "param3 : 0x%08X\n", msg->param[2]); dev_err(itf->dev, "param4 : 0x%08X\n", msg->param[3]); break; + case HIC_GET_IP_STATUS: + break; default: dev_err(itf->dev, "command(%d) not done", msg->param[0]); @@ -413,8 +563,11 @@ static void itf_handle_general(struct fimc_is_interface *itf, break; case IHC_NOT_READY: is_blocking = false; - dev_err(itf->dev, "IHC_NOT_READY is occured, need reset"); + dev_err(itf->dev, "IHC_NOT_READY has been reported: reset required\n"); break; + case IHC_REPORT_ERR: + is_blocking = false; + dev_err(itf->dev, "IHC_REPORT_ERR has occured"); default: is_blocking = false; dev_err(itf->dev, "%s: unknown (#%08X) command\n", @@ -440,14 +593,21 @@ static void itf_handle_scaler_done(struct fimc_is_interface *itf, struct timespec ts; unsigned int wh, i; unsigned int fcount = msg->param[0]; - unsigned long *comp_state; - + unsigned long *subip_state; + + /* + * Scaler done message layout + * 1: frame count + * 2: status != 0 -> frame not done + * 3: requested frame count + */ if (msg->param[3] == SCALER_SCC) { scl = &pipeline->scaler[SCALER_SCC]; - comp_state = &pipeline->comp_state[IS_SCC]; + subip_state = &pipeline->subip_state[IS_SCC]; + } else { scl = &pipeline->scaler[SCALER_SCP]; - comp_state = &pipeline->comp_state[IS_SCP]; + subip_state = &pipeline->subip_state[IS_SCP]; } fmt = scl->fmt; @@ -470,10 +630,13 @@ static void itf_handle_scaler_done(struct fimc_is_interface *itf, pr_debug("SCP buffer done %d/%d\n", msg->param[0], msg->param[2]); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + if (msg->param[1]) + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + else + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } fimc_is_pipeline_buf_unlock(pipeline); - clear_bit(COMP_RUN, comp_state); + clear_bit(COMP_RUN, subip_state); wake_up(&scl->event_q); } @@ -481,29 +644,85 @@ static void itf_handle_shot_done(struct fimc_is_interface *itf, struct fimc_is_msg *msg) { struct fimc_is *is = fimc_interface_to_is(itf); - struct fimc_is_pipeline *pipeline = &is->pipeline[msg->instance]; + struct fimc_is_pipeline *pipeline = + &is->pipeline[msg->instance &~FIMC_IS_GROUP_ID]; unsigned int status = msg->param[1]; struct fimc_is_buf *bayer_buf; - int ret; - if (status != ISR_DONE) - dev_err(itf->dev, "Shot done is invalid(0x%08X)\n", status); + /* + * Shot done message layout: + * 1: frame count + * 2: status + * 3: status for shot: 0 - success + */ /* DQ the bayer input buffer */ fimc_is_pipeline_buf_lock(pipeline); bayer_buf = fimc_is_isp_run_queue_get(&pipeline->isp); if (bayer_buf) { - vb2_buffer_done(&bayer_buf->vb, VB2_BUF_STATE_DONE); + if (msg->param[2]) + vb2_buffer_done(&bayer_buf->vb, VB2_BUF_STATE_ERROR); + else + vb2_buffer_done(&bayer_buf->vb, VB2_BUF_STATE_DONE); pr_debug("Bayer buffer done.\n"); } fimc_is_pipeline_buf_unlock(pipeline); /* Clear state & call shot again */ clear_bit(PIPELINE_RUN, &pipeline->state); + clear_bit(COMP_RUN, &pipeline->subip_state[IS_ISP]); + queue_work(fimc_is_workqueue, &itf->shot_done_work); + if (status != ISR_DONE) { + fimc_is_pipeline_reset(pipeline); + dev_err(itf->dev, "Shot failure (0x%08X : 0x%08X) \n", + status, msg->param[2]); + + } +} - ret = fimc_is_pipeline_shot(pipeline); - if (ret) - dev_err(itf->dev, "Shot failed\n"); +static inline void fimc_is_handle_irq(struct fimc_is_interface *itf, + struct fimc_is_msg *msg, + u32 intsrc) +{ + void __iomem *shared_regs = itf->shared_regs; + unsigned long offset; + unsigned int reg_range_id = MCUCTL_END_REG; + + switch(intsrc) { + case INTR_GENERAL: + itf_handle_general(itf, msg); + reg_range_id = MCUCTL_IHC_REG; + break; + case INTR_ISP_DONE: + reg_range_id = MCUCTL_ISP_REG; + break; + case INTR_3A0C_DONE: + reg_range_id = MCUCTL_3AA0C_REG; + break; + case INTR_3A1C_DONE: + reg_range_id = MCUCTL_3AA1C_REG; + break; + case INTR_SHOT_DONE: + itf_handle_shot_done(itf, msg); + reg_range_id = MCUCTL_SHOT_REG; + break; + case INTR_SCC_DONE: + msg->param[3] = SCALER_SCC; + itf_handle_scaler_done(itf, msg); + reg_range_id = MCUCTL_SCC_REG; + break; + case INTR_SCP_DONE: + msg->param[3] = SCALER_SCP; + itf_handle_scaler_done(itf, msg); + reg_range_id = MCUCTL_SCP_REG; + break; + case INTR_META_DONE: + reg_range_id = MCUCTL_META_REG; + break; + } + if (!(mcuctl_sreg_get_offset(itf->fw_data, reg_range_id, &offset))){ + write_shared_reg(shared_regs, offset, 0); + } } /* Main FIMC-IS interrupt handler */ @@ -512,44 +731,20 @@ static irqreturn_t itf_irq_handler(int irq, void *data) struct fimc_is_interface *itf = data; struct fimc_is_msg msg; unsigned int status, intr; - struct is_common_reg __iomem *com_regs; - - com_regs = itf->com_regs; + unsigned int intr_src; status = itf_get_intr(itf); for (intr = INTR_GENERAL; intr < INTR_MAX_MAP; intr++) { if (status & BIT(intr)) { - itf_get_cmd(itf, &msg, intr); - - switch (intr) { - case INTR_GENERAL: - itf_handle_general(itf, &msg); - com_regs->ihcmd_iflag = 0; - break; - case INTR_SHOT_DONE: - itf_handle_shot_done(itf, &msg); - com_regs->shot_iflag = 0; - break; - case INTR_SCC_FDONE: - msg.param[3] = SCALER_SCC; - itf_handle_scaler_done(itf, &msg); - com_regs->scc_iflag = 0; - break; - case INTR_SCP_FDONE: - msg.param[3] = SCALER_SCP; - itf_handle_scaler_done(itf, &msg); - com_regs->scp_iflag = 0; - break; - case INTR_META_DONE: - com_regs->meta_iflag = 0; - break; - } + intr_src = FIMC_IS_FW_GET_INTR_SRC(itf->fw_data, intr); + itf_get_cmd(itf, &msg, intr_src); + fimc_is_handle_irq(itf, &msg, intr_src); status &= ~BIT(intr); writel(BIT(intr), itf->regs + INTCR1); - } - } + } + } if (status != 0) dev_err(itf->dev, "status is NOT all clear(0x%08X)", status); @@ -565,12 +760,49 @@ int fimc_is_itf_open_sensor(struct fimc_is_interface *itf, struct fimc_is_msg msg = { .command = HIC_OPEN_SENSOR, .instance = instance, - .param = { sensor_id, i2c_channel, sensor_ext }, + .param = {sensor_id, i2c_channel, sensor_ext }, + }; + + return fimc_is_itf_set_cmd(itf, &msg); +} + +int fimc_is_itf_open_sensor_ext(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int sensor_id, + unsigned int sensor_ext) +{ + struct fimc_is_msg msg = { + .command = HIC_OPEN_SENSOR, + .instance = instance, + .param = { sensor_id, sensor_ext, FIMC_IS_GROUP_ID >> 16 }, }; return fimc_is_itf_set_cmd(itf, &msg); } +int fimc_is_itf_sensor_close(struct fimc_is_interface *itf, + unsigned int instance) +{ + struct fimc_is_msg msg = { + .command = HIC_CLOSE_SENSOR, + .instance = instance, + .param = { 0,}, + }; + return fimc_is_itf_set_cmd(itf, &msg); +} + +int fimc_is_itf_sensor_mode(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int mode) +{ + struct fimc_is_msg msg = { + .command = HIC_SENSOR_MODE_CHANGE, + .instance = instance, + .param = { mode }, + }; + return fimc_is_itf_set_cmd(itf, &msg); +} + int fimc_is_itf_get_setfile_addr(struct fimc_is_interface *itf, unsigned int instance, unsigned int *setfile_addr) { @@ -624,7 +856,7 @@ int fimc_is_itf_process_on(struct fimc_is_interface *itf, { struct fimc_is_msg msg = { .command = HIC_PROCESS_START, - .instance = instance, + .instance = instance | FIMC_IS_GROUP_ID, }; return fimc_is_itf_set_cmd(itf, &msg); @@ -635,7 +867,7 @@ int fimc_is_itf_process_off(struct fimc_is_interface *itf, { struct fimc_is_msg msg = { .command = HIC_PROCESS_STOP, - .instance = instance, + .instance = instance | FIMC_IS_GROUP_ID, }; return fimc_is_itf_set_cmd(itf, &msg); @@ -643,15 +875,18 @@ int fimc_is_itf_process_off(struct fimc_is_interface *itf, int fimc_is_itf_set_param(struct fimc_is_interface *itf, unsigned int instance, + unsigned int scenario, unsigned int lindex, unsigned int hindex) { struct fimc_is_msg msg = { .command = HIC_SET_PARAMETER, - .instance = instance, - .param = { ISS_PREVIEW_STILL, 0, lindex, hindex }, + .instance = instance , + .param = { scenario, 0, lindex, hindex }, }; - + unsigned int param_count = hweight32(lindex); + param_count += hweight32(hindex); + msg.param[1] = param_count; return fimc_is_itf_set_cmd(itf, &msg); } @@ -660,12 +895,24 @@ int fimc_is_itf_preview_still(struct fimc_is_interface *itf, { struct fimc_is_msg msg = { .command = HIC_PREVIEW_STILL, - .instance = instance, + .instance = instance | FIMC_IS_GROUP_ID, }; return fimc_is_itf_set_cmd(itf, &msg); } +int fimc_is_itf_change_sensor_mode(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int mode) +{ + struct fimc_is_msg msg = { + .command = HIC_SENSOR_MODE_CHANGE, + .instance = instance, + .param = { mode, }, + }; + return fimc_is_itf_set_cmd(itf, &msg); +} + int fimc_is_itf_get_capability(struct fimc_is_interface *itf, unsigned int instance, unsigned int address) { @@ -678,29 +925,44 @@ int fimc_is_itf_get_capability(struct fimc_is_interface *itf, return fimc_is_itf_set_cmd(itf, &msg); } -int fimc_is_itf_cfg_mem(struct fimc_is_interface *itf, +int fimc_is_itf_map_mem(struct fimc_is_interface *itf, unsigned int instance, unsigned int address, unsigned int size) { struct fimc_is_msg msg = { - .command = HIC_SET_A5_MEM_ACCESS, - .instance = instance, + .command = HIC_SET_A5_MEM_ACCESS, /* HIC_SET_A5_MMAP */ + .instance = instance | FIMC_IS_GROUP_ID, .param = { address, size }, }; return fimc_is_itf_set_cmd(itf, &msg); } +int fimc_is_itf_unmap_mem(struct fimc_is_interface *itf, + unsigned int instance) +{ + if (FIMC_IS_FW_CMD_SUPPORT(itf->fw_data, HIC_SET_A5_UNMAP)) { + + struct fimc_is_msg msg = { + .command = HIC_SET_A5_UNMAP, + .instance = instance | FIMC_IS_GROUP_ID, + }; + + return fimc_is_itf_set_cmd(itf, &msg); + } + return 0; +} + int fimc_is_itf_shot_nblk(struct fimc_is_interface *itf, unsigned int instance, unsigned int bayer, unsigned int shot, unsigned int fcount, unsigned int rcount) { struct fimc_is_msg msg = { .command = HIC_SHOT, - .instance = instance, + .instance = instance | FIMC_IS_GROUP_ID, .param = { bayer, shot, fcount, rcount }, }; - + dump_message(&msg); return fimc_is_itf_set_cmd_shot(itf, &msg); } @@ -719,19 +981,85 @@ int fimc_is_itf_power_down(struct fimc_is_interface *itf, return ret; } +int fimc_is_itf_hw_running(struct fimc_is_interface *itf) +{ + if (FIMC_IS_FW_CMD_SUPPORT(itf->fw_data, HIC_GET_IP_STATUS)){ + struct fimc_is_msg msg = { + .command = HIC_GET_IP_STATUS, + }; + return (-ETIME != fimc_is_itf_set_cmd(itf, &msg)) ? 1 : 0; + } + return 0; +} + +int fimc_is_itf_i2c_lock(struct fimc_is_interface *itf, + unsigned int instance, + int i2c_clk, bool lock) +{ + if (FIMC_IS_FW_CMD_SUPPORT(itf->fw_data, HIC_I2C_CONTROL_LOCK)) { + + struct fimc_is_msg msg = { + .command = HIC_I2C_CONTROL_LOCK, + .instance = instance, + .param = {lock, i2c_clk}, + }; + + return fimc_is_itf_set_cmd_shot(itf, &msg); + + } + return 0; +} + +int fimc_is_itf_sys_ctrl(struct fimc_is_interface *itf, + unsigned int instance, + int cmd, int val) +{ + if (FIMC_IS_FW_CMD_SUPPORT(itf->fw_data, HIC_SYSTEM_CONTROL)) { + + struct fimc_is_msg msg = { + .command = HIC_SYSTEM_CONTROL, + .instance = instance, + .param = {cmd, val}, + }; + + return fimc_is_itf_set_cmd_shot(itf, &msg); + } + return 0; +} + +void fimc_is_itf_notify_frame_done(struct fimc_is_interface *itf) +{ + struct mcuctl_sreg_desc shared_reg_desc; + + if (!(mcuctl_sreg_get_desc(itf->fw_data, MCUCTL_FRAME_COUNT_REG, + &shared_reg_desc))) { + unsigned int frame_count; + unsigned long offset = shared_reg_desc.base_offset; + /* Frame count for sensor:0*/ + offset += (--shared_reg_desc.data_range) * MCUCTL_SREG_SIZE; + frame_count = read_shared_reg(itf->shared_regs, offset); + write_shared_reg(itf->shared_regs, offset, ++frame_count); + + } +} + /* Debugfs for showing FW debug messages */ static int fimc_is_log_show(struct seq_file *s, void *data) { struct fimc_is_interface *itf = s->private; struct fimc_is *is = fimc_interface_to_is(itf); + struct fimc_is_fw_mem fw_minfo; + u8 *buf; + + if (FIMC_IS_FW_CALL_OP(mem_config, &fw_minfo)) + return -ENODEV; - const u8 *buf = (u8 *) (is->minfo.fw.vaddr + DEBUG_OFFSET); + buf = (u8 *)(is->minfo.fw.vaddr + fw_minfo.dbg_offset); if (is->minfo.fw.vaddr == 0) { dev_err(itf->dev, "Firmware memory is not initialized\n"); return -EIO; } - seq_printf(s, "%s\n", buf); return 0; } @@ -781,13 +1109,16 @@ int fimc_is_interface_init(struct fimc_is_interface *itf, } itf->regs = regs; - itf->com_regs = (struct is_common_reg *)(regs + ISSR(0)); + itf->shared_regs = (void __iomem*)(regs + ISSR(0)); itf->dev = &is->pdev->dev; - + fimc_is_fw_set_default(&itf->fw_data); init_waitqueue_head(&itf->irq_queue); spin_lock_init(&itf->slock_state); spin_lock_init(&itf->slock); + /* Init the work queue */ + itf_init_workqueue(); + INIT_WORK(&itf->shot_done_work, shot_done_work_fn); /* Register interrupt handler */ ret = devm_request_irq(dev, irq, itf_irq_handler, 0, dev_name(dev), itf); diff --git a/drivers/media/platform/exynos5-is/fimc-is-interface.h b/drivers/media/platform/exynos5-is/fimc-is-interface.h index 44b641b26d3..20f0592878f 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-interface.h +++ b/drivers/media/platform/exynos5-is/fimc-is-interface.h @@ -12,22 +12,12 @@ #define FIMC_IS_INTERFACE_H_ #include "fimc-is-core.h" +#include "fimc-is-backend.h" #define TRY_RECV_AWARE_COUNT 100 -#define ISDRV_VERSION 111 - -enum interrupt_map { - INTR_GENERAL = 0, - INTR_ISP_FDONE = 1, - INTR_SCC_FDONE = 2, - INTR_DNR_FDONE = 3, - INTR_SCP_FDONE = 4, - INTR_ISP_YUV_DONE = 5, - INTR_META_DONE = 6, - INTR_SHOT_DONE = 7, - INTR_MAX_MAP -}; +#define EXYNOS5_ISDRV_VERSION 111 +#define EXYNOS3_ISDRV_VERSION 244 enum fimc_is_interface_state { IS_IF_STATE_INIT, @@ -64,20 +54,22 @@ struct fimc_is_interface { unsigned long state; void __iomem *regs; - struct is_common_reg __iomem *com_regs; + void __iomem *shared_regs; + struct fimc_is_fw_data *fw_data; /* Lock for writing into MCUCTL registers */ spinlock_t slock; /* Lock for context state variable */ spinlock_t slock_state; wait_queue_head_t irq_queue; struct device *dev; + unsigned int drv_version; /* Held while sending commands to FW */ struct mutex request_barrier; - + struct work_struct shot_done_work; enum streaming_state streaming; enum processing_state processing; enum pdown_ready_state pdown_ready; - + struct shared_reg_map_entry *shared_regs_map; struct fimc_is_msg reply; int debug_cnt; @@ -93,6 +85,15 @@ int fimc_is_itf_open_sensor(struct fimc_is_interface *itf, unsigned int sensor_id, unsigned int i2c_channel, unsigned int sensor_ext); +int fimc_is_itf_open_sensor_ext(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int sensor_id, + unsigned int sensor_ext); +int fimc_is_itf_sensor_close(struct fimc_is_interface *itf, + unsigned int instance); +int fimc_is_itf_sensor_mode(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int mode); int fimc_is_itf_get_setfile_addr(struct fimc_is_interface *this, unsigned int instance, unsigned int *setfile_addr); int fimc_is_itf_load_setfile(struct fimc_is_interface *itf, @@ -107,18 +108,28 @@ int fimc_is_itf_process_off(struct fimc_is_interface *itf, unsigned int instance); int fimc_is_itf_set_param(struct fimc_is_interface *this, unsigned int instance, + unsigned int scenario, unsigned int lindex, unsigned int hindex); int fimc_is_itf_preview_still(struct fimc_is_interface *itf, unsigned int instance); +int fimc_is_itf_capture_still(struct fimc_is_interface *itf, + unsigned int instance); int fimc_is_itf_get_capability(struct fimc_is_interface *itf, - unsigned int instance, unsigned int address); -int fimc_is_itf_cfg_mem(struct fimc_is_interface *itf, + unsigned int instance, unsigned int address); +int fimc_is_itf_change_sensor_mode(struct fimc_is_interface *itf, + unsigned int instance, + unsigned int mode); +int fimc_is_itf_map_mem(struct fimc_is_interface *itf, unsigned int instance, unsigned int address, unsigned int size); +int fimc_is_itf_unmap_mem(struct fimc_is_interface *itf, + unsigned int instance); int fimc_is_itf_shot_nblk(struct fimc_is_interface *itf, unsigned int instance, unsigned int bayer, unsigned int shot, unsigned int fcount, unsigned int rcount); int fimc_is_itf_power_down(struct fimc_is_interface *itf, unsigned int instance); +int fimc_is_itf_hw_running(struct fimc_is_interface *itf); +void fimc_is_itf_notify_frame_done(struct fimc_is_interface *itf); #endif diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.c b/drivers/media/platform/exynos5-is/fimc-is-isp.c index 078f6d9cf57..82485d651bd 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-isp.c +++ b/drivers/media/platform/exynos5-is/fimc-is-isp.c @@ -1,5 +1,5 @@ /* - * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver + * Samsung EXYNOS5250/EXYNOS3 FIMC-IS (Imaging Subsystem) driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Arun Kumar K <arun.kk@samsung.com> @@ -132,7 +132,6 @@ static void isp_video_output_buffer_queue(struct vb2_buffer *vb) fimc_is_pipeline_buf_lock(isp->pipeline); fimc_is_isp_wait_queue_add(isp, buf); fimc_is_pipeline_buf_unlock(isp->pipeline); - /* Call shot command */ fimc_is_pipeline_shot_safe(isp->pipeline); } @@ -221,12 +220,8 @@ static int isp_try_fmt_mplane(struct file *file, void *fh, if (!fmt) fmt = (struct fimc_is_fmt *) &formats[0]; - v4l_bound_align_image(&pixm->width, - ISP_MIN_WIDTH + SENSOR_WIDTH_PADDING, - ISP_MAX_WIDTH + SENSOR_WIDTH_PADDING, 0, - &pixm->height, - ISP_MIN_HEIGHT + SENSOR_HEIGHT_PADDING, - ISP_MAX_HEIGHT + SENSOR_HEIGHT_PADDING, 0, + v4l_bound_align_image(&pixm->width, ISP_MIN_WIDTH, ISP_MAX_WIDTH, 0, + &pixm->height, ISP_MIN_HEIGHT, ISP_MAX_HEIGHT, 0, 0); plane_fmt->bytesperline = (pixm->width * fmt->depth[0]) / 8; @@ -458,14 +453,24 @@ static int isp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) return ret; - sensor->width = fmt.format.width - SENSOR_WIDTH_PADDING; - sensor->height = fmt.format.height - SENSOR_HEIGHT_PADDING; + sensor->width = fmt.format.width; + sensor->height = fmt.format.height; sensor->pixel_width = fmt.format.width; sensor->pixel_height = fmt.format.height; /* Check sensor resolution match */ - if ((sensor->pixel_width != isp->width) || - (sensor->pixel_height != isp->height)) { + if ((sensor->width >= sdata->pixel_width) || + (sensor->height >= sdata->pixel_height)) { + v4l2_err(sd, "The sensor does not support " + "requested resolution: %dx%d vs %dx%d\n", + sensor->width, sensor->height, + sdata->pixel_width, sdata->pixel_height); + return -EINVAL; + } + + /* ISP supports only bayer cropping and scaling down */ + if ((sensor->pixel_width < isp->width) || + (sensor->pixel_height < isp->height)) { v4l2_err(sd, "Resolution mismatch\n"); return -EPIPE; } diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.h b/drivers/media/platform/exynos5-is/fimc-is-isp.h index fdb6d86bd2a..d7156872f7c 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-isp.h +++ b/drivers/media/platform/exynos5-is/fimc-is-isp.h @@ -1,7 +1,7 @@ /* - * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * Samsung EXYNOS5/EXYNOS3 FIMC-IS (Imaging Subsystem) driver * - * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Copyright (C) 2012-2014 Samsung Electronics Co., Ltd. * Arun Kumar K <arun.kk@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -23,9 +23,8 @@ #define ISP_DEF_WIDTH 1296 #define ISP_DEF_HEIGHT 732 - -#define ISP_MAX_WIDTH 4808 -#define ISP_MAX_HEIGHT 3356 +#define ISP_MAX_WIDTH 4208 +#define ISP_MAX_HEIGHT 3120 #define ISP_MIN_WIDTH 32 #define ISP_MIN_HEIGHT 32 @@ -65,7 +64,6 @@ struct fimc_is_isp { struct media_pad subdev_pads[ISP_SD_PADS_NUM]; struct v4l2_ctrl_handler ctrl_handler; struct mutex video_lock; - struct v4l2_subdev *sensor_sd; struct fimc_is_pipeline *pipeline; diff --git a/drivers/media/platform/exynos5-is/fimc-is-metadata.h b/drivers/media/platform/exynos5-is/fimc-is-metadata.h index 02367c418d7..269f4e423cf 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-metadata.h +++ b/drivers/media/platform/exynos5-is/fimc-is-metadata.h @@ -18,9 +18,21 @@ struct rational { uint32_t den; }; +/** + * @brief Metadata type: basic/ extended + */ +#define CAMERA2_SHOT_BASE_MODE 1 +#define CAMERA2_SHOT_EXT_MODE 2 + #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 + + /* * Controls/dynamic metadata */ @@ -30,19 +42,41 @@ enum metadata_mode { METADATA_MODE_FULL }; -struct camera2_request_ctl { - uint32_t id; - enum metadata_mode metadatamode; - uint8_t outputstreams[16]; - uint32_t framecount; +struct camera2_request_ctl{ + struct camera2_request_ctl_base { + uint32_t id; + enum metadata_mode metadatamode; + uint8_t outputstreams[16]; + uint32_t framecount; + }base; + uint32_t requestCount; /* metadata v2 */ }; struct camera2_request_dm { - uint32_t id; - enum metadata_mode metadatamode; - uint32_t framecount; + struct camera2_request_dm_base { + uint32_t id; + enum metadata_mode metadatamode; + uint32_t framecount; + }base; + uint32_t requestcount; /* metadata v2 */ +}; + +/** + * struct camera2_entry_ctl per-frame control for entry control + * @lowIndexParam: parameters flag - low 32-bits + * @highIndexParam: parameters flag - high 32-bits + * @parameter set of parameters + */ +struct camera2_entry_ctl { + uint32_t lowIndexParam; + uint32_t highIndexParam; + uint32_t parameter[2048]; }; +struct camera2_entry_dm { + uint32_t lowIndexParam; + uint32_t highIndexParam; +}; enum optical_stabilization_mode { @@ -130,10 +164,14 @@ struct camera2_sensor_ctl { }; struct camera2_sensor_dm { - uint64_t exposure_time; - uint64_t frame_duration; - uint32_t sensitivity; - uint64_t timestamp; + struct camera2_sensor_dm_base { + uint64_t exposure_time; + uint64_t frame_duration; + uint32_t sensitivity; + uint64_t timestamp; + }base; + uint32_t analog_gain; /* metadata v2 */ + uint32_t digital_gain; /* metadata v2 */ }; struct camera2_sensor_sm { @@ -177,15 +215,22 @@ struct camera2_flash_ctl { }; struct camera2_flash_dm { - enum flash_mode flash_mode; - /* 10 is max power */ - uint32_t firing_power; - /* unit : microseconds */ - uint64_t firing_time; - /* 1 : stable, 0 : unstable */ - uint32_t firing_stable; - /* 1 : success, 0 : fail */ - uint32_t decision; + struct camera2_flash_dm_base { + enum flash_mode flash_mode; + /* 10 is max power */ + uint32_t firing_power; + /* unit : microseconds */ + uint64_t firing_time; + /* 1 : stable, 0 : unstable */ + uint32_t firing_stable; + /* 1 : success, 0 : fail */ + uint32_t decision; + }base; + /* 0: None, 1 : pre, 2 : main flash ready */ + uint32_t flashReady; /* metadata v2 */ + + /* 0: None, 1 : pre, 2 : main flash off ready */ + uint32_t flashOffReady; /* metadata v2 */ }; struct camera2_flash_sm { @@ -253,32 +298,52 @@ enum color_correction_mode { COLOR_CORRECTION_MODE_EFFECT_POSTERIZE, COLOR_CORRECTION_MODE_EFFECT_WHITEBOARD, COLOR_CORRECTION_MODE_EFFECT_BLACKBOARD, - COLOR_CORRECTION_MODE_EFFECT_AQUA -}; + COLOR_CORRECTION_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, + TOTAOCOUNT_COLORCORRECTION_MODE_EFFECT +}; struct camera2_color_correction_ctl { - enum color_correction_mode mode; - float transform[9]; - uint32_t hue; - uint32_t saturation; - uint32_t brightness; + struct camera2_color_correction_ctl_base { + enum color_correction_mode mode; + float transform[9]; + uint32_t hue; + uint32_t saturation; + uint32_t brightness; + }base; + uint32_t contrast; /* metadata v2 */ }; struct camera2_color_correction_dm { - enum color_correction_mode mode; - float transform[9]; - uint32_t hue; - uint32_t saturation; - uint32_t brightness; + struct camera2_color_correction_dm_base { + enum color_correction_mode mode; + float transform[9]; + uint32_t hue; + uint32_t saturation; + uint32_t brightness; + }base; + uint32_t contrast; /* metadata v2 */ }; struct camera2_color_correction_sm { - /* assuming 10 supported modes */ - uint8_t available_modes[CAMERA2_MAX_AVAILABLE_MODE]; - uint32_t hue_range[2]; - uint32_t saturation_range[2]; - uint32_t brightness_range[2]; + struct camera2_color_correction_sm_base { + /* assuming 10 supported modes */ + uint8_t available_modes[CAMERA2_MAX_AVAILABLE_MODE]; + uint32_t hue_range[2]; + uint32_t saturation_range[2]; + uint32_t brightness_range[2]; + }base; + uint32_t contrastRange[2]; /* metadata v2 */ }; enum tonemap_mode { @@ -326,11 +391,17 @@ enum scaler_formats { }; struct camera2_scaler_ctl { - uint32_t crop_region[3]; + struct camera2_scaler_ctl_base { + uint32_t crop_region[4]; + }base; + uint32_t orientation; /* metadata v2 */ }; struct camera2_scaler_dm { - uint32_t crop_region[3]; + struct camera2_scaler_dm_base { + uint32_t crop_region[4]; + }base; + uint32_t orientation; /* metadata v2 */ }; struct camera2_scaler_sm { @@ -383,22 +454,31 @@ enum stats_mode { STATS_MODE_ON }; +enum stats_lowlightmode { + STATE_LLS_REQUIRED = 1 +}; + struct camera2_stats_ctl { enum face_detect_mode face_detect_mode; enum stats_mode histogram_mode; enum stats_mode sharpness_map_mode; }; - struct camera2_stats_dm { - enum face_detect_mode face_detect_mode; - uint32_t face_rectangles[CAMERA2_MAX_FACES][4]; - uint8_t face_scores[CAMERA2_MAX_FACES]; - uint32_t face_landmarks[CAMERA2_MAX_FACES][6]; - uint32_t face_ids[CAMERA2_MAX_FACES]; - enum stats_mode histogram_mode; - uint32_t histogram[3 * 256]; - enum stats_mode sharpness_map_mode; + struct camera2_stats_dm_base { + enum face_detect_mode face_detect_mode; + uint32_t face_rectangles[CAMERA2_MAX_FACES][4]; + uint8_t face_scores[CAMERA2_MAX_FACES]; + uint32_t face_landmarks[CAMERA2_MAX_FACES][6]; + uint32_t face_ids[CAMERA2_MAX_FACES]; + enum stats_mode histogram_mode; + uint32_t histogram[3 * 256]; + enum stats_mode sharpness_map_mode; + }base; + /* sharpnessMap */ + enum stats_lowlightmode LowLightMode; /* metadata v2 */ + uint32_t lls_tuning_set_index; /* metadata v2 */ + uint32_t lls_brightness_index; /* metadata v2 */ }; @@ -445,7 +525,25 @@ enum aa_scene_mode { AA_SCENE_MODE_PARTY, AA_SCENE_MODE_CANDLELIGHT, AA_SCENE_MODE_BARCODE, - AA_SCENE_MODE_NIGHT_CAPTURE + 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 { @@ -487,7 +585,9 @@ 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, + AA_AE_ANTIBANDING_AUTO_50HZ, /* 50Hz + Auto NEW */ + AA_AE_ANTIBANDING_AUTO_60HZ /* 60Hz + Auto NEW */ }; enum aa_awbmode { @@ -588,23 +688,23 @@ struct camera2_aa_dm { }; struct camera2_aa_sm { - uint8_t available_scene_modes[CAMERA2_MAX_AVAILABLE_MODE]; - uint8_t available_effects[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t available_scene_modes[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t available_effects[CAMERA2_MAX_AVAILABLE_MODE]; /* Assuming # of available scene modes = 10 */ - uint32_t max_regions; - uint8_t ae_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; + uint32_t max_regions; + uint8_t ae_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; /* Assuming # of available ae modes = 8 */ struct rational ae_compensation_step; - int32_t ae_compensation_range[2]; - uint32_t ae_available_target_fps_ranges[CAMERA2_MAX_AVAILABLE_MODE][2]; - uint8_t ae_available_antibanding_modes[CAMERA2_MAX_AVAILABLE_MODE]; - uint8_t awb_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; + int32_t ae_compensation_range[2]; + uint32_t ae_available_target_fps_ranges[CAMERA2_MAX_AVAILABLE_MODE][2]; + uint8_t ae_available_antibanding_modes[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t awb_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; /* Assuming # of awbAvailableModes = 10 */ - uint8_t af_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; + uint8_t af_available_modes[CAMERA2_MAX_AVAILABLE_MODE]; /* Assuming # of afAvailableModes = 4 */ - uint8_t available_video_stabilization_modes[4]; + uint8_t available_video_stabilization_modes[4]; /* Assuming # of availableVideoStabilizationModes = 4 */ - uint32_t iso_range[2]; + uint32_t iso_range[2]; }; struct camera2_lens_usm { @@ -626,6 +726,25 @@ struct camera2_flash_usm { uint64_t firing_time_frame_delay; }; +struct camera2_ctl_base { + struct camera2_request_ctl_base request; + struct camera2_lens_ctl lens; + struct camera2_sensor_ctl sensor; + struct camera2_flash_ctl flash; + struct camera2_hotpixel_ctl hotpixel; + struct camera2_demosaic_ctl demosaic; + struct camera2_noise_reduction_ctl noise; + struct camera2_shading_ctl shading; + struct camera2_geometric_ctl geometric; + struct camera2_color_correction_ctl_base color; + struct camera2_tonemap_ctl tonemap; + struct camera2_edge_ctl edge; + struct camera2_scaler_ctl_base scaler; + struct camera2_jpeg_ctl jpeg; + struct camera2_stats_ctl stats; + struct camera2_aa_ctl aa; +}; + struct camera2_ctl { struct camera2_request_ctl request; struct camera2_lens_ctl lens; @@ -643,6 +762,26 @@ struct camera2_ctl { struct camera2_jpeg_ctl jpeg; struct camera2_stats_ctl stats; struct camera2_aa_ctl aa; + struct camera2_entry_ctl entry; /* metadata v2 */ +}; + +struct camera2_dm_base { + struct camera2_request_dm_base request; + struct camera2_lens_dm lens; + struct camera2_sensor_dm_base sensor; + struct camera2_flash_dm_base flash; + struct camera2_hotpixel_dm hotpixel; + struct camera2_demosaic_dm demosaic; + struct camera2_noise_reduction_dm noise; + struct camera2_shading_dm shading; + struct camera2_geometric_dm geometric; + struct camera2_color_correction_dm_base color; + struct camera2_tonemap_dm tonemap; + struct camera2_edge_dm edge; + struct camera2_scaler_dm_base scaler; + struct camera2_jpeg_dm jpeg; + struct camera2_stats_dm_base stats; + struct camera2_aa_dm aa; }; struct camera2_dm { @@ -662,6 +801,24 @@ struct camera2_dm { struct camera2_jpeg_dm jpeg; struct camera2_stats_dm stats; struct camera2_aa_dm aa; + struct camera2_entry_dm entry; /* metadata v2 */ +}; + +struct camera2_sm_base { + struct camera2_lens_sm lens; + struct camera2_sensor_sm sensor; + struct camera2_flash_sm flash; + struct camera2_color_correction_sm_base color; + struct camera2_tonemap_sm tonemap; + struct camera2_scaler_sm scaler; + struct camera2_jpeg_sm jpeg; + struct camera2_stats_sm stats; + struct camera2_aa_sm aa; + + /* User-defined(ispfw specific) static metadata. */ + struct camera2_lens_usm lensud; + struct camera2_sensor_usm sensor_ud; + struct camera2_flash_usm flash_ud; }; struct camera2_sm { @@ -684,50 +841,215 @@ struct camera2_sm { /* * User-defined control for lens. */ -struct camera2_lens_uctl { +struct camera2_lens_uctl_base { + struct camera2_lens_ctl ctl; + /* It depends on the af algorithm(normally 255 or 1023) or normally 8, 9 or 10 */ + uint32_t max_pos; + /* It depends on the af algorithm. + Some actuator support slew rate control. */ + uint32_t slew_rate; +}; - /* It depends by af algorithm(normally 255 or 1023) */ - uint32_t max_pos; - /* Some actuator support slew rate control. */ - uint32_t slew_rate; +struct camera2_lens_uctl { + struct camera2_lens_ctl ctl; + /* It depends on max_pos */ + uint32_t pos; /* metadata v2 */ + /* It depends on the af algorithm(normally 255 or 1023) or normally 8, 9 or 10 */ + uint32_t max_pos; + /* It depends on the af algorithm */ + uint32_t direction; /* metadata v2 */ + /* Some actuators support slew rate control. */ + uint32_t slew_rate; }; /* * User-defined metadata for lens. */ struct camera2_lens_udm { - /* It depends by af algorithm(normally 255 or 1023) */ - uint32_t max_pos; - /* Some actuator support slew rate control. */ - uint32_t slew_rate; + /* 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 actuators support slew rate control. */ + uint32_t slewRate; +}; + +/** + * User-defined metadata for ae + */ +struct camera2_ae_udm { + /* Vendor specific length */ + uint32_t vs_length; + /* vendor specific data array */ + uint32_t vs_data[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** + * User-defined metadata for AWB + */ +struct camera2_awb_udm { + uint32_t vs_length; + uint32_t vd_data[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** + * User-defined metadata for AF + */ +struct camera2_af_udm { + uint32_t vs_length; + uint32_t vs_data[CAMERA2_MAX_VENDER_LENGTH]; + uint32_t lens_pos_infinity; + uint32_t lens_pos_macro; + uint32_t lens_pos_current; +}; + +/** + * User-defined metadata for AS (anti-shading) + */ +struct camera2_as_udm { + uint32_t vs_length; + uint32_t vs_data[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** + * User-defined metadata for ipc + */ +struct camera2_ipc_udm { + uint32_t vs_length; + uint32_t vs_data[CAMERA2_MAX_VENDER_LENGTH]; +}; + +/** + * + */ +struct camera2_internal_udm { + uint32_t vs_data1[CAMERA2_MAX_VENDER_LENGTH]; + uint32_t vs_data2[CAMERA2_MAX_VENDER_LENGTH]; +}; + +struct camera2_bayer_udm { + uint32_t width; + uint32_t height; +}; + + +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, +}; + +struct camera2_pdaf_single_result { + uint16_t mode; + uint16_t goal_pos; + uint16_t reliability; + uint16_t current_pos; +}; + +struct camera2_pdaf_multi_result { + uint16_t mode; + uint16_t goal_pos; + uint16_t reliability; +}; + +struct camera2_pdaf_udm { + uint16_t num_column; + uint16_t num_row; + struct camera2_pdaf_multi_result multi_result[CAMERA2_MAX_PDAF_MULTIROI_COLUMN][CAMERA2_MAX_PDAF_MULTIROI_ROW]; + struct camera2_pdaf_single_result single_result; + uint16_t lens_pos_resolution; +}; + +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; }; /* * User-defined control for sensor. */ struct camera2_sensor_uctl { - struct camera2_sensor_ctl ctl; - /* Dynamic frame duration. - * This feature is decided to max. value between - * 'sensor.exposureTime'+alpha and 'sensor.frameDuration'. - */ - uint64_t dynamic_frame_duration; + struct camera2_sensor_uctl_base { + struct camera2_sensor_ctl ctl; + /* Dynamic frame duration. + * This feature is set to max. value between + * 'sensor.exposureTime'+ alpha and 'sensor.frameDuration'. + */ + uint64_t dynamic_frame_duration; + }base; + uint32_t analogGain; /* metadata v2 */ + uint32_t digitalGain; /* metadata v2 */ + uint64_t longExposureTime; /* For supporting WDR */ /* metadata v2 */ + uint64_t shortExposureTime; /* metadata v2 */ + uint32_t longAnalogGain; /* metadata v2 */ + uint32_t shortAnalogGain; /* metadata v2 */ + uint32_t longDigitalGain; /* metadata v2 */ + uint32_t shortDigitalGain; /* metadata v2 */ }; struct camera2_scaler_uctl { - /* Target address for next frame. - * [0] invalid address, stop - * [others] valid address - */ - uint32_t scc_target_address[4]; - uint32_t scp_target_address[4]; + struct camera2_scaler_uctl_base { + /* Next frame target address, where '0' denotes invalid one. */ + uint32_t scc_target_address[4]; + uint32_t scp_target_address[4]; + }base; + uint32_t dis_target_address[4]; /* metadata v2 */ + uint32_t taap_target_address[4]; /* 3AA preview DMA */ /* metadata v2 */ + uint32_t taac_target_address[4]; /* 3AA capture DMA */ /* metadata v2 */ + uint32_t orientation; /* metadata v2 */ }; struct camera2_flash_uctl { struct camera2_flash_ctl ctl; }; +struct camera2_bayer_uctl { + struct camera2_scaler_ctl ctl; +}; + +struct camera2_companion_uctl { + enum companion_drc_mode drc_mode; + enum companion_wdr_mode wdr_mode; + enum companion_paf_mode paf_mode; +}; + +struct camera2_uctl_base { + /* Set sensor, lens, flash control for next frame. + * This flag can be combined. + * [0 bit] lens + * [1 bit] sensor + * [2 bit] flash + */ + uint32_t u_update_bitmap; + + /* For debugging */ + uint32_t u_frame_number; + + /* isp fw specific control (user-defined) of lens. */ + struct camera2_lens_uctl_base lens_ud; + /* isp fw specific control (user-defined) of sensor. */ + struct camera2_sensor_uctl_base sensor_ud; + /* isp fw specific control (user-defined) of flash. */ + struct camera2_flash_uctl flash_ud; + + struct camera2_scaler_uctl_base scaler_ud; + +}; + struct camera2_uctl { /* Set sensor, lens, flash control for next frame. * This flag can be combined. @@ -748,10 +1070,33 @@ struct camera2_uctl { struct camera2_flash_uctl flash_ud; struct camera2_scaler_uctl scaler_ud; + + struct camera2_bayer_uctl bayer_ud; + + struct camera2_companion_uctl companion_ud; }; 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; + struct camera2_internal_udm internal; + struct camera2_bayer_udm bayer; + struct camera2_companion_udm companion; +}; + +struct camera2_shot_base { + /* standard area */ + struct camera2_ctl_base ctl; + struct camera2_dm_base dm; + /* user defined area */ + struct camera2_uctl_base uctl; + struct camera2_udm udm; + /* magic : 23456789 */ + uint32_t magicnumber; }; struct camera2_shot { @@ -764,4 +1109,6 @@ struct camera2_shot { /* magic : 23456789 */ uint32_t magicnumber; }; + + #endif diff --git a/drivers/media/platform/exynos5-is/fimc-is-param.h b/drivers/media/platform/exynos5-is/fimc-is-param.h index 015cc13130a..3aaea6caec1 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-param.h +++ b/drivers/media/platform/exynos5-is/fimc-is-param.h @@ -1,5 +1,5 @@ /* - * Samsung Exynos5 SoC series FIMC-IS driver + * Samsung Exynos5/Exynos3 SoC series FIMC-IS driver * * Copyright (c) 2013 Samsung Electronics Co., Ltd * Kil-yeon Lim <kilyeon.im@samsung.com> @@ -18,54 +18,66 @@ #define PARAMETER_MAX_SIZE 128 /* in bytes */ #define PARAMETER_MAX_MEMBER (PARAMETER_MAX_SIZE / 4) +/* Tagging extended parameter fields */ +#define __param_extended +#define __param_unused + enum is_param_set_bit { PARAM_GLOBAL_SHOTMODE = 0, - PARAM_SENSOR_CONTROL, + PARAM_SENSOR_CONTROL, PARAM_SENSOR_OTF_INPUT, PARAM_SENSOR_OTF_OUTPUT, PARAM_SENSOR_FRAME_RATE, PARAM_SENSOR_DMA_OUTPUT, - PARAM_BUFFER_CONTROL, + PARAM_BUFFER_CONTROL, PARAM_BUFFER_OTF_INPUT, PARAM_BUFFER_OTF_OUTPUT, - PARAM_ISP_CONTROL, - PARAM_ISP_OTF_INPUT = 10, + PARAM_3AA_CONTROL, + PARAM_3AA_OTF_INPUT, + PARAM_3AA_VDMA1_INPUT, + PARAM_3AA_DDMA_INPUT, + PARAM_3AA_OTF_OUTPUT, + PARAM_ISP_AA = PARAM_3AA_OTF_OUTPUT, + PARAM_3AA_VDMA4_OUTPUT, + PARAM_ISP_FLASH = PARAM_3AA_VDMA4_OUTPUT, + PARAM_3AA_VDMA2_OUTPUT, + PARAM_ISP_AWB = PARAM_3AA_VDMA2_OUTPUT, + PARAM_3AA_DDMA_OUTPUT, + PARAM_ISP_IMAGE_EFFECT = PARAM_3AA_DDMA_OUTPUT, + PARAM_ISP_CONTROL, + PARAM_ISP_ISO = PARAM_ISP_CONTROL, + PARAM_ISP_OTF_INPUT, + PARAM_ISP_ADJUST = PARAM_ISP_OTF_INPUT, PARAM_ISP_DMA1_INPUT, + PARAM_ISP_METERING = PARAM_ISP_DMA1_INPUT, PARAM_ISP_DMA2_INPUT, - PARAM_ISP_AA, - PARAM_ISP_FLASH, - PARAM_ISP_AWB, - PARAM_ISP_IMAGE_EFFECT, - PARAM_ISP_ISO, - PARAM_ISP_ADJUST, - PARAM_ISP_METERING, - PARAM_ISP_AFC = 20, + PARAM_ISP_AFC = PARAM_ISP_DMA2_INPUT, PARAM_ISP_OTF_OUTPUT, PARAM_ISP_DMA1_OUTPUT, PARAM_ISP_DMA2_OUTPUT, - PARAM_DRC_CONTROL, + PARAM_DRC_CONTROL, PARAM_DRC_OTF_INPUT, PARAM_DRC_DMA_INPUT, PARAM_DRC_OTF_OUTPUT, - PARAM_SCALERC_CONTROL, + PARAM_SCALERC_CONTROL, PARAM_SCALERC_OTF_INPUT, PARAM_SCALERC_IMAGE_EFFECT = 30, PARAM_SCALERC_INPUT_CROP, PARAM_SCALERC_OUTPUT_CROP, PARAM_SCALERC_OTF_OUTPUT, PARAM_SCALERC_DMA_OUTPUT = 34, - PARAM_ODC_CONTROL, + PARAM_ODC_CONTROL, PARAM_ODC_OTF_INPUT, PARAM_ODC_OTF_OUTPUT, - PARAM_DIS_CONTROL, + PARAM_DIS_CONTROL, PARAM_DIS_OTF_INPUT, PARAM_DIS_OTF_OUTPUT = 40, - PARAM_TDNR_CONTROL, + PARAM_TDNR_CONTROL, PARAM_TDNR_OTF_INPUT, PARAM_TDNR_1ST_FRAME, PARAM_TDNR_OTF_OUTPUT, PARAM_TDNR_DMA_OUTPUT, - PARAM_SCALERP_CONTROL, + PARAM_SCALERP_CONTROL, PARAM_SCALERP_OTF_INPUT, PARAM_SCALERP_IMAGE_EFFECT, PARAM_SCALERP_INPUT_CROP, @@ -74,7 +86,7 @@ enum is_param_set_bit { PARAM_SCALERP_FLIP, PARAM_SCALERP_OTF_OUTPUT, PARAM_SCALERP_DMA_OUTPUT, - PARAM_FD_CONTROL, + PARAM_FD_CONTROL, PARAM_FD_OTF_INPUT, PARAM_FD_DMA_INPUT, PARAM_FD_CONFIG = 58, @@ -182,7 +194,7 @@ enum otf_output_command { OTF_OUTPUT_COMMAND_ENABLE = 1 }; -enum orf_output_format { +enum otf_output_format { OTF_OUTPUT_FORMAT_YUV444 = 1, OTF_OUTPUT_FORMAT_YUV422 = 2, OTF_OUTPUT_FORMAT_YUV420 = 3, @@ -214,11 +226,12 @@ enum dma_output_command { }; 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 = 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 { @@ -465,8 +478,8 @@ enum isp_afc_error { enum isp_scene_command { ISP_SCENE_NONE = 0, ISP_SCENE_PORTRAIT = 1, - ISP_SCENE_LANDSCAPE = 2, - ISP_SCENE_SPORTS = 3, + ISP_SCENE_LANDSCAPE = 2, + ISP_SCENE_SPORTS = 3, ISP_SCENE_PARTYINDOOR = 4, ISP_SCENE_BEACHSNOW = 5, ISP_SCENE_SUNSET = 6, @@ -534,6 +547,12 @@ enum scaler_flip_error { SCALER_FLIP_ERROR_NONE = 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 +}; + /* -------------------------- 3DNR ----------------------------------- */ enum tdnr_1st_frame_command { TDNR_1ST_FRAME_COMMAND_NOPROCESSING = 0, @@ -616,13 +635,7 @@ struct param_otf_input { u32 format; u32 bitwidth; u32 order; - u32 crop_offset_x; - u32 crop_offset_y; - u32 crop_width; - u32 crop_height; - u32 frametime_min; - u32 frametime_max; - u32 reserved[PARAMETER_MAX_MEMBER - 13]; + u32 reserved[PARAMETER_MAX_MEMBER - 7]; u32 err; }; @@ -636,20 +649,7 @@ struct param_dma_input { u32 order; u32 buffer_number; u32 buffer_address; - u32 bayer_crop_offset_x; - u32 bayer_crop_offset_y; - u32 bayer_crop_width; - u32 bayer_crop_height; - u32 dma_crop_offset_x; - u32 dma_crop_offset_y; - u32 dma_crop_width; - u32 dma_crop_height; - u32 user_min_frametime; - u32 user_max_frametime; - u32 wide_frame_gap; - u32 frame_gap; - u32 line_gap; - u32 reserved[PARAMETER_MAX_MEMBER - 23]; + u32 reserved[PARAMETER_MAX_MEMBER - 10]; u32 err; }; @@ -796,7 +796,10 @@ struct param_isp_afc { struct param_scaler_imageeffect { u32 cmd; - u32 reserved[PARAMETER_MAX_MEMBER - 2]; + u32 arbitrary_cb; + u32 arbitrary_cr; + u32 yuv_range; + u32 reserved[PARAMETER_MAX_MEMBER - 5]; u32 err; }; @@ -876,22 +879,28 @@ struct buffer_param { struct param_otf_output otf_output; }; + +struct taa_param { + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_dma_input __param_unused __dma_input; + struct param_otf_output __param_unused __otf_output; + struct param_dma_output dma_output; /* Before BDS */ + struct param_dma_output dma_bds_output; /* After BDS */ + struct param_dma_output __param_unused __dma_output; + +}; + struct isp_param { - struct param_control control; - struct param_otf_input otf_input; - struct param_dma_input dma1_input; - struct param_dma_input dma2_input; - struct param_isp_aa aa; - struct param_isp_flash flash; - struct param_isp_awb awb; - struct param_isp_imageeffect effect; - struct param_isp_iso iso; - struct param_isp_adjust adjust; - struct param_isp_metering metering; - struct param_isp_afc afc; - struct param_otf_output otf_output; - struct param_dma_output dma1_output; - struct param_dma_output dma2_output; + struct param_control control; + struct param_otf_input otf_input; + struct param_dma_input dma_input; + struct param_dma_input __param_unused __dma_input; + struct param_otf_output __param_unused otf_output; + struct param_dma_output __param_unused __dma_output; + struct param_dma_output dma_output; /* VDMA5_OUTPUT */ + }; struct drc_param { @@ -906,7 +915,7 @@ struct scalerc_param { 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_output_crop output_crop; struct param_otf_output otf_output; struct param_dma_output dma_output; }; @@ -954,6 +963,7 @@ 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; diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c index 25eaf2433f5..bc87a59ab03 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c +++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c @@ -15,8 +15,10 @@ #include "fimc-is-metadata.h" #include "fimc-is-regs.h" #include "fimc-is-cmd.h" +#include "fimc-is-fw.h" #include <media/videobuf2-dma-contig.h> #include <linux/delay.h> +#include <asm/firmware.h> /* Default setting values */ #define DEFAULT_PREVIEW_STILL_WIDTH 1280 @@ -30,6 +32,22 @@ #define DEFAULT_PREVIEW_VIDEO_WIDTH 640 #define DEFAULT_PREVIEW_VIDEO_HEIGHT 480 +#define FIMC_IS_SENSOR_ID 0x00 +#define FIMC_IS_SENSOR_I2C_CHAN 0x02 +#define FIMC_IS_SENSOR_I2C_ADDR 0x03 +#define FIMC_IS_SESNOR_I2C_SPEED 0x04 +#define FIMC_IS_SENSOR_MIPI_LANES 0x3f +#define FIMC_IS_SENSOR_I2C_SCLK 0x44 + +#define FIMC_IS_PARAMS_MASK_ALL 0xffffffffUL +#define FIMC_IS_PARAMS_ALL 64 + +#define FIMC_IS_SHOT_SIZE 0x8000 + +#define FIMC_SCC_MAX_RESIZE_RATIO 4 +#define FIMC_SCC_MIN_RESIZE_RATIO 16 + + /* Init params for pipeline devices */ static const struct sensor_param init_sensor_param = { .frame_rate = { @@ -37,8 +55,8 @@ static const struct sensor_param init_sensor_param = { }, }; -static const struct isp_param init_isp_param = { - .control = { +static const struct taa_param init_taa_param = { + .control= { .cmd = CONTROL_COMMAND_START, .bypass = CONTROL_BYPASS_DISABLE, }, @@ -49,40 +67,30 @@ static const struct isp_param init_isp_param = { .format = OTF_INPUT_FORMAT_BAYER, .bitwidth = OTF_INPUT_BIT_WIDTH_10BIT, .order = OTF_INPUT_ORDER_BAYER_GR_BG, - .frametime_max = 33333, }, - .dma1_input = { + .dma_input = { .cmd = DMA_INPUT_COMMAND_DISABLE, }, - .dma2_input = { - .cmd = DMA_INPUT_COMMAND_DISABLE, - }, - .aa = { - .cmd = ISP_AA_COMMAND_START, - }, - .flash = { - .cmd = ISP_FLASH_COMMAND_DISABLE, - .redeye = ISP_FLASH_REDEYE_DISABLE, - }, - .awb = { - .cmd = ISP_AWB_COMMAND_AUTO, + .dma_output = { + .cmd = DMA_OUTPUT_COMMAND_DISABLE, }, - .effect = { - .cmd = ISP_IMAGE_EFFECT_DISABLE, - }, - .iso = { - .cmd = ISP_ISO_COMMAND_AUTO, - }, - .adjust = { - .cmd = ISP_ADJUST_COMMAND_AUTO, +}; + +static const struct isp_param init_isp_param = { + .control = { + .cmd = CONTROL_COMMAND_START, + .bypass = CONTROL_BYPASS_DISABLE, }, - .metering = { - .cmd = ISP_METERING_COMMAND_CENTER, - .win_width = DEFAULT_CAPTURE_STILL_WIDTH, - .win_height = DEFAULT_CAPTURE_STILL_HEIGHT, + .otf_input = { + .cmd = OTF_INPUT_COMMAND_DISABLE, + .width = DEFAULT_CAPTURE_STILL_WIDTH, + .height = DEFAULT_CAPTURE_STILL_HEIGHT, + .format = OTF_INPUT_FORMAT_BAYER, + .bitwidth = OTF_INPUT_BIT_WIDTH_10BIT, + .order = OTF_INPUT_ORDER_BAYER_GR_BG, }, - .afc = { - .cmd = ISP_AFC_COMMAND_AUTO, + .dma_input = { + .cmd = DMA_INPUT_COMMAND_DISABLE, }, .otf_output = { .cmd = OTF_OUTPUT_COMMAND_ENABLE, @@ -92,7 +100,7 @@ static const struct isp_param init_isp_param = { .bitwidth = OTF_OUTPUT_BIT_WIDTH_12BIT, .order = OTF_OUTPUT_ORDER_BAYER_GR_BG, }, - .dma1_output = { + .__dma_output = { .cmd = DMA_OUTPUT_COMMAND_DISABLE, .width = DEFAULT_CAPTURE_STILL_WIDTH, .height = DEFAULT_CAPTURE_STILL_HEIGHT, @@ -101,7 +109,7 @@ static const struct isp_param init_isp_param = { .plane = 1, .order = DMA_INPUT_ORDER_YCBCR, }, - .dma2_output = { + .dma_output = { .cmd = DMA_OUTPUT_COMMAND_DISABLE, .width = DEFAULT_CAPTURE_STILL_WIDTH, .height = DEFAULT_CAPTURE_STILL_HEIGHT, @@ -140,7 +148,7 @@ static const struct drc_param init_drc_param = { .width = DEFAULT_CAPTURE_STILL_WIDTH, .height = DEFAULT_CAPTURE_STILL_HEIGHT, .format = OTF_OUTPUT_FORMAT_YUV444, - .bitwidth = OTF_INPUT_BIT_WIDTH_8BIT, + .bitwidth = OTF_INPUT_BIT_WIDTH_12BIT, .order = OTF_OUTPUT_ORDER_BAYER_GR_BG, }, }; @@ -239,6 +247,7 @@ static const struct dis_param init_dis_param = { .order = OTF_OUTPUT_ORDER_BAYER_GR_BG, }, }; + static const struct tdnr_param init_tdnr_param = { .control = { .cmd = CONTROL_COMMAND_START, @@ -357,6 +366,13 @@ static const struct fd_param init_fd_param = { }, }; +struct workqueue_struct *fimc_is_pipeline_workqueue; + +#define NSEC_PER_MSEC 1000000L +#define FIMC_IS_MIN_FRAME_DURATION (5 * (NSEC_PER_MSEC)) + +#define __fimc_is_has_subip(p, subip) ((p)->subip_mask & (1 << (subip))) + static int fimc_is_pipeline_create_subdevs(struct fimc_is_pipeline *pipeline) { struct fimc_is *is = pipeline->is; @@ -374,9 +390,10 @@ static int fimc_is_pipeline_create_subdevs(struct fimc_is_pipeline *pipeline) if (ret) return ret; - /* SCP scaler */ - ret = fimc_is_scaler_subdev_create(&pipeline->scaler[SCALER_SCP], - SCALER_SCP, is->alloc_ctx, pipeline); + /* SCP scaler - if supported */ + if (pipeline->subip_state[IS_SCP] != COMP_INVALID) + ret = fimc_is_scaler_subdev_create(&pipeline->scaler[SCALER_SCP], + SCALER_SCP, is->alloc_ctx, pipeline); if (ret) return ret; @@ -387,13 +404,43 @@ static int fimc_is_pipeline_unregister_subdevs(struct fimc_is_pipeline *p) { fimc_is_isp_subdev_destroy(&p->isp); fimc_is_scaler_subdev_destroy(&p->scaler[SCALER_SCC]); - fimc_is_scaler_subdev_destroy(&p->scaler[SCALER_SCP]); + if (p->subip_state[IS_SCP] != COMP_INVALID) + fimc_is_scaler_subdev_destroy(&p->scaler[SCALER_SCP]); return 0; } +void fimc_is_pipeline_notify(struct fimc_is_event_listener *listener, + unsigned int event_id, + void *arg) +{ + struct fimc_is_pipeline *pipeline = (struct fimc_is_pipeline*)listener->private_data; + struct fimc_is *is = pipeline->is; + if (EXYNOS_FIMC_IS_FRAME_DONE == event_id) { + ktime_t now = ktime_get(); + long long delta = (pipeline->frame_duration) + ? ktime_to_ns(ktime_sub(now, pipeline->last_frame)) + : FIMC_IS_MIN_FRAME_DURATION; + pipeline->frame_duration = max(pipeline->frame_duration, delta); + ++pipeline->fcount; + fimc_is_itf_notify_frame_done(&is->interface); + pipeline->last_frame = now; + } +} + + +static void fimc_is_pipeline_work_fn(struct work_struct*); +static void fimc_is_pipeline_wdg_work_fn(struct work_struct*); + +static int fimc_is_pipelien_init_workqueue(void) +{ + fimc_is_pipeline_workqueue = alloc_workqueue("fimc-is", WQ_FREEZABLE,0); + return fimc_is_pipeline_workqueue ? 0 : -ENOMEM; +} + int fimc_is_pipeline_init(struct fimc_is_pipeline *pipeline, - unsigned int instance, void *is_ctx) + unsigned int instance, + void *is_ctx) { struct fimc_is *is = is_ctx; unsigned int i; @@ -408,21 +455,35 @@ int fimc_is_pipeline_init(struct fimc_is_pipeline *pipeline, pipeline->dev = &is->pdev->dev; pipeline->minfo = &is->minfo; pipeline->state = 0; + pipeline->subip_mask = is->drvdata->subip_mask; pipeline->force_down = false; for (i = 0; i < FIMC_IS_NUM_COMPS; i++) - pipeline->comp_state[i] = 0; + pipeline->subip_state[i] = __fimc_is_has_subip(pipeline, i); spin_lock_init(&pipeline->slock_buf); init_waitqueue_head(&pipeline->wait_q); mutex_init(&pipeline->pipe_lock); mutex_init(&pipeline->pipe_scl_lock); + ret = fimc_is_pipelien_init_workqueue(); + if (ret) { + dev_err(pipeline->dev, "Failed to initialize workqueue\n"); + return -EINVAL; + } + ret = fimc_is_pipeline_create_subdevs(pipeline); if (ret) { dev_err(pipeline->dev, "Subdev creation failed\n"); return -EINVAL; } + pipeline->listener.private_data = pipeline; + pipeline->listener.event_handler = fimc_is_pipeline_notify; + INIT_LIST_HEAD(&pipeline->listener.link); + + INIT_WORK(&pipeline->work, fimc_is_pipeline_work_fn); + INIT_DEFERRABLE_WORK(&pipeline->wdg_work, fimc_is_pipeline_wdg_work_fn); + set_bit(PIPELINE_INIT, &pipeline->state); return 0; } @@ -440,15 +501,20 @@ int fimc_is_pipeline_destroy(struct fimc_is_pipeline *pipeline) static int fimc_is_pipeline_initmem(struct fimc_is_pipeline *pipeline) { struct fimc_is_meminfo *minfo = pipeline->minfo; + struct fimc_is_fw_mem fw_minfo; unsigned int offset; + if (FIMC_IS_FW_CALL_OP(mem_config, &fw_minfo)){ + dev_err(pipeline->dev, + "Failed to get firmware memory layout\n"); + return -EINVAL; + } /* Allocate fw memory */ - minfo->fw.vaddr = dma_alloc_coherent(pipeline->dev, - FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE, + minfo->fw.vaddr = dma_zalloc_coherent(pipeline->dev, fw_minfo.fw_size, &minfo->fw.paddr, GFP_KERNEL); if (minfo->fw.vaddr == NULL) return -ENOMEM; - minfo->fw.size = FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE; + minfo->fw.size = fw_minfo.fw_size; /* FW memory should be 64MB aligned */ if ((u32)minfo->fw.paddr & FIMC_IS_FW_BASE_MASK) { @@ -460,7 +526,7 @@ static int fimc_is_pipeline_initmem(struct fimc_is_pipeline *pipeline) } /* Assigning memory regions */ - offset = FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE; + offset = fw_minfo.region_offset; minfo->region.paddr = minfo->fw.paddr + offset; minfo->region.vaddr = minfo->fw.vaddr + offset; pipeline->is_region = (struct is_region *)minfo->region.vaddr; @@ -473,8 +539,8 @@ static int fimc_is_pipeline_initmem(struct fimc_is_pipeline *pipeline) minfo->fw.vaddr); /* Allocate shot buffer */ - minfo->shot.vaddr = dma_alloc_coherent(pipeline->dev, - sizeof(struct camera2_shot), + minfo->shot.vaddr = dma_zalloc_coherent(pipeline->dev, + fw_minfo.shot_size, &minfo->shot.paddr, GFP_KERNEL); if (minfo->shot.vaddr == NULL) { dma_free_coherent(pipeline->dev, minfo->fw.size, @@ -482,8 +548,8 @@ static int fimc_is_pipeline_initmem(struct fimc_is_pipeline *pipeline) minfo->fw.paddr); return -ENOMEM; } - minfo->shot.size = sizeof(struct camera2_shot); - + minfo->shot.size = fw_minfo.shot_size; + pipeline->config.metadata_mode = fw_minfo.meta_type; return 0; } @@ -511,7 +577,7 @@ static int fimc_is_pipeline_load_firmware(struct fimc_is_pipeline *pipeline) dev_err(pipeline->dev, "Firmware file not found\n"); return -EINVAL; } - if (fw_blob->size > FIMC_IS_A5_MEM_SIZE + FIMC_IS_A5_SEN_SIZE) { + if (fw_blob->size > pipeline->minfo->fw.size) { dev_err(pipeline->dev, "Firmware file too big\n"); release_firmware(fw_blob); return -ENOMEM; @@ -554,6 +620,41 @@ static int fimc_is_pipeline_wait_timeout(struct fimc_is_pipeline *pipeline, return 0; } +static inline void fimc_is_pipeline_reset_isp(struct fimc_is_pipeline *pipeline) +{ + struct fimc_is *is = pipeline->is; + + if (is->drvdata->variant == FIMC_IS_EXYNOS3250) + pmu_is_write(0x0, is, EXYNOS3250_PMUREG_CMU_RESET_ISP); + else if (is->drvdata->variant == FIMC_IS_EXYNOS5) + pmu_is_write(0x0, is, EXYNOS5_PMUREG_CMU_RESET_ISP); +} + +static inline int fimc_is_pipeline_setup_gic(void) +{ + int i; + unsigned int reg_val; + int ret = 0; + + call_firmware_op(writesfr, (PA_FIMC_IS_GIC_C + 0x4),0x000000ff); + for (i = 0; i < 3; i++) + call_firmware_op(writesfr, + (PA_FIMC_IS_GIC_D + 0x80 + (i * 4)), + 0xffffffff); + for (i = 0; i < 18; i++) + call_firmware_op(writesfr, + (PA_FIMC_IS_GIC_D + 0x400 + (i * 4)), + 0x10101010); + ret = call_firmware_op(readsfr, + (PA_FIMC_IS_GIC_C + 0x4), + ®_val); + if (ret || reg_val != 0xfc) { + pr_err("Failed to setup IS GIC\n"); + ret = -EINVAL; + } + return ret; +} + static int fimc_is_pipeline_power(struct fimc_is_pipeline *pipeline, int on) { int ret = 0; @@ -565,9 +666,14 @@ static int fimc_is_pipeline_power(struct fimc_is_pipeline *pipeline, int on) if (pipeline->force_down) fimc_is_pipeline_forcedown(pipeline, false); + if (is->drvdata->variant == FIMC_IS_EXYNOS3250) + ret = fimc_is_pipeline_setup_gic(); + if (ret < 0) + return ret; + /* FIMC-IS local power enable */ ret = pm_runtime_get_sync(dev); - if (ret) + if (ret < 0) return ret; /* A5 start address setting */ @@ -581,11 +687,18 @@ static int fimc_is_pipeline_power(struct fimc_is_pipeline *pipeline, int on) ret = fimc_is_pipeline_wait_timeout(pipeline, on); if (ret) dev_err(pipeline->dev, "A5 power on failed\n"); + else + set_bit(PIPELINE_POWER, &pipeline->state); + } else { /* disable A5 */ - pmu_is_write(0x10000, is, PMUREG_ISP_ARM_OPTION); + if( !test_bit(PIPELINE_RESET, &pipeline->state)) + pmu_is_write(0x10000, is, PMUREG_ISP_ARM_OPTION); + else + pmu_is_write(0x00000, is, PMUREG_ISP_ARM_OPTION); /* A5 power off*/ + pmu_is_write(0x0, is, PMUREG_ISP_ARM_OPTION); pmu_is_write(0x0, is, PMUREG_ISP_ARM_CONFIGURATION); /* Check A5 power off status register */ @@ -595,10 +708,11 @@ static int fimc_is_pipeline_power(struct fimc_is_pipeline *pipeline, int on) fimc_is_pipeline_forcedown(pipeline, true); } - pmu_is_write(0x0, is, PMUREG_CMU_RESET_ISP_SYS_PWR_REG); + fimc_is_pipeline_reset_isp(pipeline); /* FIMC-IS local power down */ pm_runtime_put_sync(dev); + clear_bit(PIPELINE_POWER, &pipeline->state); } return ret; @@ -662,105 +776,166 @@ static int fimc_is_pipeline_setfile(struct fimc_is_pipeline *pipeline) return 0; } -static int fimc_is_pipeline_isp_setparams(struct fimc_is_pipeline *pipeline, - unsigned int enable) + +static void fimc_is_pipeline_isp_working_area(struct fimc_is_pipeline *pipeline, + struct v4l2_rect *crop_rect) { - struct isp_param *isp_param = &pipeline->is_region->parameter.isp; - struct fimc_is *is = pipeline->is; - unsigned long index[2] = {0}; - unsigned int sensor_width, sensor_height, scc_width, scc_height; - unsigned int crop_x, crop_y, isp_width, isp_height; + unsigned int sensor_width, sensor_height, isp_width, isp_height; unsigned int sensor_ratio, output_ratio; - int ret; + unsigned int margin_width, margin_height; + /* Crop calculation */ + sensor_width = pipeline->sensor->width; + sensor_height = pipeline->sensor->height; + isp_width = pipeline->isp.width; + isp_height = pipeline->isp.height; - /* Crop calculation */ - sensor_width = pipeline->sensor->width; - sensor_height = pipeline->sensor->height; - scc_width = pipeline->scaler[SCALER_SCC].width; - scc_height = pipeline->scaler[SCALER_SCC].height; - isp_width = sensor_width; - isp_height = sensor_height; - crop_x = crop_y = 0; + crop_rect->width = sensor_width; + crop_rect->height = sensor_height; sensor_ratio = sensor_width * 1000 / sensor_height; - output_ratio = scc_width * 1000 / scc_height; - - if (sensor_ratio == output_ratio) { - isp_width = sensor_width; - isp_height = sensor_height; - } else if (sensor_ratio < output_ratio) { - isp_height = (sensor_width * scc_height) / scc_width; - isp_height = ALIGN(isp_height, 2); - crop_y = ((sensor_height - isp_height) >> 1) & ~1U; - } else { - isp_width = (sensor_height * scc_width) / scc_height; - isp_width = ALIGN(isp_width, 4); - crop_x = ((sensor_width - isp_width) >> 1) & ~1U; + output_ratio = isp_width * 1000 / isp_height; + + crop_rect->top = ((pipeline->sensor->height - sensor_height) >> 1) & ~1U; + crop_rect->left = ((pipeline->sensor->width - sensor_width) >> 1) & ~1U; + + if (sensor_ratio < output_ratio) { + crop_rect->height = (sensor_width * isp_height) / isp_width; + crop_rect->height = ALIGN(crop_rect->height, 2); + crop_rect->top = ((sensor_height - crop_rect->height) >> 1) & ~1U; + } else if (sensor_ratio > output_ratio) { + crop_rect->width = (sensor_height * isp_width) / isp_height; + crop_rect->width = ALIGN(crop_rect->width, 4); + crop_rect->left = ((sensor_width - crop_rect->width) >> 1) & ~1U; } - pipeline->isp_width = isp_width; - pipeline->isp_height = isp_height; - isp_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; - isp_param->otf_output.width = pipeline->sensor->width; - isp_param->otf_output.height = pipeline->sensor->height; - isp_param->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; - isp_param->otf_output.bitwidth = OTF_OUTPUT_BIT_WIDTH_12BIT; - isp_param->otf_output.order = OTF_INPUT_ORDER_BAYER_GR_BG; - __set_bit(PARAM_ISP_OTF_OUTPUT, index); + margin_width = sensor_width - isp_width; + margin_height = sensor_height - isp_height; + crop_rect->width -= (margin_width < SENSOR_WIDTH_PADDING) + ? SENSOR_WIDTH_PADDING + : margin_width; + crop_rect->height -= (margin_height < SENSOR_HEIGHT_PADDING) + ? SENSOR_HEIGHT_PADDING + : margin_height; + - isp_param->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; - __set_bit(PARAM_ISP_DMA1_OUTPUT, index); +} - isp_param->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; - __set_bit(PARAM_ISP_DMA2_OUTPUT, index); +static int fimc_is_pipeline_3aa_setparams(struct fimc_is_pipeline *pipeline, + struct v4l2_rect* crop_reg, + unsigned int enable) +{ + struct taa_param *params = &pipeline->is_region->parameter.taa; + /* Set the control over 3AA / ISP */ + /* Control */ + params->control.cmd = CONTROL_COMMAND_START; + params->control.bypass = CONTROL_BYPASS_DISABLE; + params->control.run_mode = 1; + __set_bit(PARAM_3AA_CONTROL, pipeline->config.params); + /* OTF input */ + params->otf_input.cmd = OTF_INPUT_COMMAND_DISABLE; + params->otf_input.width = pipeline->sensor->width; + params->otf_input.height = pipeline->sensor->height; + + switch (pipeline->isp.fmt->fourcc) { + case V4L2_PIX_FMT_SGRBG8: + params->otf_input.bitwidth = OTF_INPUT_BIT_WIDTH_8BIT; + params->dma_input.bitwidth = DMA_INPUT_BIT_WIDTH_8BIT; + break; + case V4L2_PIX_FMT_SGRBG10: + params->otf_input.bitwidth = OTF_INPUT_BIT_WIDTH_10BIT; + params->dma_input.bitwidth = DMA_INPUT_BIT_WIDTH_10BIT; + break; + default: + params->otf_input.bitwidth = OTF_INPUT_BIT_WIDTH_12BIT; + params->dma_input.bitwidth = DMA_INPUT_BIT_WIDTH_12BIT; + break; + } + + params->otf_input.format = OTF_INPUT_FORMAT_BAYER; + params->otf_input.order = OTF_INPUT_ORDER_BAYER_GR_BG; + + FIMC_IS_FW_CALL_OP(tweak_param, PARAM_OTF_INPUT, + ¶ms->otf_input, crop_reg); + + __set_bit(PARAM_3AA_OTF_INPUT, pipeline->config.params); + + /* DMA input */ + params->dma_input.cmd = DMA_INPUT_COMMAND_BUF_MNGR; + params->dma_input.width = pipeline->sensor->width; + params->dma_input.height = pipeline->sensor->height; + params->dma_input.format = DMA_INPUT_FORMAT_BAYER; + params->dma_input.order = DMA_INPUT_ORDER_GR_BG; + params->dma_input.plane = 1; + params->dma_input.buffer_number = 0; + params->dma_input.buffer_address = 0; + + FIMC_IS_FW_CALL_OP(tweak_param, PARAM_DMA_INPUT, + ¶ms->dma_input, crop_reg); + + __set_bit(PARAM_3AA_VDMA1_INPUT, pipeline->config.params); + + /* DMA output - post BDS output */ + params->dma_bds_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + params->dma_bds_output.width = crop_reg->width; + params->dma_bds_output.height = crop_reg->height; + params->dma_bds_output.buffer_number = 0; + params->dma_bds_output.buffer_address = 0; + params->dma_bds_output.dma_out_mask = 0; + params->dma_bds_output.bitwidth = DMA_OUTPUT_BIT_WIDTH_12BIT; + params->dma_bds_output.notify_dma_done = 1; + params->dma_bds_output.format = DMA_INPUT_FORMAT_BAYER; + __set_bit(PARAM_3AA_VDMA2_OUTPUT, pipeline->config.params); + + return 0; +} - if (enable) - isp_param->control.bypass = CONTROL_BYPASS_DISABLE; - else - isp_param->control.bypass = CONTROL_BYPASS_ENABLE; - isp_param->control.cmd = CONTROL_COMMAND_START; - isp_param->control.run_mode = 1; - __set_bit(PARAM_ISP_CONTROL, index); - - isp_param->dma1_input.cmd = DMA_INPUT_COMMAND_BUF_MNGR; - isp_param->dma1_input.width = sensor_width; - isp_param->dma1_input.height = sensor_height; - isp_param->dma1_input.dma_crop_offset_x = crop_x; - isp_param->dma1_input.dma_crop_offset_y = crop_y; - isp_param->dma1_input.dma_crop_width = isp_width; - isp_param->dma1_input.dma_crop_height = isp_height; - isp_param->dma1_input.bayer_crop_offset_x = 0; - isp_param->dma1_input.bayer_crop_offset_y = 0; - isp_param->dma1_input.bayer_crop_width = 0; - isp_param->dma1_input.bayer_crop_height = 0; - isp_param->dma1_input.user_min_frametime = 0; - isp_param->dma1_input.user_max_frametime = 66666; - isp_param->dma1_input.wide_frame_gap = 1; - isp_param->dma1_input.frame_gap = 4096; - isp_param->dma1_input.line_gap = 45; - isp_param->dma1_input.order = DMA_INPUT_ORDER_GR_BG; - isp_param->dma1_input.plane = 1; - isp_param->dma1_input.buffer_number = 1; - isp_param->dma1_input.buffer_address = 0; - isp_param->dma1_input.reserved[1] = 0; - isp_param->dma1_input.reserved[2] = 0; - if (pipeline->isp.fmt->fourcc == V4L2_PIX_FMT_SGRBG8) - isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_8BIT; - else if (pipeline->isp.fmt->fourcc == V4L2_PIX_FMT_SGRBG10) - isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_10BIT; - else - isp_param->dma1_input.bitwidth = DMA_INPUT_BIT_WIDTH_12BIT; - __set_bit(PARAM_ISP_DMA1_INPUT, index); +static int fimc_is_pipeline_isp_setparams(struct fimc_is_pipeline *pipeline, + struct v4l2_rect* crop_reg, unsigned int enable) +{ + struct isp_param *params = &pipeline->is_region->parameter.isp; + + pipeline->isp_width = crop_reg->width; + pipeline->isp_height = crop_reg->height; + + params->control.cmd = 1; + params->control.run_mode = 1; + + /* OTF OUTPUT */ + params->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; + params->otf_output.width = crop_reg->width; + params->otf_output.height = crop_reg->height; + + params->otf_output.format= OTF_OUTPUT_FORMAT_YUV422; + params->otf_output.bitwidth = OTF_OUTPUT_BIT_WIDTH_8BIT; + params->otf_output.order = OTF_OUTPUT_ORDER_BAYER_GR_BG; + params->dma_output.format = DMA_OUTPUT_FORMAT_YUV422; + params->dma_output.bitwidth = DMA_OUTPUT_BIT_WIDTH_8BIT; + params->dma_output.order = DMA_OUTPUT_ORDER_CRYCBY; + __set_bit(PARAM_ISP_OTF_OUTPUT, pipeline->config.params); + /* DMA OUTPUT */ + params->__dma_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + __set_bit(PARAM_ISP_DMA1_OUTPUT, pipeline->config.params); + params->dma_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + __set_bit(PARAM_ISP_DMA2_OUTPUT, pipeline->config.params); + + /* Set dma output - 3AA capture */ + params->dma_output.cmd = 0; //FIMC_IS_PARAM_ENABLE; + params->dma_output.width = crop_reg->width; + params->dma_output.height = crop_reg->height; + params->dma_output.buffer_number = 0; + params->dma_output.buffer_address = 0; + params->dma_output.dma_out_mask = 0; + params->dma_output.plane = 1; + params->dma_output.bitwidth = DMA_OUTPUT_BIT_WIDTH_12BIT; + params->dma_output.format = DMA_OUTPUT_FORMAT_YUV422; + params->dma_output.order = DMA_OUTPUT_ORDER_CRYCBY; + params->dma_output.reserved[0] = 2; + params->dma_output.notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENBABLE; + __set_bit(PARAM_ISP_DMA2_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_ISP]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_ISP]); return 0; } @@ -769,37 +944,29 @@ static int fimc_is_pipeline_drc_setparams(struct fimc_is_pipeline *pipeline, unsigned int enable) { struct drc_param *drc_param = &pipeline->is_region->parameter.drc; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; if (enable) drc_param->control.bypass = CONTROL_BYPASS_DISABLE; else drc_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_DRC_CONTROL, index); + __set_bit(PARAM_DRC_CONTROL, pipeline->config.params); drc_param->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; drc_param->otf_input.width = pipeline->isp_width; drc_param->otf_input.height = pipeline->isp_height; - __set_bit(PARAM_DRC_OTF_INPUT, index); + __set_bit(PARAM_DRC_OTF_INPUT, pipeline->config.params); drc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; drc_param->otf_output.width = pipeline->isp_width; drc_param->otf_output.height = pipeline->isp_height; - __set_bit(PARAM_DRC_OTF_OUTPUT, index); + __set_bit(PARAM_DRC_OTF_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } + if (enable) - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_DRC]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_DRC]); else - clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_DRC]); + clear_bit(COMP_ENABLE, &pipeline->subip_state[IS_DRC]); return 0; } @@ -809,57 +976,61 @@ static int fimc_is_pipeline_scc_setparams(struct fimc_is_pipeline *pipeline, { struct scalerc_param *scc_param = &pipeline->is_region->parameter.scalerc; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; unsigned int scc_width, scc_height; - scc_width = pipeline->scaler[SCALER_SCC].width; + scc_width = pipeline->scaler[SCALER_SCC].width; scc_height = pipeline->scaler[SCALER_SCC].height; if (enable) scc_param->control.bypass = CONTROL_BYPASS_DISABLE; else scc_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_SCALERC_CONTROL, index); + __set_bit(PARAM_SCALERC_CONTROL, pipeline->config.params); scc_param->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; - scc_param->otf_input.width = pipeline->isp_width; + scc_param->otf_input.width = pipeline->isp_width; scc_param->otf_input.height = pipeline->isp_height; - __set_bit(PARAM_SCALERC_OTF_INPUT, index); - + __set_bit(PARAM_SCALERC_OTF_INPUT, pipeline->config.params); /* SCC OUTPUT */ scc_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; scc_param->input_crop.pos_x = 0; scc_param->input_crop.pos_y = 0; - scc_param->input_crop.crop_width = pipeline->isp_width; + scc_param->input_crop.crop_width = pipeline->isp_width; scc_param->input_crop.crop_height = pipeline->isp_height; - scc_param->input_crop.in_width = pipeline->isp_width; - scc_param->input_crop.in_height = pipeline->isp_height; - scc_param->input_crop.out_width = scc_width; + scc_param->input_crop.in_width = pipeline->isp_width; + scc_param->input_crop.in_height = pipeline->isp_height; + scc_param->input_crop.out_width = scc_width; scc_param->input_crop.out_height = scc_height; - __set_bit(PARAM_SCALERC_INPUT_CROP, index); + __set_bit(PARAM_SCALERC_INPUT_CROP, pipeline->config.params); scc_param->output_crop.cmd = SCALER_CROP_COMMAND_DISABLE; scc_param->output_crop.pos_x = 0; scc_param->output_crop.pos_y = 0; scc_param->output_crop.crop_width = scc_width; scc_param->output_crop.crop_height = scc_height; - __set_bit(PARAM_SCALERC_OUTPUT_CROP, index); + __set_bit(PARAM_SCALERC_OUTPUT_CROP, pipeline->config.params); scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; scc_param->otf_output.width = scc_width; scc_param->otf_output.height = scc_height; - __set_bit(PARAM_SCALERC_OTF_OUTPUT, index); + __set_bit(PARAM_SCALERC_OTF_OUTPUT, pipeline->config.params); + + scc_param->dma_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; + scc_param->dma_output.dma_out_mask = 0; + scc_param->dma_output.buffer_number = 0; + scc_param->dma_output.buffer_address = 0; + scc_param->dma_output.plane = 0; + scc_param->dma_output.width = scc_width; + scc_param->dma_output.height = scc_height; + __set_bit(PARAM_SCALERC_DMA_OUTPUT, pipeline->config.params); + scc_param->effect.cmd = 0; + scc_param->effect.arbitrary_cb = 128; + scc_param->effect.arbitrary_cr = 128; + scc_param->effect.yuv_range = 0; + __set_bit(PARAM_SCALERC_IMAGE_EFFECT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_SCC]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_SCC]); return 0; } @@ -867,9 +1038,6 @@ static int fimc_is_pipeline_odc_setparams(struct fimc_is_pipeline *pipeline, unsigned int enable) { struct odc_param *odc_param = &pipeline->is_region->parameter.odc; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; unsigned int scc_width, scc_height; scc_width = pipeline->scaler[SCALER_SCC].width; @@ -879,27 +1047,22 @@ static int fimc_is_pipeline_odc_setparams(struct fimc_is_pipeline *pipeline, odc_param->control.bypass = CONTROL_BYPASS_DISABLE; else odc_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_ODC_CONTROL, index); + __set_bit(PARAM_ODC_CONTROL, pipeline->config.params); odc_param->otf_input.width = scc_width; odc_param->otf_input.height = scc_height; - __set_bit(PARAM_ODC_OTF_INPUT, index); + __set_bit(PARAM_ODC_OTF_INPUT, pipeline->config.params); odc_param->otf_output.width = scc_width; odc_param->otf_output.height = scc_height; - __set_bit(PARAM_ODC_OTF_OUTPUT, index); + __set_bit(PARAM_ODC_OTF_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } + if (enable) - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_ODC]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_ODC]); else - clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_ODC]); + clear_bit(COMP_ENABLE, &pipeline->subip_state[IS_ODC]); return 0; } @@ -908,9 +1071,6 @@ static int fimc_is_pipeline_dis_setparams(struct fimc_is_pipeline *pipeline, unsigned int enable) { struct dis_param *dis_param = &pipeline->is_region->parameter.dis; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; unsigned int scc_width, scc_height; scc_width = pipeline->scaler[SCALER_SCC].width; @@ -920,29 +1080,24 @@ static int fimc_is_pipeline_dis_setparams(struct fimc_is_pipeline *pipeline, dis_param->control.bypass = CONTROL_BYPASS_DISABLE; else dis_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_DIS_CONTROL, index); + __set_bit(PARAM_DIS_CONTROL, pipeline->config.params); /* DIS INPUT */ dis_param->otf_input.width = scc_width; dis_param->otf_input.height = scc_height; - __set_bit(PARAM_DIS_OTF_INPUT, index); + __set_bit(PARAM_DIS_OTF_INPUT, pipeline->config.params); /* DIS OUTPUT */ dis_param->otf_output.width = scc_width; dis_param->otf_output.height = scc_height; - __set_bit(PARAM_DIS_OTF_OUTPUT, index); + __set_bit(PARAM_DIS_OTF_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } + if (enable) - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_DIS]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_DIS]); else - clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_DIS]); + clear_bit(COMP_ENABLE, &pipeline->subip_state[IS_DIS]); return 0; } @@ -951,9 +1106,6 @@ static int fimc_is_pipeline_3dnr_setparams(struct fimc_is_pipeline *pipeline, unsigned int enable) { struct tdnr_param *tdnr_param = &pipeline->is_region->parameter.tdnr; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; unsigned int scc_width, scc_height; scc_width = pipeline->scaler[SCALER_SCC].width; @@ -963,31 +1115,26 @@ static int fimc_is_pipeline_3dnr_setparams(struct fimc_is_pipeline *pipeline, tdnr_param->control.bypass = CONTROL_BYPASS_DISABLE; else tdnr_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_TDNR_CONTROL, index); + __set_bit(PARAM_TDNR_CONTROL, pipeline->config.params); tdnr_param->otf_input.width = scc_width; tdnr_param->otf_input.height = scc_height; - __set_bit(PARAM_TDNR_OTF_INPUT, index); + __set_bit(PARAM_TDNR_OTF_INPUT, pipeline->config.params); tdnr_param->dma_output.width = scc_width; tdnr_param->dma_output.height = scc_height; - __set_bit(PARAM_TDNR_DMA_OUTPUT, index); + __set_bit(PARAM_TDNR_DMA_OUTPUT, pipeline->config.params); tdnr_param->otf_output.width = scc_width; tdnr_param->otf_output.height = scc_height; - __set_bit(PARAM_TDNR_OTF_OUTPUT, index); + __set_bit(PARAM_TDNR_OTF_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } + if (enable) - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_TDNR]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_TDNR]); else - clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_TDNR]); + clear_bit(COMP_ENABLE, &pipeline->subip_state[IS_TDNR]); return 0; } @@ -997,9 +1144,7 @@ static int fimc_is_pipeline_scp_setparams(struct fimc_is_pipeline *pipeline, { struct scalerp_param *scp_param = &pipeline->is_region->parameter.scalerp; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; + unsigned int scc_width, scc_height; unsigned int scp_width, scp_height; @@ -1012,12 +1157,12 @@ static int fimc_is_pipeline_scp_setparams(struct fimc_is_pipeline *pipeline, scp_param->control.bypass = CONTROL_BYPASS_DISABLE; else scp_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_SCALERP_CONTROL, index); + __set_bit(PARAM_SCALERP_CONTROL, pipeline->config.params); /* SCP Input */ scp_param->otf_input.width = scc_width; scp_param->otf_input.height = scc_height; - __set_bit(PARAM_SCALERP_OTF_INPUT, index); + __set_bit(PARAM_SCALERP_OTF_INPUT, pipeline->config.params); /* SCP Output */ scp_param->input_crop.cmd = SCALER_CROP_COMMAND_ENABLE; @@ -1029,23 +1174,17 @@ static int fimc_is_pipeline_scp_setparams(struct fimc_is_pipeline *pipeline, scp_param->input_crop.in_height = scc_height; scp_param->input_crop.out_width = scp_width; scp_param->input_crop.out_height = scp_height; - __set_bit(PARAM_SCALERP_INPUT_CROP, index); + __set_bit(PARAM_SCALERP_INPUT_CROP, pipeline->config.params); scp_param->output_crop.cmd = SCALER_CROP_COMMAND_DISABLE; - __set_bit(PARAM_SCALERP_OUTPUT_CROP, index); + __set_bit(PARAM_SCALERP_OUTPUT_CROP, pipeline->config.params); scp_param->otf_output.width = scp_width; scp_param->otf_output.height = scp_height; - __set_bit(PARAM_SCALERP_OTF_OUTPUT, index); + __set_bit(PARAM_SCALERP_OTF_OUTPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_SCP]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_SCP]); return 0; } @@ -1054,31 +1193,28 @@ static int fimc_is_pipeline_fd_setparams(struct fimc_is_pipeline *pipeline, unsigned int enable) { struct fd_param *fd_param = &pipeline->is_region->parameter.fd; - struct fimc_is *is = pipeline->is; - int ret; - unsigned long index[2] = {0}; if (enable) fd_param->control.bypass = CONTROL_BYPASS_DISABLE; else fd_param->control.bypass = CONTROL_BYPASS_ENABLE; - __set_bit(PARAM_FD_CONTROL, index); + __set_bit(PARAM_FD_CONTROL, pipeline->config.params); - fd_param->otf_input.width = pipeline->scaler[SCALER_SCP].width; - fd_param->otf_input.height = pipeline->scaler[SCALER_SCP].height; - __set_bit(PARAM_FD_OTF_INPUT, index); + if (pipeline->subip_state[IS_SCP] != COMP_INVALID) { + fd_param->otf_input.width = pipeline->scaler[SCALER_SCP].width; + fd_param->otf_input.height = pipeline->scaler[SCALER_SCP].height; + } else { + fd_param->otf_input.width = pipeline->scaler[SCALER_SCC].width; + fd_param->otf_input.height = pipeline->scaler[SCALER_SCC].height; + } + __set_bit(PARAM_FD_OTF_INPUT, pipeline->config.params); wmb(); - ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); - if (ret) { - dev_err(pipeline->dev, "%s failed\n", __func__); - return -EINVAL; - } + if (enable) - set_bit(COMP_ENABLE, &pipeline->comp_state[IS_FD]); + set_bit(COMP_ENABLE, &pipeline->subip_state[IS_FD]); else - clear_bit(COMP_ENABLE, &pipeline->comp_state[IS_FD]); + clear_bit(COMP_ENABLE, &pipeline->subip_state[IS_FD]); return 0; } @@ -1093,23 +1229,27 @@ void fimc_is_pipeline_buf_unlock(struct fimc_is_pipeline *pipeline) spin_unlock_irqrestore(&pipeline->slock_buf, pipeline->slock_flags); } -int fimc_is_pipeline_setparams(struct fimc_is_pipeline *pipeline) +int fimc_is_pipeline_set_params(struct fimc_is_pipeline *pipeline) { int ret; + struct v4l2_rect crop_rect = {0,}; + fimc_is_pipeline_isp_working_area(pipeline, &crop_rect); /* Enabling basic components in pipeline */ - ret = fimc_is_pipeline_isp_setparams(pipeline, true); + ret = fimc_is_pipeline_3aa_setparams(pipeline, &crop_rect, true); + if (!ret) + ret = fimc_is_pipeline_isp_setparams(pipeline, &crop_rect, true); if (!ret) ret = fimc_is_pipeline_drc_setparams(pipeline, false); if (!ret) ret = fimc_is_pipeline_scc_setparams(pipeline, true); - if (!ret) + if (!ret && pipeline->subip_state[IS_ODC] != COMP_INVALID) ret = fimc_is_pipeline_odc_setparams(pipeline, false); - if (!ret) + if (!ret && pipeline->subip_state[IS_DIS] != COMP_INVALID) ret = fimc_is_pipeline_dis_setparams(pipeline, false); - if (!ret) + if (!ret && pipeline->subip_state[IS_TDNR] != COMP_INVALID) ret = fimc_is_pipeline_3dnr_setparams(pipeline, false); - if (!ret) + if (!ret && pipeline->subip_state[IS_SCP] != COMP_INVALID) ret = fimc_is_pipeline_scp_setparams(pipeline, true); if (!ret) ret = fimc_is_pipeline_fd_setparams(pipeline, false); @@ -1119,6 +1259,58 @@ int fimc_is_pipeline_setparams(struct fimc_is_pipeline *pipeline) return ret; } + +static void fimc_is_pipeline_scalerc_validate(struct fimc_is_pipeline* pipeline, + struct v4l2_rect *scc_reg) +{ + struct v4l2_rect scc_input = {0,}; + struct v4l2_rect scc_output = {0,}; + struct fimc_is_scaler *scc = &pipeline->scaler[SCALER_SCC]; + struct scalerc_param *scc_params = &pipeline->is_region->parameter.scalerc; + const struct fimc_is_fmt *fmt = scc->fmt; + + scc_input.width = scc_params->otf_input.width; + scc_input.height = scc_params->otf_input.height; + + scc_output.width = scc->width; + scc_output.height = scc->height; + + if (scc_input.width * FIMC_SCC_MAX_RESIZE_RATIO < scc_output.width) { + scc_reg->width = scc_input.width * FIMC_SCC_MAX_RESIZE_RATIO; + v4l2_warn(&scc->subdev, + "Cannot scale up beyond max scale ratio [x4]\n"); + } + if (scc_input.height * FIMC_SCC_MAX_RESIZE_RATIO < scc_output.height) { + scc_reg->height = scc_input.height * FIMC_SCC_MAX_RESIZE_RATIO; + v4l2_warn(&scc->subdev, + "Cannot scale up beyond max scale ratio [x4]\n"); + } + if (scc_input.width > scc_output.width * FIMC_SCC_MIN_RESIZE_RATIO) { + scc_reg->width = scc_input.width / FIMC_SCC_MAX_RESIZE_RATIO; + v4l2_warn(&scc->subdev, + "Cannot scale down beyond min scale ratio [1/16]\n"); + } + if (scc_input.height > scc_output.height * FIMC_SCC_MAX_RESIZE_RATIO) { + scc_reg->height = scc_input.height / FIMC_SCC_MAX_RESIZE_RATIO; + v4l2_warn(&scc->subdev, + "Cannot scale down beyond min scale ratio [1/16]\n"); + } + + scc_reg->width -= scc_reg->width & 0x7; + scc_reg->height -= scc_reg->height & 0x7; + + switch(fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_NV12M: + scc_reg->width -= scc_reg->width & 0xf; + scc_reg->height -= scc_reg->height & 0xf; + case V4L2_PIX_FMT_NV16: + break; + default: + break; + } +} + int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline, enum fimc_is_scaler_id scaler_id, unsigned int num_bufs, @@ -1130,16 +1322,19 @@ int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline, struct scalerc_param *scc_param = &pipeline->is_region->parameter.scalerc; struct param_dma_output *dma_output; + struct param_otf_output *otf_output; const struct fimc_is_fmt *fmt; + struct fimc_is_scaler* scaler; unsigned int region_index; - unsigned long *comp_state; - int ret; + unsigned long *subip_state; + struct v4l2_rect scaler_reg = {0,}; + int ret = 0; unsigned int pipe_start_flag = 0; unsigned int i, buf_mask = 0; unsigned long index[2] = {0}; - enum fimc_is_scaler_id id; - if (!test_bit(PIPELINE_OPEN, &pipeline->state)) { + if (!test_bit(PIPELINE_OPEN, &pipeline->state) + || !test_bit(PIPELINE_PREPARE, &pipeline->state)) { dev_err(pipeline->dev, "Pipeline not opened.\n"); return -EINVAL; } @@ -1160,46 +1355,84 @@ int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline, } if (scaler_id == SCALER_SCC) { - dma_output = &scc_param->dma_output; + scaler = &pipeline->scaler[SCALER_SCC]; + /* Validate the resize ratio : 1/16 to 4 */ + scaler_reg.width = scaler->width; + scaler_reg.height = scaler->height; + fimc_is_pipeline_scalerc_validate(pipeline, &scaler_reg); + dma_output = &scc_param->dma_output; region_index = FIMC_IS_SCC_REGION_INDEX; - comp_state = &pipeline->comp_state[IS_SCC]; - id = SCALER_SCC; + subip_state = &pipeline->subip_state[IS_SCC]; __set_bit(PARAM_SCALERC_DMA_OUTPUT, index); + otf_output = &scc_param->otf_output; + scc_param->otf_output.cmd = OTF_OUTPUT_COMMAND_DISABLE; + __set_bit(PARAM_SCALERC_OTF_OUTPUT, index); } else { + scaler = &pipeline->scaler[SCALER_SCP]; + scaler_reg.width = scaler->width; + scaler_reg.height = scaler->height; dma_output = &scp_param->dma_output; - comp_state = &pipeline->comp_state[IS_SCP]; + otf_output = &scp_param->otf_output; + subip_state = &pipeline->subip_state[IS_SCP]; region_index = FIMC_IS_SCP_REGION_INDEX; - id = SCALER_SCP; __set_bit(PARAM_SCALERP_DMA_OUTPUT, index); + __set_bit(PARAM_SCALERP_OTF_OUTPUT, index); } for (i = 0; i < num_bufs; i++) buf_mask |= BIT(i); - fmt = pipeline->scaler[id].fmt; - dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; - dma_output->dma_out_mask = buf_mask; + fmt = scaler->fmt; + if (num_planes < fmt->num_planes ) { + v4l2_warn(&scaler->subdev, + "Insufficient number of planes!\n"); + goto exit; + } + + dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE; + dma_output->dma_out_mask = buf_mask; dma_output->buffer_number = num_bufs; - dma_output->plane = num_planes; - dma_output->width = pipeline->scaler[id].width; - dma_output->height = pipeline->scaler[id].height; - if ((fmt->fourcc == V4L2_PIX_FMT_YUV420M) || - (fmt->fourcc == V4L2_PIX_FMT_NV12M)) + dma_output->plane = fmt->num_planes; + dma_output->width = scaler_reg.width; + dma_output->height = scaler_reg.height; + + switch (fmt->fourcc){ + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_NV12M: dma_output->format = DMA_OUTPUT_FORMAT_YUV420; - else if (fmt->fourcc == V4L2_PIX_FMT_NV16) + dma_output->order = DMA_OUTPUT_ORDER_CRCB; + break; + case V4L2_PIX_FMT_NV16: dma_output->format = DMA_OUTPUT_FORMAT_YUV422; + dma_output->order = DMA_OUTPUT_ORDER_CRYCBY; + default: + v4l2_info(&scaler->subdev, + "Unsupported format.\n"); + break; + } dma_output->buffer_address = pipeline->minfo->shared.paddr + region_index * sizeof(unsigned int); + + + if (is->drvdata->variant == FIMC_IS_EXYNOS3250) + dma_output->reserved[0] = SCALER_DMA_OUT_SCALED; + else + dma_output->reserved[0] = SCALER_DMA_OUT_UNSCALED; + __set_bit(PARAM_SCALERC_DMA_OUTPUT, index); + otf_output->cmd = OTF_OUTPUT_COMMAND_DISABLE; + otf_output->width = scaler_reg.width; + otf_output->height = scaler_reg.height; + ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); + ISS_PREVIEW_STILL, index[0], index[1]); if (ret) dev_err(pipeline->dev, "fimc_is_itf_set_param is fail\n"); - else - set_bit(COMP_START, comp_state); - + else{ + set_bit(COMP_START, subip_state); + } if (pipe_start_flag) { ret = fimc_is_pipeline_start(pipeline, 0); if (ret) @@ -1222,7 +1455,7 @@ int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline, struct param_dma_output *dma_output; unsigned int pipe_start_flag = 0; unsigned long index[2] = {0}; - unsigned long *comp_state; + unsigned long *subip_state; int ret; if (!test_bit(PIPELINE_OPEN, &pipeline->state)) @@ -1243,10 +1476,10 @@ int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline, pipe_start_flag = 1; } - comp_state = (scaler_id == SCALER_SCC) ? - &pipeline->comp_state[IS_SCC] : &pipeline->comp_state[IS_SCP]; + subip_state = (scaler_id == SCALER_SCC) ? + &pipeline->subip_state[IS_SCC] : &pipeline->subip_state[IS_SCP]; - if (!test_bit(COMP_START, comp_state)) { + if (!test_bit(COMP_START, subip_state)) { ret = 0; goto exit; } @@ -1261,11 +1494,11 @@ int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline, dma_output->cmd = DMA_OUTPUT_COMMAND_DISABLE; ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); + ISS_PREVIEW_STILL, index[0], index[1]); if (ret) dev_err(pipeline->dev, "fimc_is_itf_set_param is fail\n"); else - clear_bit(COMP_START, comp_state); + clear_bit(COMP_START, subip_state); if (pipe_start_flag) { ret = fimc_is_pipeline_start(pipeline, 0); @@ -1280,13 +1513,7 @@ exit: void fimc_is_pipeline_config_shot(struct fimc_is_pipeline *pipeline) { - struct camera2_shot *shot = - (struct camera2_shot *)pipeline->minfo->shot.vaddr; - - shot->magicnumber = FIMC_IS_MAGIC_NUMBER; - - shot->ctl.aa.mode = AA_CONTROL_AUTO; - shot->ctl.aa.ae_mode = AA_AEMODE_ON; + FIMC_IS_FW_CALL_OP(config_shot, pipeline->minfo->shot.vaddr); } int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline) @@ -1298,20 +1525,83 @@ int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline) return ret; } +static void fimc_is_pipeline_shot_cancel(struct fimc_is_pipeline *pipeline) +{ + struct fimc_is_buf *buffer; + struct fimc_is_isp *isp = &pipeline->isp; + struct fimc_is_scaler *scaler; + + fimc_is_pipeline_buf_lock(pipeline); + /* Release ISP buffers */ + while (!list_empty(&isp->wait_queue)) { + buffer = fimc_is_isp_wait_queue_get(isp); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + + if(!list_empty(&isp->run_queue)) { + while (!list_empty(&isp->run_queue)) { + buffer = fimc_is_isp_run_queue_get(isp); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + } + /* Release scaler buffers */ + scaler = &pipeline->scaler[SCALER_SCC]; + if (test_bit(COMP_START, &pipeline->subip_state[IS_SCC])) { + while (!list_empty(&scaler->wait_queue)) { + buffer = fimc_is_scaler_wait_queue_get(scaler); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + while (!list_empty(&scaler->run_queue)) { + buffer = fimc_is_scaler_run_queue_get(scaler); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + } + scaler = &pipeline->scaler[SCALER_SCP]; + if (test_bit(COMP_START, &pipeline->subip_state[IS_SCP])) { + while (!list_empty(&scaler->wait_queue)) { + buffer = fimc_is_scaler_wait_queue_get(scaler); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + while (!list_empty(&scaler->run_queue)) { + buffer = fimc_is_scaler_run_queue_get(scaler); + vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + } + } + fimc_is_pipeline_buf_unlock(pipeline); +} + int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline) { struct fimc_is *is = pipeline->is; int ret; unsigned int rcount, i; + unsigned int *shared_reg = NULL; struct camera2_shot *shot = pipeline->minfo->shot.vaddr; struct fimc_is_buf *scc_buf, *scp_buf, *bayer_buf; + unsigned int shot_paddr = pipeline->minfo->shot.paddr; + struct camera2_scaler_uctl_base *scaler_ud; - if (!test_bit(PIPELINE_START, &pipeline->state)) - return -EINVAL; + if (!test_bit(PIPELINE_START, &pipeline->state)) { + ret = fimc_is_itf_process_on(&is->interface, pipeline->instance); + if (ret) + return -EINVAL; + } if (test_bit(PIPELINE_RUN, &pipeline->state)) return -EBUSY; + /** + * Verify if previous requests didn't result in potentiall error. + * If so - there is no point to call another shots untill + * the pipeline gets re-configured properly + */ + if (test_bit(PIPELINE_RESET, &pipeline->state)) { + dev_err(pipeline->dev, "Failed on previous frame capture request." + " Re-configure prior to further attempts.\n"); + fimc_is_pipeline_shot_cancel(pipeline); + goto err_exit; + } + fimc_is_pipeline_buf_lock(pipeline); if (list_empty(&pipeline->isp.wait_queue)) { @@ -1331,8 +1621,18 @@ int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline) } fimc_is_isp_run_queue_add(&pipeline->isp, bayer_buf); + shared_reg = (unsigned int*)(pipeline->minfo->shared.vaddr); + memset(shared_reg + FIMC_IS_SCC_REGION_INDEX *sizeof(unsigned int), + 0, sizeof(unsigned int) * FIMC_IS_MAX_PLANES); + memset(shared_reg + FIMC_IS_SCP_REGION_INDEX *sizeof(unsigned int), + 0, sizeof(unsigned int) * FIMC_IS_MAX_PLANES); + + scaler_ud = pipeline->config.metadata_mode == CAMERA2_SHOT_BASE_MODE + ? &((struct camera2_shot_base*)shot)->uctl.scaler_ud + : &shot->uctl.scaler_ud.base; + /* Get SCC buffer */ - if (test_bit(COMP_START, &pipeline->comp_state[IS_SCC]) && + if (test_bit(COMP_START, &pipeline->subip_state[IS_SCC]) && !list_empty(&pipeline->scaler[SCALER_SCC].wait_queue)) { scc_buf = fimc_is_scaler_wait_queue_get( &pipeline->scaler[SCALER_SCC]); @@ -1340,19 +1640,25 @@ int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline) fimc_is_scaler_run_queue_add( &pipeline->scaler[SCALER_SCC], scc_buf); - for (i = 0; i < 3; i++) - shot->uctl.scaler_ud.scc_target_address[i] = + if (scc_buf->vb.num_planes > FIMC_IS_MAX_PLANES) + dev_dbg(pipeline->dev, "Invalid number of planes!"); + shared_reg = (unsigned int*)(pipeline->minfo->shared.vaddr + + FIMC_IS_SCC_REGION_INDEX * sizeof(unsigned int)); + for (i = 0; i < scc_buf->vb.num_planes; i++) { + scaler_ud->scc_target_address[i] = scc_buf->paddr[i]; - set_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]); + shared_reg[i] = scc_buf->paddr[i]; + } + set_bit(COMP_RUN, &pipeline->subip_state[IS_SCC]); } } else { - dev_dbg(pipeline->dev, "No SCC buffer available\n"); + dev_err(pipeline->dev, "No SCC buffer available\n"); for (i = 0; i < 3; i++) - shot->uctl.scaler_ud.scc_target_address[i] = 0; + scaler_ud->scc_target_address[i] = 0; } /* Get SCP buffer */ - if (test_bit(COMP_START, &pipeline->comp_state[IS_SCP]) && + if (test_bit(COMP_START, &pipeline->subip_state[IS_SCP]) && !list_empty(&pipeline->scaler[SCALER_SCP].wait_queue)) { scp_buf = fimc_is_scaler_wait_queue_get( &pipeline->scaler[SCALER_SCP]); @@ -1360,86 +1666,136 @@ int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline) fimc_is_scaler_run_queue_add( &pipeline->scaler[SCALER_SCP], scp_buf); - for (i = 0; i < 3; i++) - shot->uctl.scaler_ud.scp_target_address[i] = + if (scp_buf->vb.num_planes > FIMC_IS_MAX_PLANES) + dev_dbg(pipeline->dev, + "Invalid number of planes!"); + shared_reg = (unsigned int*)(pipeline->minfo->shared.vaddr + + FIMC_IS_SCP_REGION_INDEX * sizeof(unsigned int)); + for (i = 0; i < FIMC_IS_MAX_PLANES; i++){ + scaler_ud->scp_target_address[i] = scp_buf->paddr[i]; - set_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]); + shared_reg[i] = scp_buf->paddr[i]; + } + set_bit(COMP_RUN, &pipeline->subip_state[IS_SCP]); } } else { dev_dbg(pipeline->dev, "No SCP buffer available\n"); for (i = 0; i < 3; i++) - shot->uctl.scaler_ud.scp_target_address[i] = 0; + scaler_ud->scp_target_address[i] = 0; } fimc_is_pipeline_buf_unlock(pipeline); /* Send shot command */ pipeline->fcount++; rcount = pipeline->fcount; - shot->ctl.request.framecount = pipeline->fcount; dev_dbg(pipeline->dev, "Shot command fcount : %d, Bayer addr : 0x%08x\n", pipeline->fcount, bayer_buf->paddr[0]); ret = fimc_is_itf_shot_nblk(&is->interface, pipeline->instance, bayer_buf->paddr[0], - pipeline->minfo->shot.paddr, + shot_paddr, pipeline->fcount, rcount); if (ret) { dev_err(pipeline->dev, "Shot command failed\n"); goto err_exit; } + queue_delayed_work(fimc_is_pipeline_workqueue, &pipeline->wdg_work, + nsecs_to_jiffies(pipeline->frame_duration)*2); return 0; err_exit: clear_bit(PIPELINE_RUN, &pipeline->state); - clear_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]); - clear_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]); + clear_bit(COMP_RUN, &pipeline->subip_state[IS_SCC]); + clear_bit(COMP_RUN, &pipeline->subip_state[IS_SCP]); return -EINVAL; } -int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon) + +static int fimc_is_pipeline_set_shotmode(struct fimc_is_pipeline *pipeline) +{ + struct global_param *global_shotmode = &pipeline->is_region->parameter.global; + global_shotmode->shotmode.cmd = 1; + global_shotmode->shotmode.skip_frames = 4; + return 0; +} + +static int fimc_is_pipeline_prepare(struct fimc_is_pipeline *pipeline) { int ret = 0; struct fimc_is *is = pipeline->is; - - /* Check if open or not */ - if (!test_bit(PIPELINE_OPEN, &pipeline->state)) { - dev_err(pipeline->dev, "Pipeline not open\n"); - return -EINVAL; - } - - mutex_lock(&pipeline->pipe_lock); - - /* Check if already started */ - if (test_bit(PIPELINE_START, &pipeline->state)) - goto err_exit; - + int scenario; /* Set pipeline component params */ - ret = fimc_is_pipeline_setparams(pipeline); + ret = fimc_is_pipeline_set_params(pipeline); if (ret) { dev_err(pipeline->dev, "Set params failed\n"); - goto err_exit; + goto leave; + } + + fimc_is_pipeline_set_shotmode(pipeline); + + for(scenario = ISS_PREVIEW_STILL; scenario < ISS_END; ++scenario) { + ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, + scenario, + pipeline->config.params[0], + pipeline->config.params[1]); + if (ret) { + fimc_is_pipeline_reset_isp(pipeline); + dev_err(pipeline->dev, "Failed to set parameters\n"); + goto leave; + } } /* Send preview still command */ ret = fimc_is_itf_preview_still(&is->interface, pipeline->instance); if (ret) { dev_err(pipeline->dev, "Preview still failed\n"); - goto err_exit; + goto leave; } /* Confiture shot memory to A5 */ - ret = fimc_is_itf_cfg_mem(&is->interface, pipeline->instance, + ret = fimc_is_itf_map_mem(&is->interface, pipeline->instance, pipeline->minfo->shot.paddr, - sizeof(struct camera2_shot)); + FIMC_IS_SHOT_SIZE); if (ret) { dev_err(pipeline->dev, "Config A5 mem failed\n"); - goto err_exit; + goto leave; } /* Set shot params */ fimc_is_pipeline_config_shot(pipeline); + set_bit(PIPELINE_PREPARE, &pipeline->state); +leave: + return ret; +} + +int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon) +{ + int ret = 0; + struct fimc_is *is = pipeline->is; + + /* Check if open or not */ + if (!test_bit(PIPELINE_OPEN, &pipeline->state)) { + dev_err(pipeline->dev, "Pipeline not open\n"); + return -EINVAL; + } + + mutex_lock(&pipeline->pipe_lock); + + /* Check if already started */ + if (test_bit(PIPELINE_START, &pipeline->state)) + goto err_exit; + + /* EXYNOS3250: EVT0 does not support sensor configuration : no sensor mode change ? */ + if (!test_bit(PIPELINE_PREPARE, &pipeline->state)) + ret = fimc_is_pipeline_prepare(pipeline); + if (ret) { + dev_err(pipeline->dev, + "Failed to initialize IS pipeleine\n"); + goto err_exit; + } + /* Process ON command */ ret = fimc_is_itf_process_on(&is->interface, pipeline->instance); if (ret) { @@ -1467,24 +1823,55 @@ err_exit: return ret; } +int fimc_is_pipeline_stop_fast_path(struct fimc_is_pipeline *pipeline) +{ + unsigned long timeout = FIMC_IS_COMMAND_TIMEOUT/2; + + mutex_lock(&pipeline->pipe_lock); + /** + * Give a chance to perform the actual stop + * but ignore potential errors + * as IS might not be responsive at all. + */ + wait_event_timeout(pipeline->scaler[SCALER_SCC].event_q, + !test_bit(COMP_RUN, &pipeline->subip_state[IS_SCC]), + timeout); + wait_event_timeout(pipeline->scaler[SCALER_SCC].event_q, + !test_bit(COMP_RUN, &pipeline->subip_state[IS_SCC]), + timeout); + wait_event_timeout(pipeline->scaler[SCALER_SCP].event_q, + !test_bit(COMP_RUN, &pipeline->subip_state[IS_SCP]), + timeout); + + clear_bit(PIPELINE_START, &pipeline->state); + mutex_unlock(&pipeline->pipe_lock); + return 0; +} + int fimc_is_pipeline_stop(struct fimc_is_pipeline *pipeline, int streamoff) { - int ret; + int ret = 0; struct fimc_is *is = pipeline->is; mutex_lock(&pipeline->pipe_lock); /* Check if started */ if (!test_bit(PIPELINE_OPEN, &pipeline->state) || - !test_bit(PIPELINE_START, &pipeline->state)) { - ret = -EINVAL; + !test_bit(PIPELINE_START, &pipeline->state)) { goto err_exit; } + if (test_bit(PIPELINE_RESET, &pipeline->state)){ + mutex_unlock(&pipeline->pipe_lock); + return fimc_is_pipeline_stop_fast_path(pipeline); + } + + /* Wait if any operation is in progress */ ret = wait_event_timeout(pipeline->wait_q, !test_bit(PIPELINE_RUN, &pipeline->state), FIMC_IS_COMMAND_TIMEOUT); + if (!ret) { dev_err(pipeline->dev, "Pipeline timeout"); ret = -EBUSY; @@ -1493,15 +1880,16 @@ int fimc_is_pipeline_stop(struct fimc_is_pipeline *pipeline, int streamoff) /* Wait for scaler operations if any to complete */ ret = wait_event_timeout(pipeline->scaler[SCALER_SCC].event_q, - !test_bit(COMP_RUN, &pipeline->comp_state[IS_SCC]), + !test_bit(COMP_RUN, &pipeline->subip_state[IS_SCC]), FIMC_IS_COMMAND_TIMEOUT); if (!ret) { dev_err(pipeline->dev, "SCC timeout"); ret = -EBUSY; goto err_exit; } + ret = wait_event_timeout(pipeline->scaler[SCALER_SCP].event_q, - !test_bit(COMP_RUN, &pipeline->comp_state[IS_SCP]), + !test_bit(COMP_RUN, &pipeline->subip_state[IS_SCP]), FIMC_IS_COMMAND_TIMEOUT); if (!ret) { dev_err(pipeline->dev, "SCP timeout"); @@ -1536,12 +1924,23 @@ err_exit: return ret; } + +static void fimc_is_setup_sensor_open_extended(struct is_region *region, + struct fimc_is_sensor *sensor) +{ + region->shared[FIMC_IS_SENSOR_ID] = sensor->drvdata->id; + region->shared[FIMC_IS_SENSOR_I2C_CHAN] = sensor->i2c_bus; + region->shared[FIMC_IS_SENSOR_I2C_ADDR] = sensor->i2c_slave_addr; + region->shared[FIMC_IS_SESNOR_I2C_SPEED] = 400000; + region->shared[FIMC_IS_SENSOR_MIPI_LANES] = 1; + region->shared[FIMC_IS_SENSOR_I2C_SCLK] = 24000000; +} + int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, struct fimc_is_sensor *sensor) { struct fimc_is *is = pipeline->is; struct is_region *region; - unsigned long index[2] = {0}; int ret; if (!sensor) @@ -1556,9 +1955,18 @@ int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, } pipeline->fcount = 0; + pipeline->last_frame = ktime_set(0,0); + pipeline->frame_duration = 0; pipeline->sensor = sensor; if (is->num_pipelines == 0) { + /* Setup firmware options */ + ret = fimc_is_fw_init_ops(is->drvdata->fw_version); + if (ret) { + dev_err(pipeline->dev, + "Failed to load firmware configuration."); + goto err_exit; + } /* Init memory */ ret = fimc_is_pipeline_initmem(pipeline); if (ret) { @@ -1590,14 +1998,31 @@ int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, /* Open Sensor */ region = pipeline->is_region; - ret = fimc_is_itf_open_sensor(&is->interface, - pipeline->instance, - sensor->drvdata->id, - sensor->i2c_bus, - pipeline->minfo->shared.paddr); + + if (is->drvdata->variant == FIMC_IS_EXYNOS5) { + ret = fimc_is_itf_open_sensor(&is->interface, + pipeline->instance, + sensor->drvdata->id, + sensor->i2c_bus, + pipeline->minfo->shared.paddr); + } else { + + fimc_is_setup_sensor_open_extended(region, sensor); + ret = fimc_is_itf_open_sensor_ext(&is->interface, + pipeline->instance, + sensor->drvdata->id, + pipeline->minfo->shared.paddr); + } + if (ret) { dev_err(pipeline->dev, "Open sensor failed\n"); - goto err_exit; + fimc_is_pipeline_power(pipeline, 0); + goto err_fw; + } + + if (region->shared[MAX_SHARED_COUNT - 1] != MAGIC_NUMBER) { + dev_err(pipeline->dev, "Invalid magic number\n"); + goto err_fw; } /* Load setfile */ @@ -1605,21 +2030,11 @@ int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, if (ret) goto err_exit; - /* Stream off */ - ret = fimc_is_itf_stream_off(&is->interface, pipeline->instance); - if (ret) - goto err_exit; - - /* Process off */ - ret = fimc_is_itf_process_off(&is->interface, pipeline->instance); - if (ret) - goto err_exit; - if (is->num_pipelines == 0) { /* Copy init params to FW region */ - memset(®ion->parameter, 0x0, sizeof(struct is_param_region)); region->parameter.sensor = init_sensor_param; + region->parameter.taa = init_taa_param; region->parameter.isp = init_isp_param; region->parameter.drc = init_drc_param; region->parameter.scalerc = init_scalerc_param; @@ -1628,19 +2043,21 @@ int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, region->parameter.tdnr = init_tdnr_param; region->parameter.scalerp = init_scalerp_param; region->parameter.fd = init_fd_param; + FIMC_IS_FW_CALL_OP(tweak_param, PARAM_ISP_CAM, + ®ion->parameter.taa, NULL); wmb(); /* Set all init params to FW */ - index[0] = 0xffffffff; - index[1] = 0xffffffff; ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, - index[0], index[1]); + ISS_PREVIEW_STILL, FIMC_IS_PARAMS_MASK_ALL, FIMC_IS_PARAMS_MASK_ALL); if (ret) { dev_err(pipeline->dev, "%s failed\n", __func__); return -EINVAL; } } + fimc_is_register_listener(is, &pipeline->listener); + /* Set state to OPEN */ set_bit(PIPELINE_OPEN, &pipeline->state); is->num_pipelines++; @@ -1668,7 +2085,8 @@ int fimc_is_pipeline_close(struct fimc_is_pipeline *pipeline) goto err_exit; } - if (test_bit(PIPELINE_START, &pipeline->state)) { + if (test_bit(PIPELINE_START, &pipeline->state) + && !(test_bit(PIPELINE_RESET, &pipeline->state))) { dev_err(pipeline->dev, "Cannot close pipeline when its started\n"); ret = -EINVAL; goto err_exit; @@ -1689,7 +2107,14 @@ int fimc_is_pipeline_close(struct fimc_is_pipeline *pipeline) fimc_is_pipeline_freemem(pipeline); } + pipeline->config.params[0] = 0; + pipeline->config.params[1] = 0; + pipeline->fcount = 0; + fimc_is_unregister_listener(is, &pipeline->listener); + clear_bit(PIPELINE_OPEN, &pipeline->state); + clear_bit(PIPELINE_PREPARE, &pipeline->state); + clear_bit(PIPELINE_RESET, &pipeline->state); mutex_unlock(&pipeline->pipe_lock); return 0; @@ -1697,3 +2122,40 @@ err_exit: mutex_unlock(&pipeline->pipe_lock); return ret; } + +static void fimc_is_pipeline_wdg_work_fn(struct work_struct *work) +{ + struct fimc_is_pipeline *pipeline = container_of(work, + struct fimc_is_pipeline, wdg_work.work); + if (test_bit(PIPELINE_RUN, &pipeline->state) ){ + /* Check if the IS is responsive */ + struct fimc_is * is = pipeline->is; + if (fimc_is_itf_hw_running(&is->interface)) { + return; + } + clear_bit(PIPELINE_RUN,&pipeline->state); + fimc_is_pipeline_reset(pipeline); + fimc_is_pipeline_shot_cancel(pipeline); + + } +} + +static void fimc_is_pipeline_work_fn(struct work_struct* work) +{ + struct fimc_is_pipeline *pipeline = container_of(work, + struct fimc_is_pipeline, work); + + if (test_bit(PIPELINE_RESET, &pipeline->state)) { + fimc_is_pipeline_stop(pipeline, 1); + } +} + +void fimc_is_pipeline_reset(struct fimc_is_pipeline *pipeline) +{ + cancel_delayed_work(&pipeline->wdg_work); + if (!test_bit(PIPELINE_RESET, &pipeline->state)) { + set_bit(PIPELINE_RESET, &pipeline->state); + queue_work(fimc_is_pipeline_workqueue, &pipeline->work); + } + +} diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.h b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h index b327edd6928..7c75df25534 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-pipeline.h +++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h @@ -16,13 +16,6 @@ #include "fimc-is-isp.h" #include "fimc-is-scaler.h" -#define FIMC_IS_A5_MEM_SIZE 0x00a00000 -#define FIMC_IS_A5_SEN_SIZE 0x00100000 -#define FIMC_IS_REGION_SIZE 0x5000 -#define FIMC_IS_SETFILE_SIZE 0xc0d8 -#define FIMC_IS_TDNR_MEM_SIZE (1920 * 1080 * 4) -#define FIMC_IS_DEBUG_REGION_ADDR 0x00840000 -#define FIMC_IS_SHARED_REGION_ADDR 0x008c0000 #define FIMC_IS_A5_TIMEOUT 1000 #define FIMC_IS_SCP_REGION_INDEX 400 @@ -53,11 +46,22 @@ #define FIMC_IS_MAGIC_NUMBER 0x23456789 +/* + * FIMC IS sub-block mask specifying which sub-blocks + * are available for given SoC. + * @see is_components for sub-blocks identifiers + */ +#define FIMC_IS_EXYNOS5_SUBBLOCKS 0xff +#define FIMC_IS_EXYNOS3250_SUBBLOCKS 0x87 + enum pipeline_state { PIPELINE_INIT, + PIPELINE_POWER, PIPELINE_OPEN, + PIPELINE_PREPARE, PIPELINE_START, PIPELINE_RUN, + PIPELINE_RESET, }; enum is_components { @@ -72,20 +76,29 @@ enum is_components { }; enum component_state { + COMP_INVALID, COMP_ENABLE, COMP_START, COMP_RUN }; +struct fimc_is_config { + unsigned long params[2]; + int metadata_mode; +}; + struct fimc_is_pipeline { unsigned long state; - unsigned long comp_state[FIMC_IS_NUM_COMPS]; + unsigned long subip_state[FIMC_IS_NUM_COMPS]; + unsigned int subip_mask; bool force_down; unsigned int instance; /* Locks the isp / scaler buffers */ spinlock_t slock_buf; unsigned long slock_flags; wait_queue_head_t wait_q; + struct work_struct work; + struct delayed_work wdg_work; /* For locking pipeline ops */ struct mutex pipe_lock; /* For locking scaler ops in pipeline */ @@ -99,10 +112,15 @@ struct fimc_is_pipeline { struct fimc_is_sensor *sensor; struct fimc_is_isp isp; struct fimc_is_scaler scaler[FIMC_IS_NUM_SCALERS]; - + struct fimc_is_config config; + const struct fimc_is_fw_ctrl *fw_ctrl; + unsigned int data_flow; unsigned int fcount; + ktime_t last_frame; + long long frame_duration; unsigned int isp_width; unsigned int isp_height; + struct fimc_is_event_listener listener; }; void fimc_is_pipeline_buf_lock(struct fimc_is_pipeline *pipeline); @@ -120,10 +138,11 @@ int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline); int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon); int fimc_is_pipeline_stop(struct fimc_is_pipeline *pipeline, int streamoff); int fimc_is_pipeline_init(struct fimc_is_pipeline *pipeline, - unsigned int instance, void *is_ctx); + unsigned int instance, + void *is_ctx); int fimc_is_pipeline_destroy(struct fimc_is_pipeline *pipeline); int fimc_is_pipeline_open(struct fimc_is_pipeline *pipeline, struct fimc_is_sensor *sensor); int fimc_is_pipeline_close(struct fimc_is_pipeline *pipeline); - +void fimc_is_pipeline_reset(struct fimc_is_pipeline *pipeline); #endif diff --git a/drivers/media/platform/exynos5-is/fimc-is-regs.h b/drivers/media/platform/exynos5-is/fimc-is-regs.h index 06aa466624f..b29fdf48324 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-regs.h +++ b/drivers/media/platform/exynos5-is/fimc-is-regs.h @@ -13,10 +13,8 @@ #ifndef FIMC_IS_REGS_H #define FIMC_IS_REGS_H -/* WDT_ISP register */ -#define WDT 0x00170000 /* MCUCTL register */ -#define MCUCTL 0x00180000 +#define MCUCTL 0x00 /* MCU Controller Register */ #define MCUCTLR (MCUCTL+0x00) #define MCUCTLR_AXI_ISPX_AWCACHE(x) ((x) << 16) @@ -91,15 +89,23 @@ #define GPICTLR (MCUCTL+0x48) /* IS Shared Registers between ISP CPU and HOST CPU */ -#define ISSR(n) (MCUCTL + 0x80 + (n)) +#define ISSR(n) (MCUCTL + 0x80 + 4*(n)) /* PMU for FIMC-IS*/ -#define PMUREG_CMU_RESET_ISP_SYS_PWR_REG 0x1584 +#define EXYNOS5_PMUREG_CMU_RESET_ISP 0x1584 #define PMUREG_ISP_ARM_CONFIGURATION 0x2280 #define PMUREG_ISP_ARM_STATUS 0x2284 #define PMUREG_ISP_ARM_OPTION 0x2288 +/* Low Power Interface settings */ #define PMUREG_ISP_LOW_POWER_OFF 0x0004 -#define PMUREG_ISP_CONFIGURATION 0x4020 -#define PMUREG_ISP_STATUS 0x4024 +/* EXYNOS3 (EXYNOS3250)*/ +#define EXYNOS3250_PMUREG_CMU_RESET_ISP 0x1174 +#define PMUREG_ISP_CONFIGURATION 0x3ca0 +#define PMUREG_ISP_STATUS 0x3ca4 +#define PMUREG_ISP_ARM_SYS_PWR_REG 0x1050 +#define PMUREG_CMU_SYSCLK_ISP_SYS_PWR_REG 0x13b8 + +#define PA_FIMC_IS_GIC_C 0x121E0000 +#define PA_FIMC_IS_GIC_D 0x121F0000 #endif diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.c b/drivers/media/platform/exynos5-is/fimc-is-sensor.c index 2ad648ef508..7c370b36324 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-sensor.c +++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.c @@ -13,20 +13,23 @@ static const struct sensor_drv_data s5k6a3_drvdata = { .id = FIMC_IS_SENSOR_ID_S5K6A3, - .open_timeout = S5K6A3_OPEN_TIMEOUT, .setfile_name = "exynos5_s5k6a3_setfile.bin", + .pixel_width = S5K6A3_SENSOR_WIDTH, + .pixel_height = S5K6A3_SENSOR_HEIGHT, }; static const struct sensor_drv_data s5k4e5_drvdata = { .id = FIMC_IS_SENSOR_ID_S5K4E5, - .open_timeout = S5K4E5_OPEN_TIMEOUT, .setfile_name = "exynos5_s5k4e5_setfile.bin", + .pixel_width = S5K4E5_SENSOR_WIDTH, + .pixel_height = S5K4E5_SENSOR_HEIGHT, }; static const struct sensor_drv_data s5k8b1_drvdata = { .id = FIMC_IS_SENSOR_ID_S5K8B1, - .open_timeout = S5K8B1_OPEN_TIMEOUT, - .setfile_name = "exynos5_s5k8b1_setfile.bin", + .setfile_name = "exynos3_s5k8b1_setfile.bin", + .pixel_width = S5K8B1_SENSOR_WIDTH, + .pixel_height = S5K8B1_SENSOR_HEIGHT, }; static const struct of_device_id fimc_is_sensor_of_ids[] = { diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.h b/drivers/media/platform/exynos5-is/fimc-is-sensor.h index 06709b85615..936aec3b73e 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-sensor.h +++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.h @@ -1,7 +1,7 @@ /* - * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * Samsung EXYNOS5 & EXYNOS3 FIMC-IS (Imaging Subsystem) driver * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd. * Author: Arun Kumar K <arun.kk@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -14,15 +14,17 @@ #include <linux/of.h> #include <linux/types.h> -#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */ +#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */ + #define S5K6A3_SENSOR_WIDTH 1392 #define S5K6A3_SENSOR_HEIGHT 1392 -#define S5K4E5_OPEN_TIMEOUT 2000 /* ms */ #define S5K4E5_SENSOR_WIDTH 2560 #define S5K4E5_SENSOR_HEIGHT 1920 -#define S5K8B1_OPEN_TIMEOUT 2000 /* ms */ +/* S5K8B1 sensor */ +#define S5K8B1_SENSOR_WIDTH 1936 +#define S5K8B1_SENSOR_HEIGHT 1096 #define SENSOR_WIDTH_PADDING 16 #define SENSOR_HEIGHT_PADDING 10 @@ -41,14 +43,16 @@ enum fimc_is_sensor_id { struct sensor_drv_data { enum fimc_is_sensor_id id; /* sensor open timeout in ms */ - unsigned short open_timeout; char *setfile_name; + unsigned int pixel_width; + unsigned int pixel_height; }; /** * struct fimc_is_sensor - fimc-is sensor data structure * @drvdata: a pointer to the sensor's parameters data structure * @i2c_bus: ISP I2C bus index (0...1) + * @i2c_slave_addr: I2C slave address * @width: sensor active width * @height: sensor active height * @pixel_width: sensor effective pixel width (width + padding) @@ -57,6 +61,7 @@ struct sensor_drv_data { struct fimc_is_sensor { const struct sensor_drv_data *drvdata; unsigned int i2c_bus; + unsigned int i2c_slave_addr; unsigned int width; unsigned int height; unsigned int pixel_width; diff --git a/drivers/media/platform/exynos5-is/fimc-is.h b/drivers/media/platform/exynos5-is/fimc-is.h index 980c349ed64..2d654f7e4ea 100644 --- a/drivers/media/platform/exynos5-is/fimc-is.h +++ b/drivers/media/platform/exynos5-is/fimc-is.h @@ -42,6 +42,10 @@ */ #define fimc_is_sensor_get_sd(is, sid) (&is->sensor[sid].subdev) +/* + * Macro to retrieve FIMC IS sub IP data + */ +#define fimc_is_get_subip(is, subip) (&(is->drvdata->subip_data->_##subip)) /** * struct fimc_is - fimc-is driver private data @@ -62,7 +66,7 @@ struct fimc_is { struct fimc_md *md; struct vb2_alloc_ctx *alloc_ctx; - struct clk *clock[IS_CLK_MAX_NUM]; + struct clk *clocks[IS_CLKS_MAX]; void __iomem *pmu_regs; unsigned int num_pipelines; @@ -72,6 +76,9 @@ struct fimc_is { struct fimc_is_sensor sensor[FIMC_IS_NUM_SENSORS]; struct fimc_is_pipeline pipeline[FIMC_IS_NUM_PIPELINES]; struct fimc_is_interface interface; + /* To protect the listeners list */ + spinlock_t events_lock; + struct list_head event_listeners; }; /* Queue operations for ISP */ @@ -148,6 +155,45 @@ static inline struct fimc_is_buf *fimc_is_scaler_run_queue_get( return buf; } +/* + * Simplified events handling scheme: allows any of the FIMC-IS + * sub-devices/components to register as an event listener. + * Once registration has been performed the custom event handler + * will get triggered whenever FIMC-IS gets notified on a particular event. + * This notification is handled through FIMC IS media device driver. + */ +static inline void fimc_is_register_listener(struct fimc_is* is, + struct fimc_is_event_listener *listener) +{ + unsigned long flags; + + spin_lock_irqsave(&is->events_lock, flags); + list_add_tail(&listener->link, &is->event_listeners); + spin_unlock_irqrestore(&is->events_lock, flags); +} + +static inline void fimc_is_unregister_listener(struct fimc_is *is, + struct fimc_is_event_listener *listener) +{ + unsigned long flags; + + spin_lock_irqsave(&is->events_lock, flags); + list_del(&listener->link); + spin_unlock_irqrestore(&is->events_lock, flags); +} + +static inline void fimc_is_dispatch_events(struct fimc_is *is, unsigned int event_id, + void *arg) +{ + unsigned long flags; + struct fimc_is_event_listener *listener; + + spin_lock_irqsave(&is->events_lock, flags); + list_for_each_entry(listener, &is->event_listeners, link) + listener->event_handler(listener, event_id, arg); + spin_unlock_irqrestore(&is->events_lock, flags); +} + static inline void pmu_is_write(u32 v, struct fimc_is *is, unsigned int offset) { writel(v, is->pmu_regs + offset); |