aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/s5k4ecgx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/s5k4ecgx.c')
-rwxr-xr-xdrivers/media/video/s5k4ecgx.c2837
1 files changed, 2837 insertions, 0 deletions
diff --git a/drivers/media/video/s5k4ecgx.c b/drivers/media/video/s5k4ecgx.c
new file mode 100755
index 0000000..9d9d5b4
--- /dev/null
+++ b/drivers/media/video/s5k4ecgx.c
@@ -0,0 +1,2837 @@
+/* drivers/media/video/s5k4ecgx.c
+ *
+ * Driver for s5k4ecgx (5MP Camera) from SEC
+ *
+ * Copyright (C) 2010, SAMSUNG ELECTRONICS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+#include <linux/completion.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/s5k4ecgx.h>
+#include <linux/videodev2_samsung.h>
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_0
+#include "s5k4ecgx_regs_1_0.h"
+#define S5K4ECGX_VERSION_1_0 0x00
+#endif
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_1
+#include "s5k4ecgx_regs_1_1.h"
+#define S5K4ECGX_VERSION_1_1 0x11
+#endif
+
+#define FORMAT_FLAGS_COMPRESSED 0x3
+#define SENSOR_JPEG_SNAPSHOT_MEMSIZE 0x410580
+
+#define DEFAULT_PIX_FMT V4L2_PIX_FMT_UYVY /* YUV422 */
+#define DEFAULT_MCLK 24000000
+#define POLL_TIME_MS 10
+#define CAPTURE_POLL_TIME_MS 1000
+
+/* maximum time for one frame at minimum fps (15fps) in normal mode */
+#define NORMAL_MODE_MAX_ONE_FRAME_DELAY_MS 67
+/* maximum time for one frame at minimum fps (4fps) in night mode */
+#define NIGHT_MODE_MAX_ONE_FRAME_DELAY_MS 250
+
+/* time to move lens to target position before last af mode register write */
+#define LENS_MOVE_TIME_MS 100
+
+/* level at or below which we need to enable flash when in auto mode */
+#define LOW_LIGHT_LEVEL 0x1D
+
+/* level at or below which we need to use low light capture mode */
+#define HIGH_LIGHT_LEVEL 0x80
+
+#define FIRST_AF_SEARCH_COUNT 80
+#define SECOND_AF_SEARCH_COUNT 80
+#define AE_STABLE_SEARCH_COUNT 4
+
+#define FIRST_SETTING_FOCUS_MODE_DELAY_MS 100
+#define SECOND_SETTING_FOCUS_MODE_DELAY_MS 200
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_DEBUG
+enum {
+ S5K4ECGX_DEBUG_I2C = 1U << 0,
+ S5K4ECGX_DEBUG_I2C_BURSTS = 1U << 1,
+};
+static uint32_t s5k4ecgx_debug_mask = S5K4ECGX_DEBUG_I2C_BURSTS;
+module_param_named(debug_mask, s5k4ecgx_debug_mask, uint, S_IWUSR | S_IRUGO);
+
+#define s5k4ecgx_debug(mask, x...) \
+ do { \
+ if (s5k4ecgx_debug_mask & mask) \
+ pr_info(x); \
+ } while (0)
+#else
+
+#define s5k4ecgx_debug(mask, x...)
+
+#endif
+
+#define S5K4ECGX_VERSION_1_1 0x11
+
+/* result values returned to HAL */
+enum {
+ AUTO_FOCUS_FAILED,
+ AUTO_FOCUS_DONE,
+ AUTO_FOCUS_CANCELLED,
+};
+
+enum af_operation_status {
+ AF_NONE = 0,
+ AF_START,
+ AF_CANCEL,
+ AF_INITIAL,
+};
+
+enum s5k4ecgx_oprmode {
+ S5K4ECGX_OPRMODE_VIDEO = 0,
+ S5K4ECGX_OPRMODE_IMAGE = 1,
+};
+
+enum s5k4ecgx_preview_frame_size {
+ S5K4ECGX_PREVIEW_QCIF = 0, /* 176x144 */
+ S5K4ECGX_PREVIEW_CIF, /* 352x288 */
+ S5K4ECGX_PREVIEW_VGA, /* 640x480 */
+ S5K4ECGX_PREVIEW_D1, /* 720x480 */
+ S5K4ECGX_PREVIEW_WVGA, /* 800x480 */
+ S5K4ECGX_PREVIEW_SVGA, /* 800x600 */
+ S5K4ECGX_PREVIEW_WSVGA, /* 1024x600*/
+ S5K4ECGX_PREVIEW_MAX,
+};
+
+enum s5k4ecgx_capture_frame_size {
+ S5K4ECGX_CAPTURE_VGA = 0, /* 640x480 */
+ S5K4ECGX_CAPTURE_WVGA, /* 800x480 */
+ S5K4ECGX_CAPTURE_SVGA, /* 800x600 */
+ S5K4ECGX_CAPTURE_WSVGA, /* 1024x600 */
+ S5K4ECGX_CAPTURE_1MP, /* 1280x960 */
+ S5K4ECGX_CAPTURE_W1MP, /* 1600x960 */
+ S5K4ECGX_CAPTURE_2MP, /* UXGA - 1600x1200 */
+ S5K4ECGX_CAPTURE_W2MP, /* 35mm Academy Offset Standard 1.66 */
+ /* 2048x1232, 2.4MP */
+ S5K4ECGX_CAPTURE_3MP, /* QXGA - 2048x1536 */
+ S5K4ECGX_CAPTURE_W4MP, /* WQXGA - 2560x1536 */
+ S5K4ECGX_CAPTURE_5MP, /* 2560x1920 */
+ S5K4ECGX_CAPTURE_MAX,
+};
+
+struct s5k4ecgx_framesize {
+ u32 index;
+ u32 width;
+ u32 height;
+};
+
+static const struct s5k4ecgx_framesize s5k4ecgx_preview_framesize_list[] = {
+ { S5K4ECGX_PREVIEW_QCIF, 176, 144 },
+ { S5K4ECGX_PREVIEW_CIF, 352, 288 },
+ { S5K4ECGX_PREVIEW_VGA, 640, 480 },
+ { S5K4ECGX_PREVIEW_D1, 720, 480 },
+};
+
+static const struct s5k4ecgx_framesize s5k4ecgx_capture_framesize_list[] = {
+ { S5K4ECGX_CAPTURE_VGA, 640, 480 },
+ { S5K4ECGX_CAPTURE_1MP, 1280, 960 },
+ { S5K4ECGX_CAPTURE_2MP, 1600, 1200 },
+ { S5K4ECGX_CAPTURE_3MP, 2048, 1536 },
+ { S5K4ECGX_CAPTURE_5MP, 2560, 1920 },
+};
+
+struct s5k4ecgx_version {
+ u32 major;
+ u32 minor;
+};
+
+struct s5k4ecgx_date_info {
+ u32 year;
+ u32 month;
+ u32 date;
+};
+
+enum s5k4ecgx_runmode {
+ S5K4ECGX_RUNMODE_NOTREADY,
+ S5K4ECGX_RUNMODE_IDLE,
+ S5K4ECGX_RUNMODE_RUNNING,
+ S5K4ECGX_RUNMODE_CAPTURE,
+};
+
+struct s5k4ecgx_firmware {
+ u32 addr;
+ u32 size;
+};
+
+struct s5k4ecgx_jpeg_param {
+ u32 enable;
+ u32 quality;
+ u32 main_size; /* Main JPEG file size */
+ u32 thumb_size; /* Thumbnail file size */
+ u32 main_offset;
+ u32 thumb_offset;
+ u32 postview_offset;
+};
+
+struct s5k4ecgx_position {
+ int x;
+ int y;
+};
+
+struct gps_info_common {
+ u32 direction;
+ u32 dgree;
+ u32 minute;
+ u32 second;
+};
+
+struct s5k4ecgx_gps_info {
+ unsigned char gps_buf[8];
+ unsigned char altitude_buf[4];
+ int gps_timeStamp;
+};
+
+struct s5k4ecgx_regset {
+ u32 size;
+ u8 *data;
+};
+
+struct s5k4ecgx_regset_table {
+ const u32 *reg;
+ int array_size;
+};
+
+#define S5K4ECGX_REGSET(x, y) \
+ [(x)] = { \
+ .reg = (y), \
+ .array_size = ARRAY_SIZE((y)), \
+}
+
+#define S5K4ECGX_REGSET_TABLE(y) \
+ { \
+ .reg = (y), \
+ .array_size = ARRAY_SIZE((y)), \
+}
+
+struct s5k4ecgx_regs {
+ struct s5k4ecgx_regset_table ev[EV_MAX];
+ struct s5k4ecgx_regset_table metering[METERING_MAX];
+ struct s5k4ecgx_regset_table iso[ISO_MAX];
+ struct s5k4ecgx_regset_table effect[IMAGE_EFFECT_MAX];
+ struct s5k4ecgx_regset_table white_balance[WHITE_BALANCE_MAX];
+ struct s5k4ecgx_regset_table preview_size[S5K4ECGX_PREVIEW_MAX];
+ struct s5k4ecgx_regset_table capture_size[S5K4ECGX_CAPTURE_MAX];
+ struct s5k4ecgx_regset_table scene_mode[SCENE_MODE_MAX];
+ struct s5k4ecgx_regset_table saturation[SATURATION_MAX];
+ struct s5k4ecgx_regset_table contrast[CONTRAST_MAX];
+ struct s5k4ecgx_regset_table sharpness[SHARPNESS_MAX];
+ struct s5k4ecgx_regset_table fps[FRAME_RATE_MAX];
+ struct s5k4ecgx_regset_table preview_return;
+ struct s5k4ecgx_regset_table jpeg_quality_high;
+ struct s5k4ecgx_regset_table jpeg_quality_normal;
+ struct s5k4ecgx_regset_table jpeg_quality_low;
+ struct s5k4ecgx_regset_table flash_start;
+ struct s5k4ecgx_regset_table flash_end;
+ struct s5k4ecgx_regset_table af_assist_flash_start;
+ struct s5k4ecgx_regset_table af_assist_flash_end;
+ struct s5k4ecgx_regset_table af_low_light_mode_on;
+ struct s5k4ecgx_regset_table af_low_light_mode_off;
+ struct s5k4ecgx_regset_table ae_awb_lock_on;
+ struct s5k4ecgx_regset_table ae_awb_lock_off;
+ struct s5k4ecgx_regset_table low_cap_on;
+ struct s5k4ecgx_regset_table low_cap_off;
+ struct s5k4ecgx_regset_table wdr_on;
+ struct s5k4ecgx_regset_table wdr_off;
+ struct s5k4ecgx_regset_table face_detection_on;
+ struct s5k4ecgx_regset_table face_detection_off;
+ struct s5k4ecgx_regset_table capture_start;
+ struct s5k4ecgx_regset_table af_macro_mode_1;
+ struct s5k4ecgx_regset_table af_macro_mode_2;
+ struct s5k4ecgx_regset_table af_macro_mode_3;
+ struct s5k4ecgx_regset_table af_normal_mode_1;
+ struct s5k4ecgx_regset_table af_normal_mode_2;
+ struct s5k4ecgx_regset_table af_normal_mode_3;
+ struct s5k4ecgx_regset_table af_return_macro_position;
+ struct s5k4ecgx_regset_table single_af_start;
+ struct s5k4ecgx_regset_table single_af_off_1;
+ struct s5k4ecgx_regset_table single_af_off_2;
+ struct s5k4ecgx_regset_table dtp_start;
+ struct s5k4ecgx_regset_table dtp_stop;
+ struct s5k4ecgx_regset_table init_reg_1;
+ struct s5k4ecgx_regset_table init_reg_2;
+ struct s5k4ecgx_regset_table flash_init;
+ struct s5k4ecgx_regset_table reset_crop;
+ struct s5k4ecgx_regset_table get_ae_stable_status;
+ struct s5k4ecgx_regset_table get_light_level;
+ struct s5k4ecgx_regset_table get_1st_af_search_status;
+ struct s5k4ecgx_regset_table get_2nd_af_search_status;
+ struct s5k4ecgx_regset_table get_capture_status;
+ struct s5k4ecgx_regset_table get_esd_status;
+ struct s5k4ecgx_regset_table get_iso;
+ struct s5k4ecgx_regset_table get_shutterspeed;
+};
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_0
+static const struct s5k4ecgx_regs regs_for_fw_version_1_0 = {
+ .ev = {
+ S5K4ECGX_REGSET(EV_MINUS_4, s5k4ecgx_EV_Minus_4_v1),
+ S5K4ECGX_REGSET(EV_MINUS_3, s5k4ecgx_EV_Minus_3_v1),
+ S5K4ECGX_REGSET(EV_MINUS_2, s5k4ecgx_EV_Minus_2_v1),
+ S5K4ECGX_REGSET(EV_MINUS_1, s5k4ecgx_EV_Minus_1_v1),
+ S5K4ECGX_REGSET(EV_DEFAULT, s5k4ecgx_EV_Default_v1),
+ S5K4ECGX_REGSET(EV_PLUS_1, s5k4ecgx_EV_Plus_1_v1),
+ S5K4ECGX_REGSET(EV_PLUS_2, s5k4ecgx_EV_Plus_2_v1),
+ S5K4ECGX_REGSET(EV_PLUS_3, s5k4ecgx_EV_Plus_3_v1),
+ S5K4ECGX_REGSET(EV_PLUS_4, s5k4ecgx_EV_Plus_4_v1),
+ },
+ .metering = {
+ S5K4ECGX_REGSET(METERING_MATRIX, s5k4ecgx_Metering_Matrix_v1),
+ S5K4ECGX_REGSET(METERING_CENTER, s5k4ecgx_Metering_Center_v1),
+ S5K4ECGX_REGSET(METERING_SPOT, s5k4ecgx_Metering_Spot_v1),
+ },
+ .iso = {
+ S5K4ECGX_REGSET(ISO_AUTO, s5k4ecgx_ISO_Auto_v1),
+ S5K4ECGX_REGSET(ISO_50, s5k4ecgx_ISO_100_v1), /* use 100 */
+ S5K4ECGX_REGSET(ISO_100, s5k4ecgx_ISO_100_v1),
+ S5K4ECGX_REGSET(ISO_200, s5k4ecgx_ISO_200_v1),
+ S5K4ECGX_REGSET(ISO_400, s5k4ecgx_ISO_400_v1),
+ S5K4ECGX_REGSET(ISO_800, s5k4ecgx_ISO_400_v1), /* use 400 */
+ S5K4ECGX_REGSET(ISO_1600, s5k4ecgx_ISO_400_v1), /* use 400 */
+ S5K4ECGX_REGSET(ISO_SPORTS, s5k4ecgx_ISO_Auto_v1),/* use auto */
+ S5K4ECGX_REGSET(ISO_NIGHT, s5k4ecgx_ISO_Auto_v1), /* use auto */
+ S5K4ECGX_REGSET(ISO_MOVIE, s5k4ecgx_ISO_Auto_v1), /* use auto */
+ },
+ .effect = {
+ S5K4ECGX_REGSET(IMAGE_EFFECT_NONE, s5k4ecgx_Effect_Normal_v1),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_BNW,
+ s5k4ecgx_Effect_Black_White_v1),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_SEPIA, s5k4ecgx_Effect_Sepia_v1),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_NEGATIVE,
+ s5k4ecgx_Effect_Negative_v1),
+ },
+ .white_balance = {
+ S5K4ECGX_REGSET(WHITE_BALANCE_AUTO, s5k4ecgx_WB_Auto_v1),
+ S5K4ECGX_REGSET(WHITE_BALANCE_SUNNY, s5k4ecgx_WB_Sunny_v1),
+ S5K4ECGX_REGSET(WHITE_BALANCE_CLOUDY, s5k4ecgx_WB_Cloudy_v1),
+ S5K4ECGX_REGSET(WHITE_BALANCE_TUNGSTEN,
+ s5k4ecgx_WB_Tungsten_v1),
+ S5K4ECGX_REGSET(WHITE_BALANCE_FLUORESCENT,
+ s5k4ecgx_WB_Fluorescent_v1),
+ },
+ .preview_size = {
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_QCIF, s5k4ecgx_176_Preview_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_CIF, s5k4ecgx_352_Preview_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_VGA, s5k4ecgx_640_Preview_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_D1, s5k4ecgx_720_Preview_v1),
+ },
+ .capture_size = {
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_VGA, s5k4ecgx_VGA_Capture_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_1MP, s5k4ecgx_1M_Capture_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_2MP, s5k4ecgx_2M_Capture_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_3MP, s5k4ecgx_3M_Capture_v1),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_5MP, s5k4ecgx_5M_Capture_v1),
+ },
+ .scene_mode = {
+ S5K4ECGX_REGSET(SCENE_MODE_NONE, s5k4ecgx_Scene_Default_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_PORTRAIT,
+ s5k4ecgx_Scene_Portrait_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_NIGHTSHOT,
+ s5k4ecgx_Scene_Nightshot_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_LANDSCAPE,
+ s5k4ecgx_Scene_Landscape_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_SPORTS, s5k4ecgx_Scene_Sports_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_PARTY_INDOOR,
+ s5k4ecgx_Scene_Party_Indoor_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_BEACH_SNOW,
+ s5k4ecgx_Scene_Beach_Snow_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_SUNSET, s5k4ecgx_Scene_Sunset_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_FIREWORKS,
+ s5k4ecgx_Scene_Fireworks_v1),
+ S5K4ECGX_REGSET(SCENE_MODE_CANDLE_LIGHT,
+ s5k4ecgx_Scene_Candle_Light_v1),
+ },
+ .saturation = {
+ S5K4ECGX_REGSET(SATURATION_MINUS_2,
+ s5k4ecgx_Saturation_Minus_2_v1),
+ S5K4ECGX_REGSET(SATURATION_MINUS_1,
+ s5k4ecgx_Saturation_Minus_1_v1),
+ S5K4ECGX_REGSET(SATURATION_DEFAULT,
+ s5k4ecgx_Saturation_Default_v1),
+ S5K4ECGX_REGSET(SATURATION_PLUS_1,
+ s5k4ecgx_Saturation_Plus_1_v1),
+ S5K4ECGX_REGSET(SATURATION_PLUS_2,
+ s5k4ecgx_Saturation_Plus_2_v1),
+ },
+ .contrast = {
+ S5K4ECGX_REGSET(CONTRAST_MINUS_2, s5k4ecgx_Contrast_Minus_2_v1),
+ S5K4ECGX_REGSET(CONTRAST_MINUS_1, s5k4ecgx_Contrast_Minus_1_v1),
+ S5K4ECGX_REGSET(CONTRAST_DEFAULT, s5k4ecgx_Contrast_Default_v1),
+ S5K4ECGX_REGSET(CONTRAST_PLUS_1, s5k4ecgx_Contrast_Plus_1_v1),
+ S5K4ECGX_REGSET(CONTRAST_PLUS_2, s5k4ecgx_Contrast_Plus_2_v1),
+ },
+ .sharpness = {
+ S5K4ECGX_REGSET(SHARPNESS_MINUS_2,
+ s5k4ecgx_Sharpness_Minus_2_v1),
+ S5K4ECGX_REGSET(SHARPNESS_MINUS_1,
+ s5k4ecgx_Sharpness_Minus_1_v1),
+ S5K4ECGX_REGSET(SHARPNESS_DEFAULT,
+ s5k4ecgx_Sharpness_Default_v1),
+ S5K4ECGX_REGSET(SHARPNESS_PLUS_1, s5k4ecgx_Sharpness_Plus_1_v1),
+ S5K4ECGX_REGSET(SHARPNESS_PLUS_2, s5k4ecgx_Sharpness_Plus_2_v1),
+ },
+ .preview_return = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Preview_Return_v1),
+ .jpeg_quality_high =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_High_v1),
+ .jpeg_quality_normal =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_Normal_v1),
+ .jpeg_quality_low = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_Low_v1),
+ .flash_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_Start_v1),
+ .flash_end = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_End_v1),
+ .af_assist_flash_start =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Pre_Flash_Start_v1),
+ .af_assist_flash_end =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Pre_Flash_End_v1),
+ .af_low_light_mode_on =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Low_Light_Mode_On_v1),
+ .af_low_light_mode_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Low_Light_Mode_Off_v1),
+ .ae_awb_lock_on =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AE_AWB_Lock_On_v1),
+ .ae_awb_lock_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AE_AWB_Lock_Off_v1),
+ .low_cap_on = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Low_Cap_On_v1),
+ .low_cap_off = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Low_Cap_Off_v1),
+ .wdr_on = S5K4ECGX_REGSET_TABLE(s5k4ecgx_WDR_on_v1),
+ .wdr_off = S5K4ECGX_REGSET_TABLE(s5k4ecgx_WDR_off_v1),
+ .face_detection_on =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Face_Detection_On_v1),
+ .face_detection_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Face_Detection_Off_v1),
+ .capture_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Capture_Start_v1),
+ .af_macro_mode_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_1_v1),
+ .af_macro_mode_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_2_v1),
+ .af_macro_mode_3 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_3_v1),
+ .af_normal_mode_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_1_v1),
+ .af_normal_mode_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_2_v1),
+ .af_normal_mode_3 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_3_v1),
+ .af_return_macro_position =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Return_Macro_pos_v1),
+ .single_af_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Start_v1),
+ .single_af_off_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Off_1_v1),
+ .single_af_off_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Off_2_v1),
+ .dtp_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_DTP_init_v1),
+ .dtp_stop = S5K4ECGX_REGSET_TABLE(s5k4ecgx_DTP_stop_v1),
+ .init_reg_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_init_reg1_v1),
+ .init_reg_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_init_reg2_v1),
+ .flash_init = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_init_v1),
+ .reset_crop = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Reset_Crop_v1),
+ .get_ae_stable_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Get_AE_Stable_Status_v1),
+ .get_light_level = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Get_Light_Level_v1),
+ .get_1st_af_search_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_1st_af_search_status_v1),
+ .get_2nd_af_search_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_2nd_af_search_status_v1),
+ .get_capture_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_capture_status_v1),
+ .get_esd_status = S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_esd_status_v1),
+ .get_iso = S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_iso_reg_v1),
+ .get_shutterspeed =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_shutterspeed_reg_v1),
+};
+#endif
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_1
+static const struct s5k4ecgx_regs regs_for_fw_version_1_1 = {
+ .ev = {
+ S5K4ECGX_REGSET(EV_MINUS_4, s5k4ecgx_EV_Minus_4),
+ S5K4ECGX_REGSET(EV_MINUS_3, s5k4ecgx_EV_Minus_3),
+ S5K4ECGX_REGSET(EV_MINUS_2, s5k4ecgx_EV_Minus_2),
+ S5K4ECGX_REGSET(EV_MINUS_1, s5k4ecgx_EV_Minus_1),
+ S5K4ECGX_REGSET(EV_DEFAULT, s5k4ecgx_EV_Default),
+ S5K4ECGX_REGSET(EV_PLUS_1, s5k4ecgx_EV_Plus_1),
+ S5K4ECGX_REGSET(EV_PLUS_2, s5k4ecgx_EV_Plus_2),
+ S5K4ECGX_REGSET(EV_PLUS_3, s5k4ecgx_EV_Plus_3),
+ S5K4ECGX_REGSET(EV_PLUS_4, s5k4ecgx_EV_Plus_4),
+ },
+ .metering = {
+ S5K4ECGX_REGSET(METERING_MATRIX, s5k4ecgx_Metering_Matrix),
+ S5K4ECGX_REGSET(METERING_CENTER, s5k4ecgx_Metering_Center),
+ S5K4ECGX_REGSET(METERING_SPOT, s5k4ecgx_Metering_Spot),
+ },
+ .iso = {
+ S5K4ECGX_REGSET(ISO_AUTO, s5k4ecgx_ISO_Auto),
+ S5K4ECGX_REGSET(ISO_50, s5k4ecgx_ISO_100), /* map to 100 */
+ S5K4ECGX_REGSET(ISO_100, s5k4ecgx_ISO_100),
+ S5K4ECGX_REGSET(ISO_200, s5k4ecgx_ISO_200),
+ S5K4ECGX_REGSET(ISO_400, s5k4ecgx_ISO_400),
+ S5K4ECGX_REGSET(ISO_800, s5k4ecgx_ISO_400), /* map to 400 */
+ S5K4ECGX_REGSET(ISO_1600, s5k4ecgx_ISO_400), /* map to 400 */
+ S5K4ECGX_REGSET(ISO_SPORTS, s5k4ecgx_ISO_Auto),/* map to auto */
+ S5K4ECGX_REGSET(ISO_NIGHT, s5k4ecgx_ISO_Auto), /* map to auto */
+ S5K4ECGX_REGSET(ISO_MOVIE, s5k4ecgx_ISO_Auto), /* map to auto */
+ },
+ .effect = {
+ S5K4ECGX_REGSET(IMAGE_EFFECT_NONE, s5k4ecgx_Effect_Normal),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_BNW, s5k4ecgx_Effect_Black_White),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_SEPIA, s5k4ecgx_Effect_Sepia),
+ S5K4ECGX_REGSET(IMAGE_EFFECT_NEGATIVE,
+ s5k4ecgx_Effect_Negative),
+ },
+ .white_balance = {
+ S5K4ECGX_REGSET(WHITE_BALANCE_AUTO, s5k4ecgx_WB_Auto),
+ S5K4ECGX_REGSET(WHITE_BALANCE_SUNNY, s5k4ecgx_WB_Sunny),
+ S5K4ECGX_REGSET(WHITE_BALANCE_CLOUDY, s5k4ecgx_WB_Cloudy),
+ S5K4ECGX_REGSET(WHITE_BALANCE_TUNGSTEN, s5k4ecgx_WB_Tungsten),
+ S5K4ECGX_REGSET(WHITE_BALANCE_FLUORESCENT,
+ s5k4ecgx_WB_Fluorescent),
+ },
+ .preview_size = {
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_QCIF, s5k4ecgx_176_Preview),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_CIF, s5k4ecgx_352_Preview),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_VGA, s5k4ecgx_640_Preview),
+ S5K4ECGX_REGSET(S5K4ECGX_PREVIEW_D1, s5k4ecgx_720_Preview),
+ },
+ .capture_size = {
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_VGA, s5k4ecgx_VGA_Capture),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_1MP, s5k4ecgx_1M_Capture),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_2MP, s5k4ecgx_2M_Capture),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_3MP, s5k4ecgx_3M_Capture),
+ S5K4ECGX_REGSET(S5K4ECGX_CAPTURE_5MP, s5k4ecgx_5M_Capture),
+ },
+ .scene_mode = {
+ S5K4ECGX_REGSET(SCENE_MODE_NONE, s5k4ecgx_Scene_Default),
+ S5K4ECGX_REGSET(SCENE_MODE_PORTRAIT, s5k4ecgx_Scene_Portrait),
+ S5K4ECGX_REGSET(SCENE_MODE_NIGHTSHOT, s5k4ecgx_Scene_Nightshot),
+ S5K4ECGX_REGSET(SCENE_MODE_LANDSCAPE, s5k4ecgx_Scene_Landscape),
+ S5K4ECGX_REGSET(SCENE_MODE_SPORTS, s5k4ecgx_Scene_Sports),
+ S5K4ECGX_REGSET(SCENE_MODE_PARTY_INDOOR,
+ s5k4ecgx_Scene_Party_Indoor),
+ S5K4ECGX_REGSET(SCENE_MODE_BEACH_SNOW,
+ s5k4ecgx_Scene_Beach_Snow),
+ S5K4ECGX_REGSET(SCENE_MODE_SUNSET, s5k4ecgx_Scene_Sunset),
+ S5K4ECGX_REGSET(SCENE_MODE_FIREWORKS, s5k4ecgx_Scene_Fireworks),
+ S5K4ECGX_REGSET(SCENE_MODE_CANDLE_LIGHT,
+ s5k4ecgx_Scene_Candle_Light),
+ },
+ .saturation = {
+ S5K4ECGX_REGSET(SATURATION_MINUS_2,
+ s5k4ecgx_Saturation_Minus_2),
+ S5K4ECGX_REGSET(SATURATION_MINUS_1,
+ s5k4ecgx_Saturation_Minus_1),
+ S5K4ECGX_REGSET(SATURATION_DEFAULT,
+ s5k4ecgx_Saturation_Default),
+ S5K4ECGX_REGSET(SATURATION_PLUS_1, s5k4ecgx_Saturation_Plus_1),
+ S5K4ECGX_REGSET(SATURATION_PLUS_2, s5k4ecgx_Saturation_Plus_2),
+ },
+ .contrast = {
+ S5K4ECGX_REGSET(CONTRAST_MINUS_2, s5k4ecgx_Contrast_Minus_2),
+ S5K4ECGX_REGSET(CONTRAST_MINUS_1, s5k4ecgx_Contrast_Minus_1),
+ S5K4ECGX_REGSET(CONTRAST_DEFAULT, s5k4ecgx_Contrast_Default),
+ S5K4ECGX_REGSET(CONTRAST_PLUS_1, s5k4ecgx_Contrast_Plus_1),
+ S5K4ECGX_REGSET(CONTRAST_PLUS_2, s5k4ecgx_Contrast_Plus_2),
+ },
+ .sharpness = {
+ S5K4ECGX_REGSET(SHARPNESS_MINUS_2, s5k4ecgx_Sharpness_Minus_2),
+ S5K4ECGX_REGSET(SHARPNESS_MINUS_1, s5k4ecgx_Sharpness_Minus_1),
+ S5K4ECGX_REGSET(SHARPNESS_DEFAULT, s5k4ecgx_Sharpness_Default),
+ S5K4ECGX_REGSET(SHARPNESS_PLUS_1, s5k4ecgx_Sharpness_Plus_1),
+ S5K4ECGX_REGSET(SHARPNESS_PLUS_2, s5k4ecgx_Sharpness_Plus_2),
+ },
+ .fps = {
+ S5K4ECGX_REGSET(FRAME_RATE_AUTO, s5k4ecgx_FPS_Auto),
+ S5K4ECGX_REGSET(FRAME_RATE_7, s5k4ecgx_FPS_7),
+ S5K4ECGX_REGSET(FRAME_RATE_15, s5k4ecgx_FPS_15),
+ S5K4ECGX_REGSET(FRAME_RATE_30, s5k4ecgx_FPS_30),
+ },
+ .preview_return = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Preview_Return),
+ .jpeg_quality_high = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_High),
+ .jpeg_quality_normal =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_Normal),
+ .jpeg_quality_low = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Jpeg_Quality_Low),
+ .flash_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_Start),
+ .flash_end = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_End),
+ .af_assist_flash_start =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Pre_Flash_Start),
+ .af_assist_flash_end =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Pre_Flash_End),
+ .af_low_light_mode_on =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Low_Light_Mode_On),
+ .af_low_light_mode_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Low_Light_Mode_Off),
+ .ae_awb_lock_on =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AE_AWB_Lock_On),
+ .ae_awb_lock_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AE_AWB_Lock_Off),
+ .low_cap_on = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Low_Cap_On),
+ .low_cap_off = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Low_Cap_Off),
+ .wdr_on = S5K4ECGX_REGSET_TABLE(s5k4ecgx_WDR_on),
+ .wdr_off = S5K4ECGX_REGSET_TABLE(s5k4ecgx_WDR_off),
+ .face_detection_on = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Face_Detection_On),
+ .face_detection_off =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Face_Detection_Off),
+ .capture_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Capture_Start),
+ .af_macro_mode_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_1),
+ .af_macro_mode_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_2),
+ .af_macro_mode_3 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Macro_mode_3),
+ .af_normal_mode_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_1),
+ .af_normal_mode_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_2),
+ .af_normal_mode_3 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Normal_mode_3),
+ .af_return_macro_position =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_AF_Return_Macro_pos),
+ .single_af_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Start),
+ .single_af_off_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Off_1),
+ .single_af_off_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Single_AF_Off_2),
+ .dtp_start = S5K4ECGX_REGSET_TABLE(s5k4ecgx_DTP_init),
+ .dtp_stop = S5K4ECGX_REGSET_TABLE(s5k4ecgx_DTP_stop),
+ .init_reg_1 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_init_reg1),
+ .init_reg_2 = S5K4ECGX_REGSET_TABLE(s5k4ecgx_init_reg2),
+ .flash_init = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Flash_init),
+ .reset_crop = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Reset_Crop),
+ .get_ae_stable_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_Get_AE_Stable_Status),
+ .get_light_level = S5K4ECGX_REGSET_TABLE(s5k4ecgx_Get_Light_Level),
+ .get_1st_af_search_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_1st_af_search_status),
+ .get_2nd_af_search_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_2nd_af_search_status),
+ .get_capture_status =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_capture_status),
+ .get_esd_status = S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_esd_status),
+ .get_iso = S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_iso_reg),
+ .get_shutterspeed =
+ S5K4ECGX_REGSET_TABLE(s5k4ecgx_get_shutterspeed_reg),
+};
+#endif
+
+struct s5k4ecgx_state {
+ struct s5k4ecgx_platform_data *pdata;
+ struct v4l2_subdev sd;
+ struct v4l2_pix_format pix;
+ struct v4l2_fract timeperframe;
+ struct s5k4ecgx_jpeg_param jpeg;
+ struct s5k4ecgx_version fw;
+ struct s5k4ecgx_version prm;
+ struct s5k4ecgx_date_info dateinfo;
+ struct s5k4ecgx_position position;
+ struct v4l2_streamparm strm;
+ struct s5k4ecgx_gps_info gps_info;
+ struct mutex ctrl_lock;
+ enum s5k4ecgx_runmode runmode;
+ enum s5k4ecgx_oprmode oprmode;
+ enum af_operation_status af_status;
+ int preview_framesize_index;
+ int capture_framesize_index;
+ int sensor_version;
+ int freq; /* MCLK in Hz */
+ int check_dataline;
+ int check_previewdata;
+ bool flash_on;
+ bool torch_on;
+ bool sensor_af_in_low_light_mode;
+ bool flash_state_on_previous_capture;
+ bool initialized;
+ bool restore_preview_size_needed;
+ int one_frame_delay_ms;
+ const struct s5k4ecgx_regs *regs;
+};
+
+static const struct v4l2_mbus_framefmt capture_fmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_FIXED,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+/**
+ * s5k4ecgx_i2c_read_twobyte: Read 2 bytes from sensor
+ */
+static int s5k4ecgx_i2c_read_twobyte(struct i2c_client *client,
+ u16 subaddr, u16 *data)
+{
+ int err;
+ unsigned char buf[2];
+ struct i2c_msg msg[2];
+
+ cpu_to_be16s(&subaddr);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *)&subaddr;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ err = i2c_transfer(client->adapter, msg, 2);
+ if (unlikely(err != 2)) {
+ dev_err(&client->dev,
+ "%s: register read fail\n", __func__);
+ return -EIO;
+ }
+
+ *data = ((buf[0] << 8) | buf[1]);
+
+ return 0;
+}
+
+/**
+ * s5k4ecgx_i2c_write_twobyte: Write (I2C) multiple bytes to the camera sensor
+ * @client: pointer to i2c_client
+ * @cmd: command register
+ * @w_data: data to be written
+ * @w_len: length of data to be written
+ *
+ * Returns 0 on success, <0 on error
+ */
+static int s5k4ecgx_i2c_write_twobyte(struct i2c_client *client,
+ u16 addr, u16 w_data)
+{
+ int retry_count = 5;
+ unsigned char buf[4];
+ struct i2c_msg msg = {client->addr, 0, 4, buf};
+ int ret = 0;
+
+ buf[0] = addr >> 8;
+ buf[1] = addr;
+ buf[2] = w_data >> 8;
+ buf[3] = w_data & 0xff;
+
+ s5k4ecgx_debug(S5K4ECGX_DEBUG_I2C, "%s : W(0x%02X%02X%02X%02X)\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+
+ do {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (likely(ret == 1))
+ break;
+ msleep(POLL_TIME_MS);
+ dev_err(&client->dev, "%s: I2C err %d, retry %d.\n",
+ __func__, ret, retry_count);
+ } while (retry_count-- > 0);
+ if (ret != 1) {
+ dev_err(&client->dev, "%s: I2C is not working.\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int s5k4ecgx_write_regs(struct v4l2_subdev *sd, const u32 regs[],
+ int size)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i, err;
+
+ for (i = 0; i < size; i++) {
+ err = s5k4ecgx_i2c_write_twobyte(client,
+ (regs[i] >> 16), regs[i]);
+ if (unlikely(err != 0)) {
+ dev_err(&client->dev,
+ "%s: register write failed\n", __func__);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int s5k4ecgx_set_from_table(struct v4l2_subdev *sd,
+ const char *setting_name,
+ const struct s5k4ecgx_regset_table *table,
+ int table_size, int index)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ dev_dbg(&client->dev, "%s: set %s index %d\n",
+ __func__, setting_name, index);
+ if ((index < 0) || (index >= table_size)) {
+ dev_err(&client->dev,
+ "%s: index(%d) out of range[0:%d] for table for %s\n",
+ __func__, index, table_size, setting_name);
+ return -EINVAL;
+ }
+ table += index;
+ if (table->reg == NULL)
+ return -EINVAL;
+ return s5k4ecgx_write_regs(sd, table->reg, table->array_size);
+}
+
+static int s5k4ecgx_set_parameter(struct v4l2_subdev *sd,
+ int *current_value_ptr,
+ int new_value,
+ const char *setting_name,
+ const struct s5k4ecgx_regset_table *table,
+ int table_size)
+{
+ int err;
+
+ if (*current_value_ptr == new_value)
+ return 0;
+
+ err = s5k4ecgx_set_from_table(sd, setting_name, table,
+ table_size, new_value);
+
+ if (!err)
+ *current_value_ptr = new_value;
+ return err;
+}
+
+static int s5k4ecgx_set_preview_stop(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING)
+ state->runmode = S5K4ECGX_RUNMODE_IDLE;
+
+ dev_dbg(&client->dev, "%s:\n", __func__);
+
+ return 0;
+}
+
+static int s5k4ecgx_set_preview_start(struct v4l2_subdev *sd)
+{
+ int err;
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ bool set_size = true;
+
+ dev_dbg(&client->dev, "%s: runmode = %d\n",
+ __func__, state->runmode);
+
+ if (!state->pix.width || !state->pix.height ||
+ !state->strm.parm.capture.timeperframe.denominator)
+ return -EINVAL;
+
+ if (state->runmode == S5K4ECGX_RUNMODE_CAPTURE) {
+ dev_dbg(&client->dev, "%s: sending Preview_Return cmd\n",
+ __func__);
+ err = s5k4ecgx_set_from_table(sd, "preview return",
+ &state->regs->preview_return, 1, 0);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed: s5k4ecgx_Preview_Return\n",
+ __func__);
+ return -EIO;
+ }
+ set_size = state->restore_preview_size_needed;
+ }
+
+ if (set_size) {
+ err = s5k4ecgx_set_from_table(sd, "preview_size",
+ state->regs->preview_size,
+ ARRAY_SIZE(state->regs->preview_size),
+ state->preview_framesize_index);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed: Could not set preview size\n",
+ __func__);
+ return -EIO;
+ }
+ }
+
+ dev_dbg(&client->dev, "%s: runmode now RUNNING\n", __func__);
+ state->runmode = S5K4ECGX_RUNMODE_RUNNING;
+
+ return 0;
+}
+
+static int s5k4ecgx_set_capture_size(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ int err;
+
+ dev_dbg(&client->dev, "%s: index:%d\n", __func__,
+ state->capture_framesize_index);
+
+ err = s5k4ecgx_set_from_table(sd, "capture_size",
+ state->regs->capture_size,
+ ARRAY_SIZE(state->regs->capture_size),
+ state->capture_framesize_index);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed: i2c_write for capture_size index %d\n",
+ __func__, state->capture_framesize_index);
+ }
+ state->runmode = S5K4ECGX_RUNMODE_CAPTURE;
+
+ return err;
+}
+
+static int s5k4ecgx_set_jpeg_quality(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ dev_dbg(&client->dev,
+ "%s: jpeg.quality %d\n", __func__, state->jpeg.quality);
+ if (state->jpeg.quality < 0)
+ state->jpeg.quality = 0;
+ if (state->jpeg.quality > 100)
+ state->jpeg.quality = 100;
+
+ switch (state->jpeg.quality) {
+ case 90 ... 100:
+ dev_dbg(&client->dev,
+ "%s: setting to high jpeg quality\n", __func__);
+ return s5k4ecgx_set_from_table(sd, "jpeg quality high",
+ &state->regs->jpeg_quality_high, 1, 0);
+ case 80 ... 89:
+ dev_dbg(&client->dev,
+ "%s: setting to normal jpeg quality\n", __func__);
+ return s5k4ecgx_set_from_table(sd, "jpeg quality normal",
+ &state->regs->jpeg_quality_normal, 1, 0);
+ default:
+ dev_dbg(&client->dev,
+ "%s: setting to low jpeg quality\n", __func__);
+ return s5k4ecgx_set_from_table(sd, "jpeg quality low",
+ &state->regs->jpeg_quality_low, 1, 0);
+ }
+}
+
+static u16 s5k4ecgx_get_light_level(struct v4l2_subdev *sd)
+{
+ int err;
+ u16 read_value = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ err = s5k4ecgx_set_from_table(sd, "get light level",
+ &state->regs->get_light_level, 1, 0);
+ if (err) {
+ dev_err(&client->dev,
+ "%s: write cmd failed, returning 0\n", __func__);
+ goto out;
+ }
+ err = s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ if (err) {
+ dev_err(&client->dev,
+ "%s: read cmd failed, returning 0\n", __func__);
+ goto out;
+ }
+
+ dev_dbg(&client->dev, "%s: read_value = %d (0x%X)\n",
+ __func__, read_value, read_value);
+
+out:
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+ return read_value;
+}
+
+static int s5k4ecgx_start_capture(struct v4l2_subdev *sd)
+{
+ int err;
+ u16 read_value;
+ u16 light_level;
+ int poll_time_ms;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+
+ /* reset cropping if our current preview is not 640x480,
+ * otherwise the capture will be wrong because of the cropping
+ */
+ if (state->preview_framesize_index != S5K4ECGX_PREVIEW_VGA) {
+ int err = s5k4ecgx_set_from_table(sd, "reset crop",
+ &state->regs->reset_crop, 1, 0);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed: Could not set preview size\n",
+ __func__);
+ return -EIO;
+ }
+ state->restore_preview_size_needed = true;
+ } else
+ state->restore_preview_size_needed = false;
+
+ msleep(50);
+ light_level = s5k4ecgx_get_light_level(sd);
+
+ dev_dbg(&client->dev, "%s: light_level = %d\n", __func__,
+ light_level);
+
+ state->flash_state_on_previous_capture = false;
+ if (parms->scene_mode != SCENE_MODE_NIGHTSHOT) {
+ switch (parms->flash_mode) {
+ case FLASH_MODE_AUTO:
+ if (light_level > LOW_LIGHT_LEVEL) {
+ /* light level bright enough
+ * that we don't need flash
+ */
+ break;
+ }
+ /* fall through to flash start */
+ case FLASH_MODE_ON:
+ if (parms->focus_mode == FOCUS_MODE_INFINITY) {
+ s5k4ecgx_set_from_table(sd,
+ "AF assist flash start",
+ &state->regs->af_assist_flash_start,
+ 1, 0);
+ s5k4ecgx_set_from_table(sd,
+ "AF assist flash end",
+ &state->regs->af_assist_flash_end,
+ 1, 0);
+ msleep(10);
+ }
+ s5k4ecgx_set_from_table(sd, "flash start",
+ &state->regs->flash_start, 1, 0);
+ state->flash_on = true;
+ state->flash_state_on_previous_capture = true;
+ pdata->flash_onoff(1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* if light is low, use low light capture settings, EXCEPT
+ * if scene mode set to NIGHTSHOT or SPORTS because they
+ * have their own settings (though a low light sport setting
+ * could be useful)
+ */
+ if ((light_level <= HIGH_LIGHT_LEVEL) &&
+ (parms->scene_mode != SCENE_MODE_NIGHTSHOT) &&
+ (parms->scene_mode != SCENE_MODE_SPORTS)) {
+ s5k4ecgx_set_from_table(sd, "low cap on",
+ &state->regs->low_cap_on, 1, 0);
+ }
+
+ err = s5k4ecgx_set_capture_size(sd);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed: i2c_write for capture_resolution\n",
+ __func__);
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev, "%s: send Capture_Start cmd\n", __func__);
+ s5k4ecgx_set_from_table(sd, "capture start",
+ &state->regs->capture_start, 1, 0);
+
+ /* a shot takes takes at least 50ms so sleep that amount first
+ * and then start polling for completion.
+ */
+ msleep(50);
+ /* Enter read mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x002C, 0x7000);
+ poll_time_ms = 50;
+ do {
+ s5k4ecgx_set_from_table(sd, "get capture status",
+ &state->regs->get_capture_status, 1, 0);
+ s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ dev_dbg(&client->dev,
+ "%s: s5k4ecgx_Capture_Start check = %#x\n",
+ __func__, read_value);
+ if (read_value != 0x00)
+ break;
+ msleep(POLL_TIME_MS);
+ poll_time_ms += POLL_TIME_MS;
+ } while (poll_time_ms < CAPTURE_POLL_TIME_MS);
+
+ dev_dbg(&client->dev, "%s: capture done check finished after %d ms\n",
+ __func__, poll_time_ms);
+
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ s5k4ecgx_set_from_table(sd, "ae awb lock off",
+ &state->regs->ae_awb_lock_off, 1, 0);
+
+ if ((light_level <= HIGH_LIGHT_LEVEL) &&
+ (parms->scene_mode != SCENE_MODE_NIGHTSHOT) &&
+ (parms->scene_mode != SCENE_MODE_SPORTS)) {
+ s5k4ecgx_set_from_table(sd, "low cap off",
+ &state->regs->low_cap_off, 1, 0);
+ }
+
+ if ((parms->scene_mode != SCENE_MODE_NIGHTSHOT) && (state->flash_on)) {
+ state->flash_on = false;
+ pdata->flash_onoff(0);
+ s5k4ecgx_set_from_table(sd, "flash end",
+ &state->regs->flash_end, 1, 0);
+ }
+
+ return 0;
+}
+
+/* wide dynamic range support */
+static int s5k4ecgx_set_wdr(struct v4l2_subdev *sd, int value)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ if (value == WDR_ON)
+ return s5k4ecgx_set_from_table(sd, "wdr on",
+ &state->regs->wdr_on, 1, 0);
+ return s5k4ecgx_set_from_table(sd, "wdr off",
+ &state->regs->wdr_off, 1, 0);
+}
+
+static int s5k4ecgx_set_face_detection(struct v4l2_subdev *sd, int value)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ if (value == FACE_DETECTION_ON)
+ return s5k4ecgx_set_from_table(sd, "face detection on",
+ &state->regs->face_detection_on, 1, 0);
+ return s5k4ecgx_set_from_table(sd, "face detection off",
+ &state->regs->face_detection_off, 1, 0);
+}
+
+static int s5k4ecgx_return_focus(struct v4l2_subdev *sd)
+{
+ int err;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 1",
+ &state->regs->af_normal_mode_1, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(FIRST_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 2",
+ &state->regs->af_normal_mode_2, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(SECOND_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 3",
+ &state->regs->af_normal_mode_3, 1, 0);
+ if (err < 0)
+ goto fail;
+
+ return 0;
+fail:
+ dev_err(&client->dev,
+ "%s: i2c_write failed\n", __func__);
+ return -EIO;
+}
+
+static int s5k4ecgx_set_focus_mode(struct v4l2_subdev *sd, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+ int err;
+
+ if (parms->focus_mode == value)
+ return 0;
+
+ dev_dbg(&client->dev, "%s value(%d)\n", __func__, value);
+
+ switch (value) {
+ case FOCUS_MODE_MACRO:
+ dev_dbg(&client->dev,
+ "%s: FOCUS_MODE_MACRO\n", __func__);
+ err = s5k4ecgx_set_from_table(sd, "af macro mode 1",
+ &state->regs->af_macro_mode_1, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(FIRST_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd, "af macro mode 2",
+ &state->regs->af_macro_mode_2, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(SECOND_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd, "af macro mode 3",
+ &state->regs->af_macro_mode_3, 1, 0);
+ if (err < 0)
+ goto fail;
+ parms->focus_mode = FOCUS_MODE_MACRO;
+ break;
+
+ case FOCUS_MODE_INFINITY:
+ case FOCUS_MODE_AUTO:
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 1",
+ &state->regs->af_normal_mode_1, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(FIRST_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 2",
+ &state->regs->af_normal_mode_2, 1, 0);
+ if (err < 0)
+ goto fail;
+ msleep(SECOND_SETTING_FOCUS_MODE_DELAY_MS);
+ err = s5k4ecgx_set_from_table(sd,
+ "af normal mode 3",
+ &state->regs->af_normal_mode_3, 1, 0);
+ if (err < 0)
+ goto fail;
+ parms->focus_mode = value;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+fail:
+ dev_err(&client->dev,
+ "%s: i2c_write failed\n", __func__);
+ return -EIO;
+}
+
+static void s5k4ecgx_auto_focus_flash_start(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+ int count;
+ u16 read_value;
+
+ s5k4ecgx_set_from_table(sd, "AF assist flash start",
+ &state->regs->af_assist_flash_start, 1, 0);
+ state->flash_on = true;
+ pdata->af_assist_onoff(1);
+
+ /* delay 200ms (SLSI value) and then poll to see if AE is stable.
+ * once it is stable, lock it and then return to do AF
+ */
+ msleep(200);
+
+ /* enter read mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x002C, 0x7000);
+ for (count = 0; count < AE_STABLE_SEARCH_COUNT; count++) {
+ if (state->af_status == AF_CANCEL)
+ break;
+ s5k4ecgx_set_from_table(sd, "get ae stable status",
+ &state->regs->get_ae_stable_status, 1, 0);
+ s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ dev_dbg(&client->dev, "%s: ae stable status = %#x\n",
+ __func__, read_value);
+ if (read_value == 0x1)
+ break;
+ msleep(state->one_frame_delay_ms);
+ }
+
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ /* if we were cancelled, turn off flash */
+ if (state->af_status == AF_CANCEL) {
+ dev_dbg(&client->dev,
+ "%s: AF cancelled\n", __func__);
+ s5k4ecgx_set_from_table(sd, "AF assist flash end",
+ &state->regs->af_assist_flash_end, 1, 0);
+ state->flash_on = false;
+ pdata->af_assist_onoff(0);
+ }
+}
+
+static int s5k4ecgx_start_auto_focus(struct v4l2_subdev *sd)
+{
+ int light_level;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+
+ dev_dbg(&client->dev, "%s: start SINGLE AF operation, flash mode %d\n",
+ __func__, parms->flash_mode);
+
+ /* in case user calls auto_focus repeatedly without a cancel
+ * or a capture, we need to cancel here to allow ae_awb
+ * to work again, or else we could be locked forever while
+ * that app is running, which is not the expected behavior.
+ */
+ s5k4ecgx_set_from_table(sd, "ae awb lock off",
+ &state->regs->ae_awb_lock_off, 1, 0);
+
+ if (parms->scene_mode == SCENE_MODE_NIGHTSHOT) {
+ /* user selected night shot mode, assume we need low light
+ * af mode. flash is always off in night shot mode
+ */
+ goto enable_af_low_light_mode;
+ }
+
+ light_level = s5k4ecgx_get_light_level(sd);
+
+ switch (parms->flash_mode) {
+ case FLASH_MODE_AUTO:
+ if (light_level > LOW_LIGHT_LEVEL) {
+ /* flash not needed */
+ break;
+ }
+ /* fall through to turn on flash for AF assist */
+ case FLASH_MODE_ON:
+ s5k4ecgx_auto_focus_flash_start(sd);
+ if (state->af_status == AF_CANCEL)
+ return 0;
+ break;
+ case FLASH_MODE_OFF:
+ break;
+ default:
+ dev_err(&client->dev,
+ "%s: Unknown Flash mode 0x%x\n",
+ __func__, parms->flash_mode);
+ break;
+ }
+
+ if (light_level > LOW_LIGHT_LEVEL) {
+ if (state->sensor_af_in_low_light_mode) {
+ state->sensor_af_in_low_light_mode = false;
+ s5k4ecgx_set_from_table(sd, "af low light mode off",
+ &state->regs->af_low_light_mode_off, 1, 0);
+ }
+ } else {
+enable_af_low_light_mode:
+ if (!state->sensor_af_in_low_light_mode) {
+ state->sensor_af_in_low_light_mode = true;
+ s5k4ecgx_set_from_table(sd, "af low light mode on",
+ &state->regs->af_low_light_mode_on, 1, 0);
+ }
+ }
+
+ s5k4ecgx_set_from_table(sd, "single af start",
+ &state->regs->single_af_start, 1, 0);
+ state->af_status = AF_INITIAL;
+ dev_dbg(&client->dev, "%s: af_status set to start\n", __func__);
+
+ return 0;
+}
+
+/* called by HAL after auto focus was finished.
+ * it might off the assist flash
+ */
+static int s5k4ecgx_finish_auto_focus(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ if (state->flash_on) {
+ struct s5k4ecgx_platform_data *pd = client->dev.platform_data;
+ s5k4ecgx_set_from_table(sd, "AF assist flash end",
+ &state->regs->af_assist_flash_end, 1, 0);
+ state->flash_on = false;
+ pd->af_assist_onoff(0);
+ }
+
+ dev_dbg(&client->dev, "%s: single AF finished\n", __func__);
+ state->af_status = AF_NONE;
+ return 0;
+}
+
+static int s5k4ecgx_stop_auto_focus(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+ int focus_mode = parms->focus_mode;
+
+ dev_dbg(&client->dev, "%s: single AF Off command Setting\n", __func__);
+
+ /* always cancel ae_awb, in case AF already finished before
+ * we got called.
+ */
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ s5k4ecgx_set_from_table(sd, "ae awb lock off",
+ &state->regs->ae_awb_lock_off, 1, 0);
+ if (state->flash_on)
+ s5k4ecgx_finish_auto_focus(sd);
+
+ if (state->af_status != AF_START) {
+ /* we weren't in the middle auto focus operation, we're done */
+ dev_dbg(&client->dev,
+ "%s: auto focus not in progress, done\n", __func__);
+
+ if (focus_mode == FOCUS_MODE_MACRO) {
+ /* for change focus mode forcely */
+ parms->focus_mode = -1;
+ s5k4ecgx_set_focus_mode(sd, FOCUS_MODE_MACRO);
+ } else if (focus_mode == FOCUS_MODE_AUTO) {
+ /* for change focus mode forcely */
+ parms->focus_mode = -1;
+ s5k4ecgx_set_focus_mode(sd, FOCUS_MODE_AUTO);
+ }
+
+ return 0;
+ }
+
+ /* auto focus was in progress. the other thread
+ * is either in the middle of s5k4ecgx_get_auto_focus_result_first(),
+ * s5k4ecgx_get_auto_focus_result_second()
+ * or will call it shortly. set a flag to have
+ * it abort it's polling. that thread will
+ * also do cleanup like restore focus position.
+ *
+ * it might be enough to just send sensor commands
+ * to abort auto focus and the other thread would get
+ * that state from it's polling calls, but I'm not sure.
+ */
+ state->af_status = AF_CANCEL;
+ dev_dbg(&client->dev,
+ "%s: sending Single_AF_Off commands to sensor\n", __func__);
+
+ s5k4ecgx_set_from_table(sd, "single af off 1",
+ &state->regs->single_af_off_1, 1, 0);
+
+ msleep(state->one_frame_delay_ms);
+
+ s5k4ecgx_set_from_table(sd, "single af off 2",
+ &state->regs->single_af_off_2, 1, 0);
+
+ return 0;
+}
+
+/* called by HAL after auto focus was started to get the first search result*/
+static int s5k4ecgx_get_auto_focus_result_first(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ u16 read_value;
+
+ if (state->af_status == AF_INITIAL) {
+ dev_dbg(&client->dev, "%s: Check AF Result\n", __func__);
+ if (state->af_status == AF_NONE) {
+ dev_dbg(&client->dev,
+ "%s: auto focus never started, returning 0x2\n",
+ __func__);
+ ctrl->value = AUTO_FOCUS_CANCELLED;
+ return 0;
+ }
+
+ /* must delay 2 frame times before checking result of 1st phase */
+ mutex_unlock(&state->ctrl_lock);
+ msleep(state->one_frame_delay_ms*2);
+ mutex_lock(&state->ctrl_lock);
+
+ /* lock AE and AWB after first AF search */
+ s5k4ecgx_set_from_table(sd, "ae awb lock on",
+ &state->regs->ae_awb_lock_on, 1, 0);
+
+ dev_dbg(&client->dev, "%s: 1st AF search\n", __func__);
+ /* enter read mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x002C, 0x7000);
+ state->af_status = AF_START;
+ } else if (state->af_status == AF_CANCEL) {
+ dev_dbg(&client->dev,
+ "%s: AF is cancelled while doing\n", __func__);
+ ctrl->value = AUTO_FOCUS_CANCELLED;
+ s5k4ecgx_finish_auto_focus(sd);
+ return 0;
+ }
+ s5k4ecgx_set_from_table(sd, "get 1st af search status",
+ &state->regs->get_1st_af_search_status,
+ 1, 0);
+ s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ dev_dbg(&client->dev,
+ "%s: 1st i2c_read --- read_value == 0x%x\n",
+ __func__, read_value);
+ ctrl->value = read_value;
+ return 0;
+}
+
+/* called by HAL after first search was succeed to get the second search result*/
+static int s5k4ecgx_get_auto_focus_result_second(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ u16 read_value;
+
+ if (state->af_status == AF_CANCEL) {
+ dev_dbg(&client->dev,
+ "%s: AF is cancelled while doing\n", __func__);
+ ctrl->value = AUTO_FOCUS_CANCELLED;
+ s5k4ecgx_finish_auto_focus(sd);
+ return 0;
+ }
+ s5k4ecgx_set_from_table(sd, "get 2nd af search status",
+ &state->regs->get_2nd_af_search_status,
+ 1, 0);
+ s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ dev_dbg(&client->dev,
+ "%s: 2nd i2c_read --- read_value == 0x%x\n",
+ __func__, read_value);
+ ctrl->value = read_value;
+ return 0;
+}
+
+static void s5k4ecgx_init_parameters(struct v4l2_subdev *sd)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+
+ state->strm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parms->capture.capturemode = 0;
+ parms->capture.timeperframe.numerator = 1;
+ parms->capture.timeperframe.denominator = 30;
+ parms->contrast = CONTRAST_DEFAULT;
+ parms->effects = IMAGE_EFFECT_NONE;
+ parms->brightness = EV_DEFAULT;
+ parms->flash_mode = FLASH_MODE_AUTO;
+ parms->focus_mode = FOCUS_MODE_AUTO;
+ parms->iso = ISO_AUTO;
+ parms->metering = METERING_CENTER;
+ parms->saturation = SATURATION_DEFAULT;
+ parms->scene_mode = SCENE_MODE_NONE;
+ parms->sharpness = SHARPNESS_DEFAULT;
+ parms->white_balance = WHITE_BALANCE_AUTO;
+
+ state->jpeg.enable = 0;
+ state->jpeg.quality = 100;
+ state->jpeg.main_offset = 0;
+ state->jpeg.main_size = 0;
+ state->jpeg.thumb_offset = 0;
+ state->jpeg.thumb_size = 0;
+ state->jpeg.postview_offset = 0;
+
+ state->fw.major = 1;
+
+ state->one_frame_delay_ms = NORMAL_MODE_MAX_ONE_FRAME_DELAY_MS;
+}
+
+static void s5k4ecgx_set_framesize(struct v4l2_subdev *sd,
+ const struct s5k4ecgx_framesize *frmsize,
+ int frmsize_count, bool exact_match);
+static int s5k4ecgx_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ dev_dbg(&client->dev, "%s: code = 0x%x, field = 0x%x,"
+ " colorspace = 0x%x, width = %d, height = %d\n",
+ __func__, fmt->code, fmt->field,
+ fmt->colorspace,
+ fmt->width, fmt->height);
+
+ if (fmt->code == V4L2_MBUS_FMT_FIXED &&
+ fmt->colorspace != V4L2_COLORSPACE_JPEG) {
+ dev_err(&client->dev,
+ "%s: mismatch in pixelformat and colorspace\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ state->pix.width = fmt->width;
+ state->pix.height = fmt->height;
+ if (fmt->colorspace == V4L2_COLORSPACE_JPEG)
+ state->pix.pixelformat = V4L2_PIX_FMT_JPEG;
+ else
+ state->pix.pixelformat = 0; /* is this used anywhere? */
+
+ if (fmt->colorspace == V4L2_COLORSPACE_JPEG) {
+ state->oprmode = S5K4ECGX_OPRMODE_IMAGE;
+ /*
+ * In case of image capture mode,
+ * if the given image resolution is not supported,
+ * use the next higher image resolution. */
+ s5k4ecgx_set_framesize(sd, s5k4ecgx_capture_framesize_list,
+ ARRAY_SIZE(s5k4ecgx_capture_framesize_list),
+ false);
+
+ } else {
+ state->oprmode = S5K4ECGX_OPRMODE_VIDEO;
+ /*
+ * In case of video mode,
+ * if the given video resolution is not matching, use
+ * the default rate (currently S5K4ECGX_PREVIEW_WVGA).
+ */
+ s5k4ecgx_set_framesize(sd, s5k4ecgx_preview_framesize_list,
+ ARRAY_SIZE(s5k4ecgx_preview_framesize_list),
+ true);
+ }
+
+ state->jpeg.enable = state->pix.pixelformat == V4L2_PIX_FMT_JPEG;
+
+ return 0;
+}
+
+static int s5k4ecgx_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ /* The camera interface should read this value, this is the resolution
+ * at which the sensor would provide framedata to the camera i/f
+ *
+ * In case of image capture,
+ * this returns the default camera resolution (SVGA)
+ */
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = state->pix.width;
+ fsize->discrete.height = state->pix.height;
+ return 0;
+}
+
+static int s5k4ecgx_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ pr_debug("%s: index = %d\n", __func__, index);
+
+ if (index >= ARRAY_SIZE(capture_fmts))
+ return -EINVAL;
+
+ *code = capture_fmts[index].code;
+
+ return 0;
+}
+
+static int s5k4ecgx_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ int num_entries;
+ int i;
+
+ num_entries = ARRAY_SIZE(capture_fmts);
+
+ pr_debug("%s: code = 0x%x, colorspace = 0x%x, num_entries = %d\n",
+ __func__, fmt->code, fmt->colorspace, num_entries);
+
+ for (i = 0; i < num_entries; i++) {
+ if (capture_fmts[i].code == fmt->code &&
+ capture_fmts[i].colorspace == fmt->colorspace) {
+ pr_debug("%s: match found, returning 0\n", __func__);
+ return 0;
+ }
+ }
+
+ pr_debug("%s: no match found, returning -EINVAL\n", __func__);
+ return -EINVAL;
+}
+
+static void s5k4ecgx_enable_torch(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+
+ s5k4ecgx_set_from_table(sd, "torch start",
+ &state->regs->flash_start, 1, 0);
+ state->torch_on = true;
+ pdata->torch_onoff(1);
+}
+
+static void s5k4ecgx_disable_torch(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+
+ if (state->torch_on) {
+ state->torch_on = false;
+ pdata->torch_onoff(0);
+ s5k4ecgx_set_from_table(sd, "torch end",
+ &state->regs->flash_end, 1, 0);
+ }
+}
+static int s5k4ecgx_set_flash_mode(struct v4l2_subdev *sd, int value)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+
+ if (parms->flash_mode == value)
+ return 0;
+
+ if ((value >= FLASH_MODE_OFF) && (value <= FLASH_MODE_TORCH)) {
+ pr_debug("%s: setting flash mode to %d\n",
+ __func__, value);
+ parms->flash_mode = value;
+ if (parms->flash_mode == FLASH_MODE_TORCH)
+ s5k4ecgx_enable_torch(sd);
+ else
+ s5k4ecgx_disable_torch(sd);
+ return 0;
+ }
+ pr_debug("%s: trying to set invalid flash mode %d\n",
+ __func__, value);
+ return -EINVAL;
+}
+
+static int s5k4ecgx_g_parm(struct v4l2_subdev *sd,
+ struct v4l2_streamparm *param)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ memcpy(param, &state->strm, sizeof(param));
+ return 0;
+}
+
+static int s5k4ecgx_s_parm(struct v4l2_subdev *sd,
+ struct v4l2_streamparm *param)
+{
+ int err = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *new_parms =
+ (struct sec_cam_parm *)&param->parm.raw_data;
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+
+ dev_dbg(&client->dev, "%s: start\n", __func__);
+
+ if (param->parm.capture.timeperframe.numerator !=
+ parms->capture.timeperframe.numerator ||
+ param->parm.capture.timeperframe.denominator !=
+ parms->capture.timeperframe.denominator) {
+
+ int fps = 0;
+ int fps_max = 30;
+
+ if (param->parm.capture.timeperframe.numerator &&
+ param->parm.capture.timeperframe.denominator)
+ fps =
+ (int)(param->parm.capture.timeperframe.denominator /
+ param->parm.capture.timeperframe.numerator);
+ else
+ fps = 0;
+
+ if (fps <= 0 || fps > fps_max) {
+ dev_err(&client->dev,
+ "%s: Framerate %d not supported,"
+ " setting it to %d fps.\n",
+ __func__, fps, fps_max);
+ fps = fps_max;
+ }
+
+ /*
+ * Don't set the fps value, just update it in the state
+ * We will set the resolution and
+ * fps in the start operation (preview/capture) call
+ */
+ parms->capture.timeperframe.numerator = 1;
+ parms->capture.timeperframe.denominator = fps;
+ }
+
+ /* we return an error if one happened but don't stop trying to
+ * set all parameters passed
+ */
+ err = s5k4ecgx_set_parameter(sd, &parms->contrast, new_parms->contrast,
+ "contrast", state->regs->contrast,
+ ARRAY_SIZE(state->regs->contrast));
+ err |= s5k4ecgx_set_parameter(sd, &parms->effects, new_parms->effects,
+ "effect", state->regs->effect,
+ ARRAY_SIZE(state->regs->effect));
+ err |= s5k4ecgx_set_parameter(sd, &parms->brightness,
+ new_parms->brightness, "brightness",
+ state->regs->ev, ARRAY_SIZE(state->regs->ev));
+ err |= s5k4ecgx_set_flash_mode(sd, new_parms->flash_mode);
+ /* Must delay 150ms before setting macro mode due to a camera
+ * sensor requirement */
+ if ((new_parms->focus_mode == FOCUS_MODE_MACRO) &&
+ (parms->focus_mode != FOCUS_MODE_MACRO))
+ msleep(150);
+ err |= s5k4ecgx_set_focus_mode(sd, new_parms->focus_mode);
+ err |= s5k4ecgx_set_parameter(sd, &parms->iso, new_parms->iso,
+ "iso", state->regs->iso,
+ ARRAY_SIZE(state->regs->iso));
+ err |= s5k4ecgx_set_parameter(sd, &parms->metering, new_parms->metering,
+ "metering", state->regs->metering,
+ ARRAY_SIZE(state->regs->metering));
+ err |= s5k4ecgx_set_parameter(sd, &parms->saturation,
+ new_parms->saturation, "saturation",
+ state->regs->saturation,
+ ARRAY_SIZE(state->regs->saturation));
+ err |= s5k4ecgx_set_parameter(sd, &parms->scene_mode,
+ new_parms->scene_mode, "scene_mode",
+ state->regs->scene_mode,
+ ARRAY_SIZE(state->regs->scene_mode));
+ err |= s5k4ecgx_set_parameter(sd, &parms->sharpness,
+ new_parms->sharpness, "sharpness",
+ state->regs->sharpness,
+ ARRAY_SIZE(state->regs->sharpness));
+ err |= s5k4ecgx_set_parameter(sd, &parms->white_balance,
+ new_parms->white_balance, "white balance",
+ state->regs->white_balance,
+ ARRAY_SIZE(state->regs->white_balance));
+ err |= s5k4ecgx_set_parameter(sd, &parms->fps,
+ new_parms->fps, "fps",
+ state->regs->fps,
+ ARRAY_SIZE(state->regs->fps));
+
+ if (parms->scene_mode == SCENE_MODE_NIGHTSHOT)
+ state->one_frame_delay_ms = NIGHT_MODE_MAX_ONE_FRAME_DELAY_MS;
+ else
+ state->one_frame_delay_ms = NORMAL_MODE_MAX_ONE_FRAME_DELAY_MS;
+
+ dev_dbg(&client->dev, "%s: returning %d\n", __func__, err);
+ return err;
+}
+
+/* This function is called from the g_ctrl api
+ *
+ * This function should be called only after the s_fmt call,
+ * which sets the required width/height value.
+ *
+ * It checks a list of available frame sizes and sets the
+ * most appropriate frame size.
+ *
+ * The list is stored in an increasing order (as far as possible).
+ * Hence the first entry (searching from the beginning) where both the
+ * width and height is more than the required value is returned.
+ * In case of no perfect match, we set the last entry (which is supposed
+ * to be the largest resolution supported.)
+ */
+static void s5k4ecgx_set_framesize(struct v4l2_subdev *sd,
+ const struct s5k4ecgx_framesize *frmsize,
+ int frmsize_count, bool preview)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct s5k4ecgx_framesize *last_frmsize =
+ &frmsize[frmsize_count - 1];
+
+ dev_dbg(&client->dev, "%s: Requested Res: %dx%d\n", __func__,
+ state->pix.width, state->pix.height);
+
+ do {
+ /*
+ * In case of image capture mode,
+ * if the given image resolution is not supported,
+ * return the next higher image resolution. */
+ if (preview) {
+ if (frmsize->width == state->pix.width &&
+ frmsize->height == state->pix.height) {
+ break;
+ }
+ } else {
+ dev_dbg(&client->dev,
+ "%s: compare frmsize %dx%d to %dx%d\n",
+ __func__,
+ frmsize->width, frmsize->height,
+ state->pix.width, state->pix.height);
+ if (frmsize->width >= state->pix.width &&
+ frmsize->height >= state->pix.height) {
+ dev_dbg(&client->dev,
+ "%s: select frmsize %dx%d, index=%d\n",
+ __func__,
+ frmsize->width, frmsize->height,
+ frmsize->index);
+ break;
+ }
+ }
+
+ frmsize++;
+ } while (frmsize <= last_frmsize);
+
+ if (frmsize > last_frmsize)
+ frmsize = last_frmsize;
+
+ state->pix.width = frmsize->width;
+ state->pix.height = frmsize->height;
+ if (preview) {
+ state->preview_framesize_index = frmsize->index;
+ dev_dbg(&client->dev, "%s: Preview Res Set: %dx%d, index %d\n",
+ __func__, state->pix.width, state->pix.height,
+ state->preview_framesize_index);
+ } else {
+ state->capture_framesize_index = frmsize->index;
+ dev_dbg(&client->dev, "%s: Capture Res Set: %dx%d, index %d\n",
+ __func__, state->pix.width, state->pix.height,
+ state->capture_framesize_index);
+ }
+}
+
+static int s5k4ecgx_check_dataline_stop(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ int err;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ err = s5k4ecgx_set_from_table(sd, "DTP stop",
+ &state->regs->dtp_stop, 1, 0);
+ if (err < 0) {
+ v4l_info(client, "%s: register set failed\n", __func__);
+ return -EIO;
+ }
+
+ state->check_dataline = 0;
+
+ return err;
+}
+
+static void s5k4ecgx_get_esd_int(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 read_value;
+ int err;
+
+ if ((S5K4ECGX_RUNMODE_RUNNING == state->runmode) &&
+ (state->af_status != AF_START)) {
+ err = s5k4ecgx_set_from_table(sd, "get esd status",
+ &state->regs->get_esd_status,
+ 1, 0);
+ err |= s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+ dev_dbg(&client->dev,
+ "%s: read_value == 0x%x\n", __func__, read_value);
+ /* return to write mode */
+ err |= s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ if (err < 0) {
+ v4l_info(client,
+ "Failed I2C for getting ESD information\n");
+ ctrl->value = 0x01;
+ } else {
+ if (read_value != 0x0000) {
+ v4l_info(client, "ESD interrupt happened!!\n");
+ ctrl->value = 0x01;
+ } else {
+ dev_dbg(&client->dev,
+ "%s: No ESD interrupt!!\n", __func__);
+ ctrl->value = 0x00;
+ }
+ }
+ } else
+ ctrl->value = 0x00;
+}
+
+/* returns the real iso currently used by sensor due to lighting
+ * conditions, not the requested iso we sent using s_ctrl.
+ */
+static int s5k4ecgx_get_iso(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ int err;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ u16 read_value1 = 0;
+ u16 read_value2 = 0;
+ int read_value;
+
+ err = s5k4ecgx_set_from_table(sd, "get iso",
+ &state->regs->get_iso, 1, 0);
+ err |= s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value1);
+ err |= s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value2);
+
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ read_value = read_value1 * read_value2 / 384;
+
+ if (read_value > 0x400)
+ ctrl->value = ISO_400;
+ else if (read_value > 0x200)
+ ctrl->value = ISO_200;
+ else if (read_value > 0x100)
+ ctrl->value = ISO_100;
+ else
+ ctrl->value = ISO_50;
+
+ dev_dbg(&client->dev, "%s: get iso == %d (0x%x, 0x%x)\n",
+ __func__, ctrl->value, read_value1, read_value2);
+
+ return err;
+}
+
+static int s5k4ecgx_get_shutterspeed(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int err;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ u16 read_value_1;
+ u16 read_value_2;
+ u32 read_value;
+
+ err = s5k4ecgx_set_from_table(sd, "get shutterspeed",
+ &state->regs->get_shutterspeed, 1, 0);
+ err |= s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value_1);
+ err |= s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value_2);
+
+ read_value = (read_value_2 << 16) | (read_value_1 & 0xffff);
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+ ctrl->value = read_value * 1000 / 400;
+ dev_dbg(&client->dev,
+ "%s: get shutterspeed == %d\n", __func__, ctrl->value);
+
+ return err;
+}
+
+static int s5k4ecgx_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+ int err = 0;
+
+ if (!state->initialized) {
+ dev_err(&client->dev,
+ "%s: return error because uninitialized\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&state->ctrl_lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_WHITE_BALANCE:
+ ctrl->value = parms->white_balance;
+ break;
+ case V4L2_CID_CAMERA_EFFECT:
+ ctrl->value = parms->effects;
+ break;
+ case V4L2_CID_CAMERA_CONTRAST:
+ ctrl->value = parms->contrast;
+ break;
+ case V4L2_CID_CAMERA_SATURATION:
+ ctrl->value = parms->saturation;
+ break;
+ case V4L2_CID_CAMERA_SHARPNESS:
+ ctrl->value = parms->sharpness;
+ break;
+ case V4L2_CID_CAM_JPEG_MAIN_SIZE:
+ ctrl->value = state->jpeg.main_size;
+ break;
+ case V4L2_CID_CAM_JPEG_MAIN_OFFSET:
+ ctrl->value = state->jpeg.main_offset;
+ break;
+ case V4L2_CID_CAM_JPEG_THUMB_SIZE:
+ ctrl->value = state->jpeg.thumb_size;
+ break;
+ case V4L2_CID_CAM_JPEG_THUMB_OFFSET:
+ ctrl->value = state->jpeg.thumb_offset;
+ break;
+ case V4L2_CID_CAM_JPEG_POSTVIEW_OFFSET:
+ ctrl->value = state->jpeg.postview_offset;
+ break;
+ case V4L2_CID_CAM_JPEG_MEMSIZE:
+ ctrl->value = SENSOR_JPEG_SNAPSHOT_MEMSIZE;
+ break;
+ case V4L2_CID_CAM_JPEG_QUALITY:
+ ctrl->value = state->jpeg.quality;
+ break;
+ case V4L2_CID_CAMERA_AUTO_FOCUS_RESULT_FIRST:
+ err = s5k4ecgx_get_auto_focus_result_first(sd, ctrl);
+ break;
+ case V4L2_CID_CAMERA_AUTO_FOCUS_RESULT_SECOND:
+ err = s5k4ecgx_get_auto_focus_result_second(sd, ctrl);
+ break;
+ case V4L2_CID_CAM_DATE_INFO_YEAR:
+ ctrl->value = 2010;
+ break;
+ case V4L2_CID_CAM_DATE_INFO_MONTH:
+ ctrl->value = 2;
+ break;
+ case V4L2_CID_CAM_DATE_INFO_DATE:
+ ctrl->value = 25;
+ break;
+ case V4L2_CID_CAM_SENSOR_VER:
+ ctrl->value = 1;
+ break;
+ case V4L2_CID_CAM_FW_MINOR_VER:
+ ctrl->value = state->fw.minor;
+ break;
+ case V4L2_CID_CAM_FW_MAJOR_VER:
+ ctrl->value = state->fw.major;
+ break;
+ case V4L2_CID_CAM_PRM_MINOR_VER:
+ ctrl->value = state->prm.minor;
+ break;
+ case V4L2_CID_CAM_PRM_MAJOR_VER:
+ ctrl->value = state->prm.major;
+ break;
+ case V4L2_CID_ESD_INT:
+ s5k4ecgx_get_esd_int(sd, ctrl);
+ break;
+ case V4L2_CID_CAMERA_GET_ISO:
+ err = s5k4ecgx_get_iso(sd, ctrl);
+ break;
+ case V4L2_CID_CAMERA_GET_SHT_TIME:
+ err = s5k4ecgx_get_shutterspeed(sd, ctrl);
+ break;
+ case V4L2_CID_CAMERA_GET_FLASH_ONOFF:
+ ctrl->value = state->flash_state_on_previous_capture;
+ break;
+ case V4L2_CID_CAMERA_OBJ_TRACKING_STATUS:
+ case V4L2_CID_CAMERA_SMART_AUTO_STATUS:
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ dev_err(&client->dev, "%s: unknown ctrl id 0x%x\n",
+ __func__, ctrl->id);
+ break;
+ }
+
+ mutex_unlock(&state->ctrl_lock);
+
+ return err;
+}
+
+static int s5k4ecgx_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct sec_cam_parm *parms =
+ (struct sec_cam_parm *)&state->strm.parm.raw_data;
+ int err = 0;
+ int value = ctrl->value;
+
+ if (!state->initialized &&
+ (ctrl->id != V4L2_CID_CAMERA_CHECK_DATALINE)) {
+ dev_err(&client->dev,
+ "%s: return error because uninitialized\n", __func__);
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "%s: V4l2 control ID =%d, val = %d\n",
+ __func__, ctrl->id - V4L2_CID_PRIVATE_BASE, value);
+
+ mutex_lock(&state->ctrl_lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_FLASH_MODE:
+ err = s5k4ecgx_set_flash_mode(sd, value);
+ break;
+ case V4L2_CID_CAMERA_BRIGHTNESS:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ err = s5k4ecgx_set_parameter(sd, &parms->brightness,
+ value, "brightness",
+ state->regs->ev,
+ ARRAY_SIZE(state->regs->ev));
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set brightness when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_WHITE_BALANCE:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ err = s5k4ecgx_set_parameter(sd, &parms->white_balance,
+ value, "white balance",
+ state->regs->white_balance,
+ ARRAY_SIZE(state->regs->white_balance));
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set white balance when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_EFFECT:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ err = s5k4ecgx_set_parameter(sd, &parms->effects,
+ value, "effects", state->regs->effect,
+ ARRAY_SIZE(state->regs->effect));
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set effect when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_ISO:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ err = s5k4ecgx_set_parameter(sd, &parms->iso,
+ value, "iso",
+ state->regs->iso,
+ ARRAY_SIZE(state->regs->iso));
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set iso when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_METERING:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ err = s5k4ecgx_set_parameter(sd, &parms->metering,
+ value, "metering",
+ state->regs->metering,
+ ARRAY_SIZE(state->regs->metering));
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set metering when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_CONTRAST:
+ err = s5k4ecgx_set_parameter(sd, &parms->contrast,
+ value, "contrast",
+ state->regs->contrast,
+ ARRAY_SIZE(state->regs->contrast));
+ break;
+ case V4L2_CID_CAMERA_SATURATION:
+ err = s5k4ecgx_set_parameter(sd, &parms->saturation,
+ value, "saturation",
+ state->regs->saturation,
+ ARRAY_SIZE(state->regs->saturation));
+ break;
+ case V4L2_CID_CAMERA_SHARPNESS:
+ err = s5k4ecgx_set_parameter(sd, &parms->sharpness,
+ value, "sharpness",
+ state->regs->sharpness,
+ ARRAY_SIZE(state->regs->sharpness));
+ break;
+ case V4L2_CID_CAMERA_WDR:
+ err = s5k4ecgx_set_wdr(sd, value);
+ break;
+ case V4L2_CID_CAMERA_FACE_DETECTION:
+ err = s5k4ecgx_set_face_detection(sd, value);
+ break;
+ case V4L2_CID_CAMERA_FOCUS_MODE:
+ err = s5k4ecgx_set_focus_mode(sd, value);
+ break;
+ case V4L2_CID_CAM_JPEG_QUALITY:
+ if (state->runmode == S5K4ECGX_RUNMODE_RUNNING) {
+ state->jpeg.quality = value;
+ err = s5k4ecgx_set_jpeg_quality(sd);
+ } else {
+ dev_err(&client->dev,
+ "%s: trying to set jpeg quality when not "
+ "in preview mode\n",
+ __func__);
+ err = -EINVAL;
+ }
+ break;
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ err = s5k4ecgx_set_parameter(sd, &parms->scene_mode,
+ SCENE_MODE_NONE, "scene_mode",
+ state->regs->scene_mode,
+ ARRAY_SIZE(state->regs->scene_mode));
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s: failed to set scene-mode default value\n",
+ __func__);
+ break;
+ }
+ if (value != SCENE_MODE_NONE) {
+ err = s5k4ecgx_set_parameter(sd, &parms->scene_mode,
+ value, "scene_mode",
+ state->regs->scene_mode,
+ ARRAY_SIZE(state->regs->scene_mode));
+ }
+ if (parms->scene_mode == SCENE_MODE_NIGHTSHOT) {
+ state->one_frame_delay_ms =
+ NIGHT_MODE_MAX_ONE_FRAME_DELAY_MS;
+ } else {
+ state->one_frame_delay_ms =
+ NORMAL_MODE_MAX_ONE_FRAME_DELAY_MS;
+ }
+
+ break;
+ case V4L2_CID_CAMERA_GPS_LATITUDE:
+ dev_err(&client->dev,
+ "%s: V4L2_CID_CAMERA_GPS_LATITUDE: not implemented\n",
+ __func__);
+ break;
+ case V4L2_CID_CAMERA_GPS_LONGITUDE:
+ dev_err(&client->dev,
+ "%s: V4L2_CID_CAMERA_GPS_LONGITUDE: not implemented\n",
+ __func__);
+ break;
+ case V4L2_CID_CAMERA_GPS_TIMESTAMP:
+ dev_err(&client->dev,
+ "%s: V4L2_CID_CAMERA_GPS_TIMESTAMP: not implemented\n",
+ __func__);
+ break;
+ case V4L2_CID_CAMERA_GPS_ALTITUDE:
+ dev_err(&client->dev,
+ "%s: V4L2_CID_CAMERA_GPS_ALTITUDE: not implemented\n",
+ __func__);
+ break;
+ case V4L2_CID_CAMERA_OBJECT_POSITION_X:
+ state->position.x = value;
+ break;
+ case V4L2_CID_CAMERA_OBJECT_POSITION_Y:
+ state->position.y = value;
+ break;
+ case V4L2_CID_CAMERA_SET_AUTO_FOCUS:
+ if (value == AUTO_FOCUS_ON)
+ err = s5k4ecgx_start_auto_focus(sd);
+ else if (value == AUTO_FOCUS_OFF)
+ err = s5k4ecgx_stop_auto_focus(sd);
+ else {
+ err = -EINVAL;
+ dev_err(&client->dev,
+ "%s: bad focus value requestion %d\n",
+ __func__, value);
+ }
+ break;
+ case V4L2_CID_CAMERA_FRAME_RATE:
+ dev_dbg(&client->dev,
+ "%s: camera frame rate request for %d fps\n",
+ __func__, value);
+ err = s5k4ecgx_set_parameter(sd, &parms->fps,
+ value, "fps",
+ state->regs->fps,
+ ARRAY_SIZE(state->regs->fps));
+ break;
+ case V4L2_CID_CAM_CAPTURE:
+ err = s5k4ecgx_start_capture(sd);
+ break;
+
+ /* Used to start / stop preview operation.
+ * This call can be modified to START/STOP operation,
+ * which can be used in image capture also
+ */
+ case V4L2_CID_CAM_PREVIEW_ONOFF:
+ if (value)
+ err = s5k4ecgx_set_preview_start(sd);
+ else
+ err = s5k4ecgx_set_preview_stop(sd);
+ break;
+ case V4L2_CID_CAMERA_CHECK_DATALINE:
+ dev_dbg(&client->dev, "%s: check_dataline set to %d\n",
+ __func__, value);
+ state->check_dataline = value;
+ break;
+ case V4L2_CID_CAMERA_CHECK_DATALINE_STOP:
+ err = s5k4ecgx_check_dataline_stop(sd);
+ break;
+ case V4L2_CID_CAMERA_RETURN_FOCUS:
+ if (parms->focus_mode != FOCUS_MODE_MACRO)
+ err = s5k4ecgx_return_focus(sd);
+ break;
+ case V4L2_CID_CAMERA_FINISH_AUTO_FOCUS:
+ err = s5k4ecgx_finish_auto_focus(sd);
+ break;
+ default:
+ dev_err(&client->dev, "%s: unknown set ctrl id 0x%x\n",
+ __func__, ctrl->id);
+ err = -ENOIOCTLCMD;
+ break;
+ }
+
+ if (err < 0)
+ dev_err(&client->dev, "%s: videoc_s_ctrl failed %d\n", __func__,
+ err);
+
+ mutex_unlock(&state->ctrl_lock);
+
+ dev_dbg(&client->dev, "%s: videoc_s_ctrl returning %d\n",
+ __func__, err);
+
+ return err;
+}
+
+static int s5k4ecgx_s_ext_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_ext_control *ctrl)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ int err = 0;
+ struct gps_info_common *tempGPSType = NULL;
+
+ switch (ctrl->id) {
+
+ case V4L2_CID_CAMERA_GPS_LATITUDE:
+ tempGPSType = (struct gps_info_common *)ctrl->reserved2[1];
+ state->gps_info.gps_buf[0] = tempGPSType->direction;
+ state->gps_info.gps_buf[1] = tempGPSType->dgree;
+ state->gps_info.gps_buf[2] = tempGPSType->minute;
+ state->gps_info.gps_buf[3] = tempGPSType->second;
+ break;
+ case V4L2_CID_CAMERA_GPS_LONGITUDE:
+ tempGPSType = (struct gps_info_common *)ctrl->reserved2[1];
+ state->gps_info.gps_buf[4] = tempGPSType->direction;
+ state->gps_info.gps_buf[5] = tempGPSType->dgree;
+ state->gps_info.gps_buf[6] = tempGPSType->minute;
+ state->gps_info.gps_buf[7] = tempGPSType->second;
+ break;
+ case V4L2_CID_CAMERA_GPS_ALTITUDE:
+ tempGPSType = (struct gps_info_common *)ctrl->reserved2[1];
+ state->gps_info.altitude_buf[0] = tempGPSType->direction;
+ state->gps_info.altitude_buf[1] =
+ (tempGPSType->dgree) & 0x00ff;
+ state->gps_info.altitude_buf[2] =
+ ((tempGPSType->dgree) & 0xff00) >> 8;
+ state->gps_info.altitude_buf[3] = tempGPSType->minute;
+ break;
+ case V4L2_CID_CAMERA_GPS_TIMESTAMP:
+ state->gps_info.gps_timeStamp = *((int *)ctrl->reserved2[1]);
+ err = 0;
+ break;
+ default:
+ dev_err(&client->dev, "%s: unknown ctrl->id %d\n",
+ __func__, ctrl->id);
+ err = -ENOIOCTLCMD;
+ break;
+ }
+
+ if (err < 0)
+ dev_err(&client->dev, "%s: vidioc_s_ext_ctrl failed %d\n",
+ __func__, err);
+
+ return err;
+}
+
+static int s5k4ecgx_s_ext_ctrls(struct v4l2_subdev *sd,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ int ret;
+ int i;
+
+ for (i = 0; i < ctrls->count; i++, ctrl++) {
+ ret = s5k4ecgx_s_ext_ctrl(sd, ctrl);
+
+ if (ret) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_DEBUG
+static void s5k4ecgx_dump_regset(struct s5k4ecgx_regset *regset)
+{
+ if ((regset->data[0] == 0x00) && (regset->data[1] == 0x2A)) {
+ if (regset->size <= 6)
+ pr_err("odd regset size %d\n", regset->size);
+ pr_info("regset: addr = 0x%02X%02X, data[0,1] = 0x%02X%02X,"
+ " total data size = %d\n",
+ regset->data[2], regset->data[3],
+ regset->data[6], regset->data[7],
+ regset->size-6);
+ } else {
+ pr_info("regset: 0x%02X%02X%02X%02X\n",
+ regset->data[0], regset->data[1],
+ regset->data[2], regset->data[3]);
+ if (regset->size != 4)
+ pr_err("odd regset size %d\n", regset->size);
+ }
+}
+#endif
+
+static int s5k4ecgx_i2c_write_block(struct v4l2_subdev *sd, u8 *buf, int size)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int retry_count = 5;
+ int ret;
+ struct i2c_msg msg = {client->addr, 0, size, buf};
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_DEBUG
+ if (s5k4ecgx_debug_mask & S5K4ECGX_DEBUG_I2C_BURSTS) {
+ if ((buf[0] == 0x0F) && (buf[1] == 0x12))
+ pr_info("%s : data[0,1] = 0x%02X%02X,"
+ " total data size = %d\n",
+ __func__, buf[2], buf[3], size-2);
+ else
+ pr_info("%s : 0x%02X%02X%02X%02X\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+ }
+#endif
+
+ do {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (likely(ret == 1))
+ break;
+ msleep(POLL_TIME_MS);
+ } while (retry_count-- > 0);
+ if (ret != 1) {
+ dev_err(&client->dev, "%s: I2C is not working.\n", __func__);
+ return -EIO;
+ }
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_0
+ {
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ if (state->fw.minor == 0) {
+ /* v1.0 sensor have problems sometimes if we write
+ * too much data too fast, so add a sleep. I've
+ * tried various combinations of size/delay. Checking
+ * for a larger size doesn't seem to work reliably, and
+ * a delay of 1ms sometimes isn't enough either.
+ */
+ if (size > 16)
+ msleep(2);
+ }
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Parse the init_reg2 array into a number of register sets that
+ * we can send over as i2c burst writes instead of writing each
+ * entry of init_reg2 as a single 4 byte write. Write the
+ * new data structures and then free them.
+ */
+static int s5k4ecgx_write_init_reg2_burst(struct v4l2_subdev *sd)
+{
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ struct s5k4ecgx_regset *regset_table;
+ struct s5k4ecgx_regset *regset;
+ struct s5k4ecgx_regset *end_regset;
+ u8 *regset_data;
+ u8 *dst_ptr;
+ const u32 *end_src_ptr;
+ bool flag_copied;
+ int init_reg_2_array_size = state->regs->init_reg_2.array_size;
+ int init_reg_2_size = init_reg_2_array_size * sizeof(u32);
+ const u32 *src_ptr = state->regs->init_reg_2.reg;
+ u32 src_value;
+ int err;
+
+ pr_debug("%s : start\n", __func__);
+
+ regset_data = vmalloc(init_reg_2_size);
+ if (regset_data == NULL)
+ return -ENOMEM;
+ regset_table = vmalloc(sizeof(struct s5k4ecgx_regset) *
+ init_reg_2_size);
+ if (regset_table == NULL) {
+ kfree(regset_data);
+ return -ENOMEM;
+ }
+
+ dst_ptr = regset_data;
+ regset = regset_table;
+ end_src_ptr = &state->regs->init_reg_2.reg[init_reg_2_array_size];
+
+ src_value = *src_ptr++;
+ while (src_ptr <= end_src_ptr) {
+ /* initial value for a regset */
+ regset->data = dst_ptr;
+ flag_copied = false;
+ *dst_ptr++ = src_value >> 24;
+ *dst_ptr++ = src_value >> 16;
+ *dst_ptr++ = src_value >> 8;
+ *dst_ptr++ = src_value;
+
+ /* check subsequent values for a data flag (starts with
+ 0x0F12) or something else */
+ do {
+ src_value = *src_ptr++;
+ if ((src_value & 0xFFFF0000) != 0x0F120000) {
+ /* src_value is start of next regset */
+ regset->size = dst_ptr - regset->data;
+ regset++;
+ break;
+ }
+ /* copy the 0x0F12 flag if not done already */
+ if (!flag_copied) {
+ *dst_ptr++ = src_value >> 24;
+ *dst_ptr++ = src_value >> 16;
+ flag_copied = true;
+ }
+ /* copy the data part */
+ *dst_ptr++ = src_value >> 8;
+ *dst_ptr++ = src_value;
+ } while (src_ptr < end_src_ptr);
+ }
+ pr_debug("%s : finished creating table\n", __func__);
+
+ end_regset = regset;
+ pr_debug("%s : first regset = %p, last regset = %p, count = %d\n",
+ __func__, regset_table, regset, end_regset - regset_table);
+ pr_debug("%s : regset_data = %p, end = %p, dst_ptr = %p\n", __func__,
+ regset_data, regset_data + (init_reg_2_size * sizeof(u32)),
+ dst_ptr);
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_DEBUG
+ if (s5k4ecgx_debug_mask & S5K4ECGX_DEBUG_I2C_BURSTS) {
+ int last_regset_end_addr = 0;
+ regset = regset_table;
+ do {
+ s5k4ecgx_dump_regset(regset);
+ if (regset->size > 4) {
+ int regset_addr = (regset->data[2] << 8 |
+ regset->data[3]);
+ if (last_regset_end_addr == regset_addr)
+ pr_info("%s : this regset can be"
+ " combined with previous\n",
+ __func__);
+ last_regset_end_addr = (regset_addr +
+ regset->size - 6);
+ }
+ regset++;
+ } while (regset < end_regset);
+ }
+#endif
+ regset = regset_table;
+ pr_debug("%s : start writing init reg 2 bursts\n", __func__);
+ do {
+ if (regset->size > 4) {
+ /* write the address packet */
+ err = s5k4ecgx_i2c_write_block(sd, regset->data, 4);
+ if (err)
+ break;
+ /* write the data in a burst */
+ err = s5k4ecgx_i2c_write_block(sd, regset->data+4,
+ regset->size-4);
+
+ } else
+ err = s5k4ecgx_i2c_write_block(sd, regset->data,
+ regset->size);
+ if (err)
+ break;
+ regset++;
+ } while (regset < end_regset);
+
+ pr_debug("%s : finished writing init reg 2 bursts\n", __func__);
+
+ vfree(regset_data);
+ vfree(regset_table);
+
+ return err;
+}
+
+static int s5k4ecgx_init_regs(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+ u16 read_value;
+
+ /* we'd prefer to do this in probe, but the framework hasn't
+ * turned on the camera yet so our i2c operations would fail
+ * if we tried to do it in probe, so we have to do it here
+ * and keep track if we succeeded or not.
+ */
+
+ /* enter read mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x002C, 0x7000);
+
+ s5k4ecgx_i2c_write_twobyte(client, 0x002E, 0x01A6);
+ s5k4ecgx_i2c_read_twobyte(client, 0x0F12, &read_value);
+
+ pr_info("%s : revision %08X\n", __func__, read_value);
+
+ /* restore write mode */
+ s5k4ecgx_i2c_write_twobyte(client, 0x0028, 0x7000);
+
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_0
+ if (read_value == S5K4ECGX_VERSION_1_0) {
+ state->regs = &regs_for_fw_version_1_0;
+ state->initialized = true;
+ return 0;
+ }
+#endif
+#ifdef CONFIG_VIDEO_S5K4ECGX_V_1_1
+ if (read_value == S5K4ECGX_VERSION_1_1) {
+ state->fw.minor = 1;
+ state->regs = &regs_for_fw_version_1_1;
+ state->initialized = true;
+ return 0;
+ }
+#endif
+
+ dev_err(&client->dev, "%s: unknown fw version 0x%x\n",
+ __func__, read_value);
+ return -ENODEV;
+}
+
+static int s5k4ecgx_init(struct v4l2_subdev *sd, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ dev_dbg(&client->dev, "%s: start\n", __func__);
+
+ s5k4ecgx_init_parameters(sd);
+
+ if (s5k4ecgx_init_regs(&state->sd) < 0)
+ return -ENODEV;
+
+ dev_dbg(&client->dev, "%s: state->check_dataline : %d\n",
+ __func__, state->check_dataline);
+
+ if (s5k4ecgx_set_from_table(sd, "init reg 1",
+ &state->regs->init_reg_1, 1, 0) < 0)
+ return -EIO;
+
+ /* delay 10ms after wakeup of SOC processor */
+ msleep(10);
+
+ if (s5k4ecgx_write_init_reg2_burst(sd) < 0)
+ return -EIO;
+
+ if (s5k4ecgx_set_from_table(sd, "flash init",
+ &state->regs->flash_init, 1, 0) < 0)
+ return -EIO;
+
+ if (state->check_dataline) {
+ if (s5k4ecgx_set_from_table(sd, "dtp start",
+ &state->regs->dtp_start, 1, 0) < 0)
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev, "%s: end\n", __func__);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops = {
+ .init = s5k4ecgx_init, /* initializing API */
+ .g_ctrl = s5k4ecgx_g_ctrl,
+ .s_ctrl = s5k4ecgx_s_ctrl,
+ .s_ext_ctrls = s5k4ecgx_s_ext_ctrls,
+};
+
+static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops = {
+ .s_mbus_fmt = s5k4ecgx_s_mbus_fmt,
+ .enum_framesizes = s5k4ecgx_enum_framesizes,
+ .enum_mbus_fmt = s5k4ecgx_enum_mbus_fmt,
+ .try_mbus_fmt = s5k4ecgx_try_mbus_fmt,
+ .g_parm = s5k4ecgx_g_parm,
+ .s_parm = s5k4ecgx_s_parm,
+};
+
+static const struct v4l2_subdev_ops s5k4ecgx_ops = {
+ .core = &s5k4ecgx_core_ops,
+ .video = &s5k4ecgx_video_ops,
+};
+
+
+/*
+ * s5k4ecgx_probe
+ * Fetching platform data is being done with s_config subdev call.
+ * In probe routine, we just register subdev device
+ */
+static int s5k4ecgx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ struct s5k4ecgx_state *state;
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+
+ if ((pdata == NULL) || (pdata->flash_onoff == NULL)) {
+ dev_err(&client->dev, "%s: bad platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ state = kzalloc(sizeof(struct s5k4ecgx_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ mutex_init(&state->ctrl_lock);
+
+ state->runmode = S5K4ECGX_RUNMODE_NOTREADY;
+ sd = &state->sd;
+ strcpy(sd->name, S5K4ECGX_DRIVER_NAME);
+
+ /*
+ * Assign default format and resolution
+ * Use configured default information in platform data
+ * or without them, use default information in driver
+ */
+ state->pix.width = pdata->default_width;
+ state->pix.height = pdata->default_height;
+
+ if (!pdata->pixelformat)
+ state->pix.pixelformat = DEFAULT_PIX_FMT;
+ else
+ state->pix.pixelformat = pdata->pixelformat;
+
+ if (!pdata->freq)
+ state->freq = DEFAULT_MCLK; /* 24MHz default */
+ else
+ state->freq = pdata->freq;
+
+ /* Registering subdev */
+ v4l2_i2c_subdev_init(sd, client, &s5k4ecgx_ops);
+
+ dev_dbg(&client->dev, "5MP camera S5K4ECGX loaded.\n");
+
+ return 0;
+}
+
+static int s5k4ecgx_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k4ecgx_state *state =
+ container_of(sd, struct s5k4ecgx_state, sd);
+
+ v4l2_device_unregister_subdev(sd);
+ mutex_destroy(&state->ctrl_lock);
+ kfree(state);
+
+ dev_dbg(&client->dev, "Unloaded camera sensor S5K4ECGX.\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k4ecgx_id[] = {
+ { S5K4ECGX_DRIVER_NAME, 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, s5k4ecgx_id);
+
+static struct i2c_driver v4l2_i2c_driver = {
+ .driver.name = S5K4ECGX_DRIVER_NAME,
+ .probe = s5k4ecgx_probe,
+ .remove = s5k4ecgx_remove,
+ .id_table = s5k4ecgx_id,
+};
+
+static int __init v4l2_i2c_drv_init(void)
+{
+ return i2c_add_driver(&v4l2_i2c_driver);
+}
+
+static void __exit v4l2_i2c_drv_cleanup(void)
+{
+ i2c_del_driver(&v4l2_i2c_driver);
+}
+
+module_init(v4l2_i2c_drv_init);
+module_exit(v4l2_i2c_drv_cleanup);
+
+MODULE_DESCRIPTION("LSI S5K4ECGX 5MP SOC camera driver");
+MODULE_AUTHOR("Seok-Young Jang <quartz.jang@samsung.com>");
+MODULE_LICENSE("GPL");
+