diff options
Diffstat (limited to 'drivers/media')
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); |