summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/platform/exynos5-is/Makefile2
-rw-r--r--drivers/media/platform/exynos5-is/exynos5-mdev.c62
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-backend.c301
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-backend.h212
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-cmd.h134
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.c455
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.h46
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-fw.c500
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-fw.h73
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-interface.c557
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-interface.h47
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.c29
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.h10
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-metadata.h513
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-param.h142
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.c1164
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.h41
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-regs.h20
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-sensor.c11
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-sensor.h17
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is.h48
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&reg_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, &regs_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*)&regs_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),
+ &reg_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,
+ &params->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,
+ &params->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(&region->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,
+ &region->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);