/* 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 #include #include #include #include #include #include #include #include #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 *)¶m->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 = ®s_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 = ®s_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 "); MODULE_LICENSE("GPL");