--- /dev/null
+/*
+ * Samsung Exynos5 SoC series Sensor driver
+ *
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_exynos_camera.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <exynos-fimc-is-sensor.h>
+#include "fimc-is-hw.h"
+#include "fimc-is-core.h"
+#include "fimc-is-param.h"
+#include "fimc-is-device-sensor.h"
+#include "fimc-is-device-sensor-peri.h"
+#include "fimc-is-resourcemgr.h"
+#include "fimc-is-dt.h"
+#include "fimc-is-cis-2t7sx.h"
+#include "fimc-is-cis-2t7sx-setA.h"
+#include "fimc-is-cis-2t7sx-setB.h"
+
+#include "fimc-is-helper-i2c.h"
+
+#define SENSOR_NAME "S5K2T7SX"
+/* #define DEBUG_2T7SX_PLL */
+
+static const struct v4l2_subdev_ops subdev_ops;
+
+static const u32 *sensor_2t7sx_global;
+static u32 sensor_2t7sx_global_size;
+static const u32 **sensor_2t7sx_setfiles;
+static const u32 *sensor_2t7sx_setfile_sizes;
+static const struct sensor_pll_info_compact **sensor_2t7sx_pllinfos;
+static u32 sensor_2t7sx_max_setfile_num;
+
+static void sensor_2t7sx_cis_data_calculation(const struct sensor_pll_info_compact *pll_info_compact, cis_shared_data *cis_data)
+{
+ u32 vt_pix_clk_hz = 0;
+ u32 frame_rate = 0, max_fps = 0, frame_valid_us = 0;
+
+ BUG_ON(!pll_info_compact);
+
+ /* 1. get pclk value from pll info */
+ vt_pix_clk_hz = pll_info_compact->pclk;
+
+ dbg_sensor(1, "ext_clock(%d), mipi_datarate(%d), pclk(%d)\n",
+ pll_info_compact->ext_clk, pll_info_compact->mipi_datarate, pll_info_compact->pclk);
+
+ /* 2. the time of processing one frame calculation (us) */
+ cis_data->min_frame_us_time = (pll_info_compact->frame_length_lines * pll_info_compact->line_length_pck
+ / (vt_pix_clk_hz / (1000 * 1000)));
+ cis_data->cur_frame_us_time = cis_data->min_frame_us_time;
+
+ /* 3. FPS calculation */
+ frame_rate = vt_pix_clk_hz / (pll_info_compact->frame_length_lines * pll_info_compact->line_length_pck);
+ dbg_sensor(1, "frame_rate (%d) = vt_pix_clk_hz(%d) / "
+ KERN_CONT "(pll_info_compact->frame_length_lines(%d) * pll_info_compact->line_length_pck(%d))\n",
+ frame_rate, vt_pix_clk_hz, pll_info_compact->frame_length_lines, pll_info_compact->line_length_pck);
+
+ /* calculate max fps */
+ max_fps = (vt_pix_clk_hz * 10) / (pll_info_compact->frame_length_lines * pll_info_compact->line_length_pck);
+ max_fps = (max_fps % 10 >= 5 ? frame_rate + 1 : frame_rate);
+
+ cis_data->pclk = vt_pix_clk_hz;
+ cis_data->max_fps = max_fps;
+ cis_data->frame_length_lines = pll_info_compact->frame_length_lines;
+ cis_data->line_length_pck = pll_info_compact->line_length_pck;
+ cis_data->line_readOut_time = sensor_cis_do_div64((u64)cis_data->line_length_pck * (u64)(1000 * 1000 * 1000), cis_data->pclk);
+ cis_data->rolling_shutter_skew = (cis_data->cur_height - 1) * cis_data->line_readOut_time;
+ cis_data->stream_on = false;
+
+ /* Frame valid time calcuration */
+ frame_valid_us = sensor_cis_do_div64((u64)cis_data->cur_height * (u64)cis_data->line_length_pck * (u64)(1000 * 1000), cis_data->pclk);
+ cis_data->frame_valid_us_time = (int)frame_valid_us;
+
+ dbg_sensor(1, "%s\n", __func__);
+ dbg_sensor(1, "Sensor size(%d x %d) setting: SUCCESS!\n",
+ cis_data->cur_width, cis_data->cur_height);
+ dbg_sensor(1, "Frame Valid(us): %d\n", frame_valid_us);
+ dbg_sensor(1, "rolling_shutter_skew: %lld\n", cis_data->rolling_shutter_skew);
+
+ dbg_sensor(1, "Fps: %d, max fps(%d)\n", frame_rate, cis_data->max_fps);
+ dbg_sensor(1, "min_frame_time(%d us)\n", cis_data->min_frame_us_time);
+ dbg_sensor(1, "Pixel rate(Mbps): %d\n", cis_data->pclk / 1000000);
+
+ /* Frame period calculation */
+ cis_data->frame_time = (cis_data->line_readOut_time * cis_data->cur_height / 1000);
+ cis_data->rolling_shutter_skew = (cis_data->cur_height - 1) * cis_data->line_readOut_time;
+
+ dbg_sensor(1, "[%s] frame_time(%d), rolling_shutter_skew(%lld)\n", __func__,
+ cis_data->frame_time, cis_data->rolling_shutter_skew);
+
+ /* Constant values */
+ cis_data->min_fine_integration_time = SENSOR_2T7SX_FINE_INTEGRATION_TIME_MIN;
+ cis_data->max_fine_integration_time = SENSOR_2T7SX_FINE_INTEGRATION_TIME_MAX;
+ cis_data->min_coarse_integration_time = SENSOR_2T7SX_COARSE_INTEGRATION_TIME_MIN;
+ cis_data->max_margin_coarse_integration_time = SENSOR_2T7SX_COARSE_INTEGRATION_TIME_MAX_MARGIN;
+}
+
+static int sensor_2t7sx_wait_stream_off_status(cis_shared_data *cis_data)
+{
+ int ret = 0;
+ u32 timeout = 0;
+
+ BUG_ON(!cis_data);
+
+#define STREAM_OFF_WAIT_TIME 250
+ while (timeout < STREAM_OFF_WAIT_TIME) {
+ if (cis_data->is_active_area == false &&
+ cis_data->stream_on == false) {
+ pr_debug("actual stream off\n");
+ break;
+ }
+ timeout++;
+ }
+
+ if (timeout == STREAM_OFF_WAIT_TIME) {
+ pr_err("actual stream off wait timeout\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* CIS OPS */
+int sensor_2t7sx_cis_init(struct v4l2_subdev *subdev)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ u32 setfile_index = 0;
+ cis_setting_info setinfo;
+
+ setinfo.return_value = 0;
+
+ setinfo.param = NULL;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+ if (!cis) {
+ err("cis is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ BUG_ON(!cis->cis_data);
+ memset(cis->cis_data, 0, sizeof(cis_shared_data));
+ cis->rev_flag = false;
+
+ ret = sensor_cis_check_rev(cis);
+ if (ret < 0) {
+ warn("sensor_2t7sx_check_rev is fail when cis init");
+ cis->rev_flag = true;
+ ret = 0;
+ }
+
+ cis->cis_data->cur_width = SENSOR_2T7SX_MAX_WIDTH;
+ cis->cis_data->cur_height = SENSOR_2T7SX_MAX_HEIGHT;
+ cis->cis_data->low_expo_start = 33000;
+ cis->need_mode_change = false;
+
+ sensor_2t7sx_cis_data_calculation(sensor_2t7sx_pllinfos[setfile_index], cis->cis_data);
+
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_min_exposure_time, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] min exposure time : %d\n", __func__, setinfo.return_value);
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_max_exposure_time, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] max exposure time : %d\n", __func__, setinfo.return_value);
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_min_analog_gain, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] min again : %d\n", __func__, setinfo.return_value);
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_max_analog_gain, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] max again : %d\n", __func__, setinfo.return_value);
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_min_digital_gain, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] min dgain : %d\n", __func__, setinfo.return_value);
+ setinfo.return_value = 0;
+ CALL_CISOPS(cis, cis_get_max_digital_gain, subdev, &setinfo.return_value);
+ dbg_sensor(1, "[%s] max dgain : %d\n", __func__, setinfo.return_value);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_log_status(struct v4l2_subdev *subdev)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client = NULL;
+ u8 data8 = 0;
+ u16 data16 = 0;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+ if (!cis) {
+ err("cis is NULL");
+ ret = -ENODEV;
+ goto p_err;
+ }
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -ENODEV;
+ goto p_err;
+ }
+
+ pr_err("[SEN:DUMP] *******************************\n");
+ fimc_is_sensor_read16(client, 0x0000, &data16);
+ pr_err("[SEN:DUMP] model_id(%x)\n", data16);
+ fimc_is_sensor_read8(client, 0x0002, &data8);
+ pr_err("[SEN:DUMP] revision_number(%x)\n", data8);
+ fimc_is_sensor_read8(client, 0x0005, &data8);
+ pr_err("[SEN:DUMP] frame_count(%x)\n", data8);
+ fimc_is_sensor_read8(client, 0x0100, &data8);
+ pr_err("[SEN:DUMP] mode_select(%x)\n", data8);
+
+ sensor_cis_dump_registers(subdev, sensor_2t7sx_setfiles[0], sensor_2t7sx_setfile_sizes[0]);
+
+ pr_err("[SEN:DUMP] *******************************\n");
+
+p_err:
+ return ret;
+}
+
+#if USE_GROUP_PARAM_HOLD
+static int sensor_2t7sx_cis_group_param_hold_func(struct v4l2_subdev *subdev, unsigned int hold)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis = NULL;
+ struct i2c_client *client = NULL;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ if (hold == cis->cis_data->group_param_hold) {
+ pr_debug("already group_param_hold (%d)\n", cis->cis_data->group_param_hold);
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_write8(client, 0x0104, hold);
+ if (ret < 0)
+ goto p_err;
+
+ cis->cis_data->group_param_hold = hold;
+ ret = 1;
+p_err:
+ return ret;
+}
+#else
+static inline int sensor_2t7sx_cis_group_param_hold_func(struct v4l2_subdev *subdev, unsigned int hold)
+{ return 0; }
+#endif
+
+/* Input
+ * hold : true - hold, flase - no hold
+ * Output
+ * return: 0 - no effect(already hold or no hold)
+ * positive - setted by request
+ * negative - ERROR value
+ */
+int sensor_2t7sx_cis_group_param_hold(struct v4l2_subdev *subdev, bool hold)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis = NULL;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ ret = sensor_2t7sx_cis_group_param_hold_func(subdev, hold);
+ if (ret < 0)
+ goto p_err;
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_global_setting(struct v4l2_subdev *subdev)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis = NULL;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+ BUG_ON(!cis);
+
+ ret = sensor_cis_set_registers(subdev, sensor_2t7sx_global, sensor_2t7sx_global_size);
+
+ if (ret < 0) {
+ err("sensor_3p8sp_set_registers fail!!");
+ goto p_err;
+ }
+
+ dbg_sensor(1, "[%s] global setting done\n", __func__);
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_mode_change(struct v4l2_subdev *subdev, u32 mode)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis = NULL;
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ if (mode > sensor_2t7sx_max_setfile_num) {
+ err("invalid mode(%d)!!", mode);
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ /* If check_rev fail when cis_init, one more check_rev in mode_change */
+ if (cis->rev_flag == true) {
+ cis->rev_flag = false;
+ ret = sensor_cis_check_rev(cis);
+ if (ret < 0) {
+ err("sensor_2t7sx_check_rev is fail");
+ goto p_err;
+ }
+ }
+
+ sensor_2t7sx_cis_data_calculation(sensor_2t7sx_pllinfos[mode], cis->cis_data);
+
+ ret = sensor_cis_set_registers(subdev, sensor_2t7sx_setfiles[mode], sensor_2t7sx_setfile_sizes[mode]);
+ if (ret < 0) {
+ err("sensor_2t7sx_set_registers fail!!");
+ goto p_err;
+ }
+
+ cis->cis_data->frame_time = (cis->cis_data->line_readOut_time * cis->cis_data->cur_height / 1000);
+ cis->cis_data->rolling_shutter_skew = (cis->cis_data->cur_height - 1) * cis->cis_data->line_readOut_time;
+ dbg_sensor(1, "[%s] frame_time(%d), rolling_shutter_skew(%lld)\n", __func__,
+ cis->cis_data->frame_time, cis->cis_data->rolling_shutter_skew);
+
+ dbg_sensor(1, "[%s] mode changed(%d)\n", __func__, mode);
+
+p_err:
+ return ret;
+}
+
+/* TODO: Sensor set size sequence(sensor done, sensor stop, 3AA done in FW case */
+int sensor_2t7sx_cis_set_size(struct v4l2_subdev *subdev, cis_shared_data *cis_data)
+{
+ int ret = 0;
+ bool binning = false;
+ u32 ratio_w = 0, ratio_h = 0, start_x = 0, start_y = 0, end_x = 0, end_y = 0;
+ u32 even_x = 0, odd_x = 0, even_y = 0, odd_y = 0;
+ struct i2c_client *client = NULL;
+ struct fimc_is_cis *cis = NULL;
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+ BUG_ON(!cis);
+
+ dbg_sensor(1, "[MOD:D:%d] %s\n", cis->id, __func__);
+
+ if (unlikely(!cis_data)) {
+ err("cis data is NULL");
+ if (unlikely(!cis->cis_data)) {
+ ret = -EINVAL;
+ goto p_err;
+ } else {
+ cis_data = cis->cis_data;
+ }
+ }
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ /* Wait actual stream off */
+ ret = sensor_2t7sx_wait_stream_off_status(cis_data);
+ if (ret) {
+ err("Must stream off\n");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ binning = cis_data->binning;
+ if (binning) {
+ ratio_w = (SENSOR_2T7SX_MAX_WIDTH / cis_data->cur_width);
+ ratio_h = (SENSOR_2T7SX_MAX_HEIGHT / cis_data->cur_height);
+ } else {
+ ratio_w = 1;
+ ratio_h = 1;
+ }
+
+ if (((cis_data->cur_width * ratio_w) > SENSOR_2T7SX_MAX_WIDTH) ||
+ ((cis_data->cur_height * ratio_h) > SENSOR_2T7SX_MAX_HEIGHT)) {
+ err("Config max sensor size over~!!\n");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ /* 1. page_select */
+ ret = fimc_is_sensor_write16(client, 0x6028, 0x2000);
+ if (ret < 0)
+ goto p_err;
+
+ /* 2. pixel address region setting */
+ start_x = ((SENSOR_2T7SX_MAX_WIDTH - cis_data->cur_width * ratio_w) / 2) & (~0x1);
+ start_y = ((SENSOR_2T7SX_MAX_HEIGHT - cis_data->cur_height * ratio_h) / 2) & (~0x1);
+ end_x = start_x + (cis_data->cur_width * ratio_w - 1);
+ end_y = start_y + (cis_data->cur_height * ratio_h - 1);
+
+ if (!(end_x & (0x1)) || !(end_y & (0x1))) {
+ err("Sensor pixel end address must odd\n");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_write16(client, 0x0344, start_x);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x0346, start_y);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x0348, end_x);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x034A, end_y);
+ if (ret < 0)
+ goto p_err;
+
+ /* 3. output address setting */
+ ret = fimc_is_sensor_write16(client, 0x034C, cis_data->cur_width);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x034E, cis_data->cur_height);
+ if (ret < 0)
+ goto p_err;
+
+ /* If not use to binning, sensor image should set only crop */
+ if (!binning) {
+ dbg_sensor(1, "Sensor size set is not binning\n");
+ goto p_err;
+ }
+
+ /* 4. sub sampling setting */
+ even_x = 1; /* 1: not use to even sampling */
+ even_y = 1;
+ odd_x = (ratio_w * 2) - even_x;
+ odd_y = (ratio_h * 2) - even_y;
+
+ ret = fimc_is_sensor_write16(client, 0x0380, even_x);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x0382, odd_x);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x0384, even_y);
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write16(client, 0x0386, odd_y);
+ if (ret < 0)
+ goto p_err;
+
+ /* 5. binnig setting */
+ ret = fimc_is_sensor_write8(client, 0x0900, binning); /* 1: binning enable, 0: disable */
+ if (ret < 0)
+ goto p_err;
+ ret = fimc_is_sensor_write8(client, 0x0901, (ratio_w << 4) | ratio_h);
+ if (ret < 0)
+ goto p_err;
+
+ /* 6. scaling setting: but not use */
+ /* scaling_mode (0: No scaling, 1: Horizontal, 2: Full) */
+ ret = fimc_is_sensor_write16(client, 0x0400, 0x0000);
+ if (ret < 0)
+ goto p_err;
+ /* down_scale_m: 1 to 16 upwards (scale_n: 16(fixed)) */
+ /* down scale factor = down_scale_m / down_scale_n */
+ ret = fimc_is_sensor_write16(client, 0x0404, 0x0010);
+ if (ret < 0)
+ goto p_err;
+
+ cis_data->frame_time = (cis_data->line_readOut_time * cis_data->cur_height / 1000);
+ cis->cis_data->rolling_shutter_skew = (cis->cis_data->cur_height - 1) * cis->cis_data->line_readOut_time;
+ dbg_sensor(1, "[%s] frame_time(%d), rolling_shutter_skew(%lld)\n", __func__,
+ cis->cis_data->frame_time, cis->cis_data->rolling_shutter_skew);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec) * 1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_stream_on(struct v4l2_subdev *subdev)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ dbg_sensor(1, "[MOD:D:%d] %s\n", cis->id, __func__);
+
+ ret = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (ret < 0)
+ err("group_param_hold_func failed at stream on");
+
+#ifdef DEBUG_2T7SX_PLL
+ {
+ u16 pll;
+
+ fimc_is_sensor_read16(client, 0x0300, &pll);
+ dbg_sensor(1, "______ vt_pix_clk_div(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0302, &pll);
+ dbg_sensor(1, "______ vt_sys_clk_div(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0304, &pll);
+ dbg_sensor(1, "______ pre_pll_clk_div(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0306, &pll);
+ dbg_sensor(1, "______ pll_multiplier(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0308, &pll);
+ dbg_sensor(1, "______ op_pix_clk_div(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x030a, &pll);
+ dbg_sensor(1, "______ op_sys_clk_div(%x)\n", pll);
+
+ fimc_is_sensor_read16(client, 0x030c, &pll);
+ dbg_sensor(1, "______ secnd_pre_pll_clk_div(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x030e, &pll);
+ dbg_sensor(1, "______ secnd_pll_multiplier(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0340, &pll);
+ dbg_sensor(1, "______ frame_length_lines(%x)\n", pll);
+ fimc_is_sensor_read16(client, 0x0342, &pll);
+ dbg_sensor(1, "______ line_length_pck(%x)\n", pll);
+ }
+#endif
+
+ /* Sensor stream on */
+ fimc_is_sensor_write16(client, 0x6028, 0x4000);
+ fimc_is_sensor_write8(client, 0x0100, 0x01);
+
+ /* WDR */
+ if (fimc_is_vender_wdr_mode_on(cis_data))
+ fimc_is_sensor_write8(client, 0x0216, 0x01);
+ else
+ fimc_is_sensor_write8(client, 0x0216, 0x00);
+
+ cis_data->stream_on = true;
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_stream_off(struct v4l2_subdev *subdev)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ dbg_sensor(1, "[MOD:D:%d] %s\n", cis->id, __func__);
+
+ ret = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (ret < 0)
+ err("group_param_hold_func failed at stream off");
+
+ /* Sensor stream off */
+ fimc_is_sensor_write16(client, 0x6028, 0x4000);
+ fimc_is_sensor_write8(client, 0x0100, 0x00);
+
+ cis_data->stream_on = false;
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_exposure_time(struct v4l2_subdev *subdev, struct ae_param *target_exposure)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u32 vt_pic_clk_freq_mhz = 0;
+ u16 long_coarse_int = 0;
+ u16 short_coarse_int = 0;
+ u32 line_length_pck = 0;
+ u32 min_fine_int = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!target_exposure);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ if ((target_exposure->long_val <= 0) || (target_exposure->short_val <= 0)) {
+ err("[%s] invalid target exposure(%d, %d)\n", __func__,
+ target_exposure->long_val, target_exposure->short_val);
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), target long(%d), short(%d)\n", cis->id, __func__,
+ cis_data->sen_vsync_count, target_exposure->long_val, target_exposure->short_val);
+
+ vt_pic_clk_freq_mhz = cis_data->pclk / (1000 * 1000);
+ line_length_pck = cis_data->line_length_pck;
+ min_fine_int = cis_data->min_fine_integration_time;
+
+ long_coarse_int = ((target_exposure->long_val * vt_pic_clk_freq_mhz) - min_fine_int) / line_length_pck;
+ short_coarse_int = ((target_exposure->short_val * vt_pic_clk_freq_mhz) - min_fine_int) / line_length_pck;
+
+ if (long_coarse_int > cis_data->max_coarse_integration_time) {
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), long coarse(%d) max(%d)\n", cis->id, __func__,
+ cis_data->sen_vsync_count, long_coarse_int, cis_data->max_coarse_integration_time);
+ long_coarse_int = cis_data->max_coarse_integration_time;
+ }
+
+ if (short_coarse_int > cis_data->max_coarse_integration_time) {
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), short coarse(%d) max(%d)\n", cis->id, __func__,
+ cis_data->sen_vsync_count, short_coarse_int, cis_data->max_coarse_integration_time);
+ short_coarse_int = cis_data->max_coarse_integration_time;
+ }
+
+ if (long_coarse_int < cis_data->min_coarse_integration_time) {
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), long coarse(%d) min(%d)\n", cis->id, __func__,
+ cis_data->sen_vsync_count, long_coarse_int, cis_data->min_coarse_integration_time);
+ long_coarse_int = cis_data->min_coarse_integration_time;
+ }
+
+ if (short_coarse_int < cis_data->min_coarse_integration_time) {
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), short coarse(%d) min(%d)\n", cis->id, __func__,
+ cis_data->sen_vsync_count, short_coarse_int, cis_data->min_coarse_integration_time);
+ short_coarse_int = cis_data->min_coarse_integration_time;
+ }
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ /* Short exposure */
+ ret = fimc_is_sensor_write16(client, 0x0202, short_coarse_int);
+ if (ret < 0)
+ goto p_err;
+
+ /* Long exposure */
+ if (fimc_is_vender_wdr_mode_on(cis_data)) {
+ ret = fimc_is_sensor_write16(client, 0x021E, long_coarse_int);
+ if (ret < 0)
+ goto p_err;
+ }
+
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), vt_pic_clk_freq_mhz (%d), line_length_pck(%d), min_fine_int (%d)\n",
+ cis->id, __func__, cis_data->sen_vsync_count, vt_pic_clk_freq_mhz, line_length_pck, min_fine_int);
+ dbg_sensor(1, "[MOD:D:%d] %s, vsync_cnt(%d), frame_length_lines(%#x), long_coarse_int %#x, short_coarse_int %#x\n",
+ cis->id, __func__, cis_data->sen_vsync_count, cis_data->frame_length_lines,
+ long_coarse_int, short_coarse_int);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_min_exposure_time(struct v4l2_subdev *subdev, u32 *min_expo)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis = NULL;
+ cis_shared_data *cis_data = NULL;
+ u32 min_integration_time = 0;
+ u32 min_coarse = 0;
+ u32 min_fine = 0;
+ u32 vt_pic_clk_freq_mhz = 0;
+ u32 line_length_pck = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!min_expo);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ cis_data = cis->cis_data;
+
+ vt_pic_clk_freq_mhz = cis_data->pclk / (1000 * 1000);
+ if (vt_pic_clk_freq_mhz == 0) {
+ pr_err("[MOD:D:%d] %s, Invalid vt_pic_clk_freq_mhz(%d)\n", cis->id, __func__, vt_pic_clk_freq_mhz);
+ goto p_err;
+ }
+ line_length_pck = cis_data->line_length_pck;
+ min_coarse = cis_data->min_coarse_integration_time;
+ min_fine = cis_data->min_fine_integration_time;
+
+ min_integration_time = ((line_length_pck * min_coarse) + min_fine) / vt_pic_clk_freq_mhz;
+ *min_expo = min_integration_time;
+
+ dbg_sensor(1, "[%s] min integration time %d\n", __func__, min_integration_time);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_max_exposure_time(struct v4l2_subdev *subdev, u32 *max_expo)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ cis_shared_data *cis_data;
+ u32 max_integration_time = 0;
+ u32 max_coarse_margin = 0;
+ u32 max_fine_margin = 0;
+ u32 max_coarse = 0;
+ u32 max_fine = 0;
+ u32 vt_pic_clk_freq_mhz = 0;
+ u32 line_length_pck = 0;
+ u32 frame_length_lines = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!max_expo);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ cis_data = cis->cis_data;
+
+ vt_pic_clk_freq_mhz = cis_data->pclk / (1000 * 1000);
+ if (vt_pic_clk_freq_mhz == 0) {
+ pr_err("[MOD:D:%d] %s, Invalid vt_pic_clk_freq_mhz(%d)\n", cis->id, __func__, vt_pic_clk_freq_mhz);
+ goto p_err;
+ }
+ line_length_pck = cis_data->line_length_pck;
+ frame_length_lines = cis_data->frame_length_lines;
+
+ max_coarse_margin = cis_data->max_margin_coarse_integration_time;
+ max_fine_margin = line_length_pck - cis_data->min_fine_integration_time;
+ max_coarse = frame_length_lines - max_coarse_margin;
+ max_fine = cis_data->max_fine_integration_time;
+
+ max_integration_time = ((line_length_pck * max_coarse) + max_fine) / vt_pic_clk_freq_mhz;
+
+ *max_expo = max_integration_time;
+
+ /* TODO: Is this values update hear? */
+ cis_data->max_margin_fine_integration_time = max_fine_margin;
+ cis_data->max_coarse_integration_time = max_coarse;
+
+ dbg_sensor(1, "[%s] max integration time %d, max margin fine integration %d, max coarse integration %d\n",
+ __func__, max_integration_time, cis_data->max_margin_fine_integration_time,
+ cis_data->max_coarse_integration_time);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_adjust_frame_duration(struct v4l2_subdev *subdev,
+ u32 input_exposure_time,
+ u32 *target_duration)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ cis_shared_data *cis_data;
+
+ u32 vt_pic_clk_freq_mhz = 0;
+ u32 line_length_pck = 0;
+ u32 frame_length_lines = 0;
+ u32 frame_duration = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!target_duration);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ cis_data = cis->cis_data;
+
+ vt_pic_clk_freq_mhz = cis_data->pclk / (1000 * 1000);
+ line_length_pck = cis_data->line_length_pck;
+ frame_length_lines = ((vt_pic_clk_freq_mhz * input_exposure_time) / line_length_pck);
+ frame_length_lines += cis_data->max_margin_coarse_integration_time;
+
+ frame_duration = (frame_length_lines * line_length_pck) / vt_pic_clk_freq_mhz;
+
+ dbg_sensor(1, "[%s](vsync cnt = %d) input exp(%d), adj duration, frame duraion(%d), min_frame_us(%d)\n",
+ __func__, cis_data->sen_vsync_count, input_exposure_time,
+ frame_duration, cis_data->min_frame_us_time);
+ dbg_sensor(1, "[%s](vsync cnt = %d) adj duration, frame duraion(%d), min_frame_us(%d)\n",
+ __func__, cis_data->sen_vsync_count, frame_duration, cis_data->min_frame_us_time);
+
+ *target_duration = MAX(frame_duration, cis_data->min_frame_us_time);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_frame_duration(struct v4l2_subdev *subdev, u32 frame_duration)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u32 vt_pic_clk_freq_mhz = 0;
+ u32 line_length_pck = 0;
+ u16 frame_length_lines = 0;
+
+ u32 max_coarse_integration_time = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ if (frame_duration < cis_data->min_frame_us_time) {
+ dbg_sensor(1, "frame duration is less than min(%d)\n", frame_duration);
+ frame_duration = cis_data->min_frame_us_time;
+ }
+
+ vt_pic_clk_freq_mhz = cis_data->pclk / (1000 * 1000);
+ line_length_pck = cis_data->line_length_pck;
+
+ frame_length_lines = (u16)((vt_pic_clk_freq_mhz * frame_duration) / line_length_pck);
+
+ dbg_sensor(1, "[MOD:D:%d] %s, vt_pic_clk_freq_mhz(%#x) frame_duration = %d us, "
+ KERN_CONT"(line_length_pck%#x), frame_length_lines(%#x)\n",
+ cis->id, __func__, vt_pic_clk_freq_mhz, frame_duration, line_length_pck, frame_length_lines);
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_write16(client, 0x0340, frame_length_lines);
+ if (ret < 0)
+ goto p_err;
+
+ cis_data->cur_frame_us_time = frame_duration;
+ cis_data->frame_length_lines = frame_length_lines;
+
+ max_coarse_integration_time = cis_data->frame_length_lines - cis_data->max_margin_coarse_integration_time;
+ cis_data->max_coarse_integration_time = max_coarse_integration_time;
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_frame_rate(struct v4l2_subdev *subdev, u32 min_fps)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ cis_shared_data *cis_data;
+
+ u32 frame_duration = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ cis_data = cis->cis_data;
+
+ if (min_fps > cis_data->max_fps) {
+ err("[MOD:D:%d] %s, request FPS is too high(%d), set to max(%d)\n",
+ cis->id, __func__, min_fps, cis_data->max_fps);
+ min_fps = cis_data->max_fps;
+ }
+
+ if (min_fps == 0) {
+ err("[MOD:D:%d] %s, request FPS is 0, set to min FPS(1)\n",
+ cis->id, __func__);
+ min_fps = 1;
+ }
+
+ frame_duration = (1 * 1000 * 1000) / min_fps;
+
+ dbg_sensor(1, "[MOD:D:%d] %s, set FPS(%d), frame duration(%d)\n",
+ cis->id, __func__, min_fps, frame_duration);
+
+ ret = sensor_2t7sx_cis_set_frame_duration(subdev, frame_duration);
+ if (ret < 0) {
+ err("[MOD:D:%d] %s, set frame duration is fail(%d)\n",
+ cis->id, __func__, ret);
+ goto p_err;
+ }
+
+ cis_data->min_frame_us_time = frame_duration;
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_adjust_analog_gain(struct v4l2_subdev *subdev, u32 input_again, u32 *target_permile)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ cis_shared_data *cis_data;
+
+ u32 again_code = 0;
+ u32 again_permile = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!target_permile);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ cis_data = cis->cis_data;
+
+ again_code = sensor_cis_calc_again_code(input_again);
+
+ if (again_code > cis_data->max_analog_gain[0])
+ again_code = cis_data->max_analog_gain[0];
+ else if (again_code < cis_data->min_analog_gain[0])
+ again_code = cis_data->min_analog_gain[0];
+
+ again_permile = sensor_cis_calc_again_permile(again_code);
+
+ dbg_sensor(1, "[%s] min again(%d), max(%d), input_again(%d), code(%d), permile(%d)\n", __func__,
+ cis_data->max_analog_gain[0],
+ cis_data->min_analog_gain[0],
+ input_again,
+ again_code,
+ again_permile);
+
+ *target_permile = again_permile;
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_analog_gain(struct v4l2_subdev *subdev, struct ae_param *again)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+
+ u16 analog_gain = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!again);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ analog_gain = (u16)sensor_cis_calc_again_code(again->val);
+
+ if (analog_gain < cis->cis_data->min_analog_gain[0])
+ analog_gain = cis->cis_data->min_analog_gain[0];
+
+ if (analog_gain > cis->cis_data->max_analog_gain[0])
+ analog_gain = cis->cis_data->max_analog_gain[0];
+
+ dbg_sensor(1, "[MOD:D:%d] %s(vsync cnt = %d), input_again = %d us, analog_gain(%#x)\n",
+ cis->id, __func__, cis->cis_data->sen_vsync_count, again->val, analog_gain);
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_write16(client, 0x0204, analog_gain);
+ if (ret < 0)
+ goto p_err;
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_analog_gain(struct v4l2_subdev *subdev, u32 *again)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+
+ u16 analog_gain = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!again);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_read16(client, 0x0204, &analog_gain);
+ if (ret < 0)
+ goto p_err;
+
+ *again = sensor_cis_calc_again_permile(analog_gain);
+
+ dbg_sensor(1, "[MOD:D:%d] %s, cur_again = %d us, analog_gain(%#x)\n",
+ cis->id, __func__, *again, analog_gain);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_min_analog_gain(struct v4l2_subdev *subdev, u32 *min_again)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u16 read_value = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!min_again);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ fimc_is_sensor_read16(client, 0x0084, &read_value);
+
+ cis_data->min_analog_gain[0] = read_value;
+
+ cis_data->min_analog_gain[1] = sensor_cis_calc_again_permile(read_value);
+
+ *min_again = cis_data->min_analog_gain[1];
+
+ dbg_sensor(1, "[%s] code %d, permile %d\n", __func__, cis_data->min_analog_gain[0], cis_data->min_analog_gain[1]);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_max_analog_gain(struct v4l2_subdev *subdev, u32 *max_again)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u16 read_value = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!max_again);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ fimc_is_sensor_read16(client, 0x0086, &read_value);
+
+ cis_data->max_analog_gain[0] = read_value;
+
+ cis_data->max_analog_gain[1] = sensor_cis_calc_again_permile(read_value);
+
+ *max_again = cis_data->max_analog_gain[1];
+
+ dbg_sensor(1, "[%s] code %d, permile %d\n", __func__, cis_data->max_analog_gain[0], cis_data->max_analog_gain[1]);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_set_digital_gain(struct v4l2_subdev *subdev, struct ae_param *dgain)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u16 long_gain = 0;
+ u16 short_gain = 0;
+ u16 dgains[4] = {0};
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!dgain);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ long_gain = (u16)sensor_cis_calc_dgain_code(dgain->long_val);
+ short_gain = (u16)sensor_cis_calc_dgain_code(dgain->short_val);
+
+ if (long_gain < cis->cis_data->min_digital_gain[0])
+ long_gain = cis->cis_data->min_digital_gain[0];
+
+ if (long_gain > cis->cis_data->max_digital_gain[0])
+ long_gain = cis->cis_data->max_digital_gain[0];
+
+ if (short_gain < cis->cis_data->min_digital_gain[0])
+ short_gain = cis->cis_data->min_digital_gain[0];
+
+ if (short_gain > cis->cis_data->max_digital_gain[0])
+ short_gain = cis->cis_data->max_digital_gain[0];
+
+ dbg_sensor(1, "[MOD:D:%d] %s(vsync cnt = %d), input_dgain = %d/%d us, long_gain(%#x), short_gain(%#x)\n",
+ cis->id, __func__, cis->cis_data->sen_vsync_count, dgain->long_val,
+ dgain->short_val, long_gain, short_gain);
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ dgains[0] = dgains[1] = dgains[2] = dgains[3] = short_gain;
+ /* Short digital gain */
+ ret = fimc_is_sensor_write16_array(client, 0x020E, dgains, 4);
+ if (ret < 0)
+ goto p_err;
+
+ /* Long digital gain */
+ if (fimc_is_vender_wdr_mode_on(cis_data)) {
+ ret = fimc_is_sensor_write16(client, 0x305C, long_gain);
+ if (ret < 0)
+ goto p_err;
+ }
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_digital_gain(struct v4l2_subdev *subdev, u32 *dgain)
+{
+ int ret = 0;
+ int hold = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+
+ u16 digital_gain = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!dgain);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x01);
+ if (hold < 0) {
+ ret = hold;
+ goto p_err;
+ }
+
+ ret = fimc_is_sensor_read16(client, 0x020E, &digital_gain);
+ if (ret < 0)
+ goto p_err;
+
+ *dgain = sensor_cis_calc_dgain_permile(digital_gain);
+
+ dbg_sensor(1, "[MOD:D:%d] %s, cur_dgain = %d us, digital_gain(%#x)\n",
+ cis->id, __func__, *dgain, digital_gain);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ if (hold > 0) {
+ hold = sensor_2t7sx_cis_group_param_hold_func(subdev, 0x00);
+ if (hold < 0)
+ ret = hold;
+ }
+
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_min_digital_gain(struct v4l2_subdev *subdev, u32 *min_dgain)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u16 read_value = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!min_dgain);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ fimc_is_sensor_read16(client, 0x1084, &read_value);
+
+ cis_data->min_digital_gain[0] = read_value;
+
+ cis_data->min_digital_gain[1] = sensor_cis_calc_dgain_permile(read_value);
+
+ *min_dgain = cis_data->min_digital_gain[1];
+
+ dbg_sensor(1, "[%s] code %d, permile %d\n", __func__,
+ cis_data->min_digital_gain[0], cis_data->min_digital_gain[1]);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+int sensor_2t7sx_cis_get_max_digital_gain(struct v4l2_subdev *subdev, u32 *max_dgain)
+{
+ int ret = 0;
+ struct fimc_is_cis *cis;
+ struct i2c_client *client;
+ cis_shared_data *cis_data;
+
+ u16 read_value = 0;
+
+#ifdef DEBUG_SENSOR_TIME
+ struct timeval st, end;
+
+ do_gettimeofday(&st);
+#endif
+
+ BUG_ON(!subdev);
+ BUG_ON(!max_dgain);
+
+ cis = (struct fimc_is_cis *)v4l2_get_subdevdata(subdev);
+
+ BUG_ON(!cis);
+ BUG_ON(!cis->cis_data);
+
+ client = cis->client;
+ if (unlikely(!client)) {
+ err("client is NULL");
+ ret = -EINVAL;
+ goto p_err;
+ }
+
+ cis_data = cis->cis_data;
+
+ fimc_is_sensor_read16(client, 0x1086, &read_value);
+
+ cis_data->max_digital_gain[0] = read_value;
+
+ cis_data->max_digital_gain[1] = sensor_cis_calc_dgain_permile(read_value);
+
+ *max_dgain = cis_data->max_digital_gain[1];
+
+ dbg_sensor(1, "[%s] code %d, permile %d\n", __func__,
+ cis_data->max_digital_gain[0], cis_data->max_digital_gain[1]);
+
+#ifdef DEBUG_SENSOR_TIME
+ do_gettimeofday(&end);
+ dbg_sensor(1, "[%s] time %lu us\n", __func__, (end.tv_sec - st.tv_sec)*1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+ return ret;
+}
+
+static struct fimc_is_cis_ops cis_ops = {
+ .cis_init = sensor_2t7sx_cis_init,
+ .cis_log_status = sensor_2t7sx_cis_log_status,
+ .cis_group_param_hold = sensor_2t7sx_cis_group_param_hold,
+ .cis_set_global_setting = sensor_2t7sx_cis_set_global_setting,
+ .cis_mode_change = sensor_2t7sx_cis_mode_change,
+ .cis_set_size = sensor_2t7sx_cis_set_size,
+ .cis_stream_on = sensor_2t7sx_cis_stream_on,
+ .cis_stream_off = sensor_2t7sx_cis_stream_off,
+ .cis_set_exposure_time = sensor_2t7sx_cis_set_exposure_time,
+ .cis_get_min_exposure_time = sensor_2t7sx_cis_get_min_exposure_time,
+ .cis_get_max_exposure_time = sensor_2t7sx_cis_get_max_exposure_time,
+ .cis_adjust_frame_duration = sensor_2t7sx_cis_adjust_frame_duration,
+ .cis_set_frame_duration = sensor_2t7sx_cis_set_frame_duration,
+ .cis_set_frame_rate = sensor_2t7sx_cis_set_frame_rate,
+ .cis_adjust_analog_gain = sensor_2t7sx_cis_adjust_analog_gain,
+ .cis_set_analog_gain = sensor_2t7sx_cis_set_analog_gain,
+ .cis_get_analog_gain = sensor_2t7sx_cis_get_analog_gain,
+ .cis_get_min_analog_gain = sensor_2t7sx_cis_get_min_analog_gain,
+ .cis_get_max_analog_gain = sensor_2t7sx_cis_get_max_analog_gain,
+ .cis_set_digital_gain = sensor_2t7sx_cis_set_digital_gain,
+ .cis_get_digital_gain = sensor_2t7sx_cis_get_digital_gain,
+ .cis_get_min_digital_gain = sensor_2t7sx_cis_get_min_digital_gain,
+ .cis_get_max_digital_gain = sensor_2t7sx_cis_get_max_digital_gain,
+ .cis_compensate_gain_for_extremely_br = sensor_cis_compensate_gain_for_extremely_br,
+ .cis_wait_streamoff = sensor_cis_wait_streamoff,
+ .cis_wait_streamon = sensor_cis_wait_streamon,
+};
+
+static int cis_2t7sx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct fimc_is_core *core = NULL;
+ struct v4l2_subdev *subdev_cis = NULL;
+ struct fimc_is_cis *cis = NULL;
+ struct fimc_is_device_sensor *device = NULL;
+ struct fimc_is_device_sensor_peri *sensor_peri = NULL;
+ u32 sensor_id = 0;
+ char const *setfile;
+ struct device *dev;
+ struct device_node *dnode;
+
+ BUG_ON(!client);
+ BUG_ON(!fimc_is_dev);
+
+ core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
+ if (!core) {
+ probe_info("core device is not yet probed");
+ return -EPROBE_DEFER;
+ }
+
+ dev = &client->dev;
+ dnode = dev->of_node;
+
+ ret = of_property_read_u32(dnode, "id", &sensor_id);
+ if (ret) {
+ err("sensor id read is fail(%d)", ret);
+ goto p_err;
+ }
+
+ probe_info("%s sensor id %d\n", __func__, sensor_id);
+
+ device = &core->sensor[sensor_id];
+
+ sensor_peri = find_peri_by_cis_id(device, SENSOR_NAME_S5K2T7SX);
+ if (!sensor_peri) {
+ probe_info("sensor peri is net yet probed");
+ return -EPROBE_DEFER;
+ }
+
+ cis = &sensor_peri->cis;
+ if (!cis) {
+ err("cis is NULL");
+ ret = -ENOMEM;
+ goto p_err;
+ }
+
+ subdev_cis = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (!subdev_cis) {
+ probe_err("subdev_cis NULL");
+ ret = -ENOMEM;
+ goto p_err;
+ }
+ sensor_peri->subdev_cis = subdev_cis;
+
+ cis->id = SENSOR_NAME_S5K2T7SX;
+ cis->subdev = subdev_cis;
+ cis->device = 0;
+ cis->client = client;
+ sensor_peri->module->client = cis->client;
+ cis->ctrl_delay = N_PLUS_TWO_FRAME;
+
+ cis->cis_data = kzalloc(sizeof(cis_shared_data), GFP_KERNEL);
+ if (!cis->cis_data) {
+ err("cis_data is NULL");
+ ret = -ENOMEM;
+ goto p_err;
+ }
+ cis->cis_ops = &cis_ops;
+
+ /* belows are depend on sensor cis. MUST check sensor spec */
+ cis->bayer_order = OTF_INPUT_ORDER_BAYER_GR_BG;
+
+ if (of_property_read_bool(dnode, "sensor_f_number")) {
+ ret = of_property_read_u32(dnode, "sensor_f_number", &cis->aperture_num);
+ if (ret)
+ warn("f-number read is fail(%d)", ret);
+ } else {
+ cis->aperture_num = F2_2;
+ }
+
+ probe_info("%s f-number %d\n", __func__, cis->aperture_num);
+
+ cis->use_dgain = true;
+ cis->hdr_ctrl_by_again = false;
+
+ ret = of_property_read_string(dnode, "setfile", &setfile);
+ if (ret) {
+ err("setfile index read fail(%d), take default setfile!!", ret);
+ setfile = "default";
+ }
+
+ if (strcmp(setfile, "default") == 0 ||
+ strcmp(setfile, "setA") == 0) {
+ probe_info("%s setfile_A\n", __func__);
+ sensor_2t7sx_global = sensor_2t7sx_setfile_A_Global;
+ sensor_2t7sx_global_size = ARRAY_SIZE(sensor_2t7sx_setfile_A_Global);
+ sensor_2t7sx_setfiles = sensor_2t7sx_setfiles_A;
+ sensor_2t7sx_setfile_sizes = sensor_2t7sx_setfile_A_sizes;
+ sensor_2t7sx_pllinfos = sensor_2t7sx_pllinfos_A;
+ sensor_2t7sx_max_setfile_num = ARRAY_SIZE(sensor_2t7sx_setfiles_A);
+ } else if (strcmp(setfile, "setB") == 0) {
+ probe_info("%s setfile_B\n", __func__);
+ sensor_2t7sx_global = sensor_2t7sx_setfile_B_Global;
+ sensor_2t7sx_global_size = ARRAY_SIZE(sensor_2t7sx_setfile_B_Global);
+ sensor_2t7sx_setfiles = sensor_2t7sx_setfiles_B;
+ sensor_2t7sx_setfile_sizes = sensor_2t7sx_setfile_B_sizes;
+ sensor_2t7sx_pllinfos = sensor_2t7sx_pllinfos_B;
+ sensor_2t7sx_max_setfile_num = ARRAY_SIZE(sensor_2t7sx_setfiles_B);
+ } else {
+ err("%s setfile index out of bound, take default (setfile_A)", __func__);
+ sensor_2t7sx_global = sensor_2t7sx_setfile_A_Global;
+ sensor_2t7sx_global_size = ARRAY_SIZE(sensor_2t7sx_setfile_A_Global);
+ sensor_2t7sx_setfiles = sensor_2t7sx_setfiles_A;
+ sensor_2t7sx_setfile_sizes = sensor_2t7sx_setfile_A_sizes;
+ sensor_2t7sx_pllinfos = sensor_2t7sx_pllinfos_A;
+ sensor_2t7sx_max_setfile_num = ARRAY_SIZE(sensor_2t7sx_setfiles_A);
+ }
+
+ v4l2_i2c_subdev_init(subdev_cis, client, &subdev_ops);
+ v4l2_set_subdevdata(subdev_cis, cis);
+ v4l2_set_subdev_hostdata(subdev_cis, device);
+ snprintf(subdev_cis->name, V4L2_SUBDEV_NAME_SIZE, "cis-subdev.%d", cis->id);
+
+ probe_info("%s done\n", __func__);
+
+p_err:
+ return ret;
+}
+
+static const struct of_device_id sensor_cis_2t7sx_match[] = {
+ {
+ .compatible = "samsung,exynos5-fimc-is-cis-2t7sx",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sensor_cis_2t7sx_match);
+
+static const struct i2c_device_id sensor_cis_2t7sx_idt[] = {
+ { SENSOR_NAME, 0 },
+ {},
+};
+
+static struct i2c_driver sensor_cis_2t7sx_driver = {
+ .probe = cis_2t7sx_probe,
+ .driver = {
+ .name = SENSOR_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = sensor_cis_2t7sx_match,
+ .suppress_bind_attrs = true,
+ },
+ .id_table = sensor_cis_2t7sx_idt
+};
+
+static int __init sensor_cis_2t7sx_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&sensor_cis_2t7sx_driver);
+ if (ret)
+ err("failed to add %s driver: %d\n",
+ sensor_cis_2t7sx_driver.driver.name, ret);
+
+ return ret;
+}
+late_initcall_sync(sensor_cis_2t7sx_init);
--- /dev/null
+/*
+ * Samsung Exynos5 SoC series Sensor driver
+ *
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_exynos_camera.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <exynos-fimc-is-sensor.h>
+#include "fimc-is-hw.h"
+#include "fimc-is-core.h"
+#include "fimc-is-device-sensor.h"
+#include "fimc-is-device-sensor-peri.h"
+#include "fimc-is-resourcemgr.h"
+#include "fimc-is-dt.h"
+
+#include "fimc-is-device-module-base.h"
+
+#define MAX_2T7SX_SETPIN_CNT 2
+
+static struct fimc_is_sensor_cfg config_module_2t7sx[] = {
+ /* width, height, fps, settle, mode, lane, speed, interleave, pd_mode */
+ FIMC_IS_SENSOR_CFG(5184, 3880, 30, 0, 0, CSI_DATA_LANES_4, 1690, CSI_MODE_VC_DT, PD_NONE,
+ VC_IN(0, HW_FORMAT_RAW10, 5184, 3880), VC_OUT(HW_FORMAT_RAW10, VC_NOTHING, 0, 0),
+ VC_IN(1, HW_FORMAT_UNKNOWN, 0, 0), VC_OUT(HW_FORMAT_UNKNOWN, VC_NOTHING, 0, 0),
+ VC_IN(2, HW_FORMAT_USER, 0, 0), VC_OUT(HW_FORMAT_USER, VC_NOTHING, 0, 0),
+ VC_IN(3, HW_FORMAT_UNKNOWN, 0, 0), VC_OUT(HW_FORMAT_UNKNOWN, VC_NOTHING, 0, 0)),
+};
+
+static const struct v4l2_subdev_core_ops core_ops = {
+ .init = sensor_module_init,
+ .g_ctrl = sensor_module_g_ctrl,
+ .s_ctrl = sensor_module_s_ctrl,
+ .g_ext_ctrls = sensor_module_g_ext_ctrls,
+ .s_ext_ctrls = sensor_module_s_ext_ctrls,
+ .ioctl = sensor_module_ioctl,
+ .log_status = sensor_module_log_status,
+};
+
+static const struct v4l2_subdev_video_ops video_ops = {
+ .s_stream = sensor_module_s_stream,
+ .s_parm = sensor_module_s_param
+};
+
+static const struct v4l2_subdev_pad_ops pad_ops = {
+ .set_fmt = sensor_module_s_format
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+ .core = &core_ops,
+ .video = &video_ops,
+ .pad = &pad_ops
+};
+
+static int sensor_module_2t7sx_power_setpin(struct device *dev,
+ struct exynos_platform_fimc_is_module *pdata)
+{
+ struct device_node *dnode;
+ int gpio_reset = 0;
+ int gpio_none = 0;
+ int gpio_mclk = 0;
+
+ BUG_ON(!dev);
+
+ dnode = dev->of_node;
+
+ dev_info(dev, "%s E v4\n", __func__);
+
+ /* TODO */
+ gpio_reset = of_get_named_gpio(dnode, "gpio_reset", 0);
+ if (!gpio_is_valid(gpio_reset)) {
+ dev_err(dev, "failed to get PIN_RESET\n");
+ return -EINVAL;
+ } else {
+ gpio_request_one(gpio_reset, GPIOF_OUT_INIT_LOW, "CAM_GPIO_OUTPUT_LOW");
+ gpio_free(gpio_reset);
+ }
+
+ gpio_mclk = of_get_named_gpio(dnode, "gpio_mclk", 0);
+ if (gpio_is_valid(gpio_mclk)) {
+ if (gpio_request_one(gpio_mclk, GPIOF_OUT_INIT_LOW, "CAM_MCLK_OUTPUT_LOW")) {
+ dev_err(dev, "%s: failed to gpio request mclk\n", __func__);
+ return -ENODEV;
+ }
+ gpio_free(gpio_mclk);
+ } else {
+ dev_err(dev, "%s: failed to get mclk\n", __func__);
+ return -EINVAL;
+ }
+
+ SET_PIN_INIT(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON);
+ SET_PIN_INIT(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF);
+ SET_PIN_INIT(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON);
+ SET_PIN_INIT(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF);
+
+ /* BACK CAEMRA - POWER ON */
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_reset, "sen_rst low", PIN_OUTPUT, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_none, "VDDIO_1.8V_CAM_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_none, "VDDD_2.8V_OIS_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_none, "VDDA_2.8V_CAM_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_none, "VDDD_1.0V_CAM_T", PIN_REGULATOR, 1, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_none, "pin", PIN_FUNCTION, 2, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_ON, gpio_reset, "sen_rst high", PIN_OUTPUT, 1, 2000);
+
+ /* BACK CAEMRA - POWER OFF */
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_reset, "sen_rst", PIN_RESET, 0, 10);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_reset, "sen_rst input", PIN_INPUT, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "VDDD_1.0V_CAM_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "VDDD_2.8V_OIS_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "VDDA_2.8V_CAM_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "VDDIO_1.8V_CAM_T", PIN_REGULATOR, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_NORMAL, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 0, 0);
+
+ /* REAR VISION CAEMRA - POWER ON */
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_reset, "sen_rst low", PIN_OUTPUT, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_none, "VDDIO_1.8V_CAM_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_none, "VDDD_2.8V_OIS_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_none, "VDDA_2.8V_CAM_T", PIN_REGULATOR, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_none, "VDDD_1.0V_CAM_T", PIN_REGULATOR, 1, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_none, "pin", PIN_FUNCTION, 2, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_ON, gpio_reset, "sen_rst high", PIN_OUTPUT, 1, 2000);
+
+
+ /* REAR VISION CAEMRA - POWER OFF */
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_reset, "sen_rst", PIN_RESET, 0, 10);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_reset, "sen_rst input", PIN_INPUT, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "VDDD_1.0V_CAM_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "VDDD_2.8V_OIS_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "VDDA_2.8V_CAM_T", PIN_REGULATOR, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "VDDIO_1.8V_CAM_T", PIN_REGULATOR, 0, 0);
+
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 0, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 1, 0);
+ SET_PIN(pdata, SENSOR_SCENARIO_VISION, GPIO_SCENARIO_OFF, gpio_none, "pin", PIN_FUNCTION, 0, 0);
+
+ dev_info(dev, "%s X v4\n", __func__);
+
+ return 0;
+}
+
+static int __init sensor_module_2t7sx_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct fimc_is_core *core;
+ struct v4l2_subdev *subdev_module;
+ struct fimc_is_module_enum *module;
+ struct fimc_is_device_sensor *device;
+ struct sensor_open_extended *ext;
+ struct exynos_platform_fimc_is_module *pdata;
+ struct device *dev;
+ struct pinctrl_state *s;
+
+ BUG_ON(!fimc_is_dev);
+
+ core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
+ if (!core) {
+ probe_info("core device is not yet probed");
+ return -EPROBE_DEFER;
+ }
+
+ dev = &pdev->dev;
+
+#ifdef CONFIG_OF
+ fimc_is_module_parse_dt(dev, sensor_module_2t7sx_power_setpin);
+#endif
+
+ pdata = dev_get_platdata(dev);
+ device = &core->sensor[pdata->id];
+
+ subdev_module = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (!subdev_module) {
+ probe_err("subdev_module is NULL");
+ ret = -ENOMEM;
+ goto p_err;
+ }
+
+ probe_info("%s pdta->id(%d), module_enum id = %d\n", __func__, pdata->id, atomic_read(&device->module_count));
+ module = &device->module_enum[atomic_read(&device->module_count)];
+ atomic_inc(&device->module_count);
+ clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
+ module->pdata = pdata;
+ module->dev = dev;
+ module->sensor_id = SENSOR_NAME_S5K2T7SX;
+ module->subdev = subdev_module;
+ module->device = pdata->id;
+ module->client = NULL;
+ module->active_width = 5168 + 16;
+ module->active_height = 3868 + 12;
+ module->margin_left = 0;
+ module->margin_right = 0;
+ module->margin_top = 0;
+ module->margin_bottom = 0;
+ module->pixel_width = module->active_width;
+ module->pixel_height = module->active_height;
+ module->max_framerate = 120;
+ module->position = pdata->position;
+ module->bitwidth = 10;
+ module->sensor_maker = "SLSI";
+ module->sensor_name = "S5K2T7SX";
+ module->setfile_name = "setfile_2t7sx.bin";
+ module->cfgs = ARRAY_SIZE(config_module_2t7sx);
+ module->cfg = config_module_2t7sx;
+ module->ops = NULL;
+
+ /* Sensor peri */
+ module->private_data = kzalloc(sizeof(struct fimc_is_device_sensor_peri), GFP_KERNEL);
+ if (!module->private_data) {
+ probe_err("fimc_is_device_sensor_peri is NULL");
+ ret = -ENOMEM;
+ goto p_err;
+ }
+ fimc_is_sensor_peri_probe((struct fimc_is_device_sensor_peri *)module->private_data);
+ PERI_SET_MODULE(module);
+
+ ext = &module->ext;
+
+ ext->sensor_con.product_name = module->sensor_id;
+ ext->sensor_con.peri_type = SE_I2C;
+ ext->sensor_con.peri_setting.i2c.channel = pdata->sensor_i2c_ch;
+ ext->sensor_con.peri_setting.i2c.slave_address = pdata->sensor_i2c_addr;
+ ext->sensor_con.peri_setting.i2c.speed = 400000;
+
+ ext->actuator_con.product_name = ACTUATOR_NAME_NOTHING;
+ ext->flash_con.product_name = FLADRV_NAME_NOTHING;
+ ext->from_con.product_name = FROMDRV_NAME_NOTHING;
+ ext->preprocessor_con.product_name = PREPROCESSOR_NAME_NOTHING;
+ ext->ois_con.product_name = OIS_NAME_NOTHING;
+
+ if (pdata->af_product_name != ACTUATOR_NAME_NOTHING) {
+ ext->actuator_con.product_name = pdata->af_product_name;
+ ext->actuator_con.peri_type = SE_I2C;
+ ext->actuator_con.peri_setting.i2c.channel = pdata->af_i2c_ch;
+ ext->actuator_con.peri_setting.i2c.slave_address = pdata->af_i2c_addr;
+ ext->actuator_con.peri_setting.i2c.speed = 400000;
+ }
+
+ if (pdata->flash_product_name != FLADRV_NAME_NOTHING) {
+ ext->flash_con.product_name = pdata->flash_product_name;
+ ext->flash_con.peri_type = SE_GPIO;
+ ext->flash_con.peri_setting.gpio.first_gpio_port_no = pdata->flash_first_gpio;
+ ext->flash_con.peri_setting.gpio.second_gpio_port_no = pdata->flash_second_gpio;
+ }
+
+ ext->from_con.product_name = FROMDRV_NAME_NOTHING;
+
+ if (pdata->preprocessor_product_name != PREPROCESSOR_NAME_NOTHING) {
+ ext->preprocessor_con.product_name = pdata->preprocessor_product_name;
+ ext->preprocessor_con.peri_info0.valid = true;
+ ext->preprocessor_con.peri_info0.peri_type = SE_SPI;
+ ext->preprocessor_con.peri_info0.peri_setting.spi.channel = pdata->preprocessor_spi_channel;
+ ext->preprocessor_con.peri_info1.valid = true;
+ ext->preprocessor_con.peri_info1.peri_type = SE_I2C;
+ ext->preprocessor_con.peri_info1.peri_setting.i2c.channel = pdata->preprocessor_i2c_ch;
+ ext->preprocessor_con.peri_info1.peri_setting.i2c.slave_address = pdata->preprocessor_i2c_addr;
+ ext->preprocessor_con.peri_info1.peri_setting.i2c.speed = 400000;
+ ext->preprocessor_con.peri_info2.valid = true;
+ ext->preprocessor_con.peri_info2.peri_type = SE_DMA;
+ if (pdata->preprocessor_dma_channel == DMA_CH_NOT_DEFINED)
+ ext->preprocessor_con.peri_info2.peri_setting.dma.channel = FLITE_ID_D;
+ else
+ ext->preprocessor_con.peri_info2.peri_setting.dma.channel = pdata->preprocessor_dma_channel;
+ }
+
+ if (pdata->ois_product_name != OIS_NAME_NOTHING) {
+ ext->ois_con.product_name = pdata->ois_product_name;
+ ext->ois_con.peri_type = SE_I2C;
+ ext->ois_con.peri_setting.i2c.channel = pdata->ois_i2c_ch;
+ ext->ois_con.peri_setting.i2c.slave_address = pdata->ois_i2c_addr;
+ ext->ois_con.peri_setting.i2c.speed = 400000;
+ } else {
+ ext->ois_con.product_name = pdata->ois_product_name;
+ ext->ois_con.peri_type = SE_NULL;
+ }
+
+ v4l2_subdev_init(subdev_module, &subdev_ops);
+
+ v4l2_set_subdevdata(subdev_module, module);
+ v4l2_set_subdev_hostdata(subdev_module, device);
+ snprintf(subdev_module->name, V4L2_SUBDEV_NAME_SIZE, "sensor-subdev.%d", module->sensor_id);
+
+ s = pinctrl_lookup_state(pdata->pinctrl, "release");
+
+ if (pinctrl_select_state(pdata->pinctrl, s) < 0) {
+ probe_err("pinctrl_select_state is fail\n");
+ goto p_err;
+ }
+
+ probe_info("%s done\n", __func__);
+
+p_err:
+ return ret;
+}
+
+static const struct of_device_id exynos_fimc_is_sensor_module_2t7sx_match[] = {
+ {
+ .compatible = "samsung,sensor-module-2t7sx",
+ },
+ {
+ .compatible = "samsung,sensor-module-2t7sx-front",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_fimc_is_sensor_module_2t7sx_match);
+
+static struct platform_driver sensor_module_2t7sx_driver = {
+ .driver = {
+ .name = "FIMC-IS-SENSOR-MODULE-2T7SX",
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_fimc_is_sensor_module_2t7sx_match,
+ }
+};
+
+static int __init fimc_is_sensor_module_2t7sx_init(void)
+{
+ int ret;
+
+ ret = platform_driver_probe(&sensor_module_2t7sx_driver,
+ sensor_module_2t7sx_probe);
+ if (ret)
+ err("failed to probe %s driver: %d\n",
+ sensor_module_2t7sx_driver.driver.name, ret);
+
+ return ret;
+}
+late_initcall(fimc_is_sensor_module_2t7sx_init);