/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Code Aurora Forum nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * Alternatively, provided that this notice is retained in full, this software * may be relicensed by the recipient under the terms of the GNU General Public * License version 2 ("GPL") and only version 2, in which case the provisions of * the GPL apply INSTEAD OF those given above. If the recipient relicenses the * software under the GPL, then the identification text in the MODULE_LICENSE * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a * recipient changes the license terms to the GPL, subsequent recipients shall * not relicense under alternate licensing terms, including the BSD or dual * BSD/GPL terms. In addition, the following license statement immediately * below and between the words START and END shall also then apply when this * software is relicensed under the GPL: * * START * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 and only version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * END * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ov8810.h" /* CAMIF output resolutions */ /* 816x612, 24MHz MCLK 96MHz PCLK */ #define OV8810_FULL_SIZE_DUMMY_PIXELS 0 #define OV8810_FULL_SIZE_DUMMY_LINES 0 #define OV8810_FULL_SIZE_WIDTH 3280 #define OV8810_FULL_SIZE_HEIGHT 2456 #define OV8810_QTR_SIZE_DUMMY_PIXELS 0 #define OV8810_QTR_SIZE_DUMMY_LINES 0 #define OV8810_QTR_SIZE_WIDTH 1632 #define OV8810_QTR_SIZE_HEIGHT 1224 #define OV8810_HRZ_FULL_BLK_PIXELS 696 /*stella 1203*/ #define OV8810_VER_FULL_BLK_LINES 44 #define OV8810_HRZ_QTR_BLK_PIXELS 890 #define OV8810_VER_QTR_BLK_LINES 44 static int cam_mode_sel = 0; /* 0: photo, 1: video@30fps, 2: video@24fps */ /* 240: 26, 365: 24, 589: 21 */ const int ov8810_ver_qtr_blk_lines_array[] = {44, 44, 365}; /*============================================================= SENSOR REGISTER DEFINES ==============================================================*/ #define Q8 0x00000100 /* Omnivision8810 product ID register address */ #define OV8810_PIDH_REG 0x300A #define OV8810_PIDL_REG 0x300B /* Omnivision8810 product ID */ #define OV8810_PID 0x88 /* Omnivision8810 version */ #define OV8810_VER 0x10 /* Time in milisecs for waiting for the sensor to reset */ #define OV8810_RESET_DELAY_MSECS 66 #define OV8810_DEFAULT_CLOCK_RATE 24000000 /* Registers*/ /* PLL Registers */ #define REG_PRE_PLL_CLK_DIV 0x3011 /*0x0305*/ #define REG_PLL_MULTIPLIER 0x3010 #define REG_VT_CLK_DIV 0x300E /*[7:4]VT_SYS_DIV, [3-0]VT_PIX_DIV*/ #define REG_OP_CLK_DIV 0x300F /*[7:4]OP_SYS_DIV, [3-0]OP_PIX_DIV*/ /* ISP Enable Control */ #define REG_ISP_ENABLE_CONTROL_00 0x3302 #define REG_ISP_ENABLE_CONTROL_01 0x3301 /* AWB Control */ #define REG_AWB_CTRL_0 0x3320 #define REG_AWB_CTRL_1 0x3321 #define REG_AWB_CTRL_2 0x3322 #define REG_AWB_CTRL_8 0x3328 /* Output Size */ #define REG_X_OUTPUT_SIZE_MSB 0x302C #define REG_X_OUTPUT_SIZE_LSB 0x302D #define REG_Y_OUTPUT_SIZE_MSB 0x302E #define REG_Y_OUTPUT_SIZE_LSB 0x302F /*Reserved register */ #define REG_BINNING_CONTROL 0x3091 /* Frame Fotmat */ #define REG_FRAME_LENGTH_LINES_MSB 0x3020 #define REG_FRAME_LENGTH_LINES_LSB 0x3021 #define REG_LINE_LENGTH_PCK_MSB 0x3022 #define REG_LINE_LENGTH_PCK_LSB 0x3023 #define REG_EXTRA_VSYNC_WIDTH_MSB 0x301E #define REG_EXTRA_VSYNC_WIDTH_LSB 0x301F #define REG_X_ADDR_START_HIGH 0x3024 #define REG_X_ADDR_START_LOW 0x3025 #define REG_Y_ADDR_START_HIGH 0x3026 #define REG_Y_ADDR_START_LOW 0x3027 #define REG_X_ADDR_END_HIGH 0x3028 #define REG_X_ADDR_END_LOW 0x3029 #define REG_Y_ADDR_END_HIGH 0x302A #define REG_Y_ADDR_END_LOW 0x302B /* Gain setting register */ #define OV8810_GAIN 0x3000 #define OV8810_AEC_MSB 0x3002 #define OV8810_AEC_LSB 0x3003 /* additional gain function provided by OV8810, * original gain can changed to 1x, 2x or 4x * to increase the gain that OV8810 can provide */ #define OV8810_REG_MUL_GAIN 0x3006 #define MUL_GAIN_INIT_VALUE 0x00 #define OV8810_MAX_EXPOSURE_GAIN 0x1FF /* Mode select register */ #define OV8810_REG_MODE_SELECT 0x30FA /* image system */ #define OV8810_MODE_SELECT_STREAM 0x01 /* start streaming */ #define OV8810_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ #define OV8810_REG_SOFTWARE_RESET 0x3012 /* 0x0103 */ #define OV8810_SOFTWARE_RESET 0x80 /* 0x01 */ /* AF Total steps parameters */ #define OV8810_AF_MSB 0x30EC #define OV8810_AF_LSB 0x30ED #define OV8810_STEPS_NEAR_TO_CLOSEST_INF 42 /*43 stella0122 */ #define OV8810_TOTAL_STEPS_NEAR_TO_FAR 42 /*43 stella0122 */ /*Test pattern*/ /* Color bar pattern selection */ #define OV8810_COLOR_BAR_PATTERN_SEL_REG 0x307B /* Color bar enabling control */ #define OV8810_COLOR_BAR_ENABLE_REG 0x307D /* I2C Address of the Sensor */ #define OV8810_I2C_SLAVE_ID 0x6C /*LSC table length*/ #define LSC_table_length 144 /*============================================================================ TYPE DECLARATIONS ============================================================================*/ /* 16bit address - 8 bit context register structure */ #if 0 typedef struct reg_addr_val_pair_struct { uint16_t reg_addr; uint8_t reg_val; } reg_struct_type; #endif struct awb_lsc_struct_type { unsigned int caBuff[8]; /*awb_calibartion*/ struct reg_addr_val_pair_struct LSC_table[150]; /*lsc_calibration*/ uint32_t LSC_table_CRC; }; enum ov8810_test_mode_t { TEST_OFF, TEST_1, TEST_2, TEST_3 }; enum ov8810_resolution_t { QTR_SIZE, FULL_SIZE, INVALID_SIZE }; /*LSC calibration*/ int global_mode; /*TODO: should be use a header file to reference this function*/ extern unsigned char *get_cam_awb_cal(void); static int sensor_probe_node = 0; static struct wake_lock ov8810_wake_lock; static inline void init_suspend(void) { wake_lock_init(&ov8810_wake_lock, WAKE_LOCK_IDLE, "ov8810"); } static inline void deinit_suspend(void) { wake_lock_destroy(&ov8810_wake_lock); } static inline void prevent_suspend(void) { wake_lock(&ov8810_wake_lock); } static inline void allow_suspend(void) { wake_unlock(&ov8810_wake_lock); } /*============================================================================ DATA DECLARATIONS ============================================================================*/ /* 96MHz PCLK @ 24MHz MCLK inc*/ /*stella1223 start*/ static struct reg_addr_val_pair_struct ov8810_init_settings_array[] = { /* Sensor clk setup */ {REG_OP_CLK_DIV, 0x04}, {REG_VT_CLK_DIV, 0x05}, #if 1 /* weiting0414 prevent capture hang restore CLK */ {REG_PLL_MULTIPLIER, 0x28}, /*0x28 96MHz PCLK 0x18 64MHz PCLK*/ {REG_PRE_PLL_CLK_DIV, 0x22}, #else {REG_PLL_MULTIPLIER, 0x14}, /*Reduce internal clock to prevent hang Weiting0331*/ {REG_PRE_PLL_CLK_DIV, 0x21}, #endif {OV8810_GAIN, 8}, /*0x30},*/ {OV8810_AEC_MSB, 0x04}, {OV8810_AEC_LSB, 0xc4}, /*stella 1203*/ {REG_ISP_ENABLE_CONTROL_00, 0x20}, {0x30b2, 0x13}, /*driving strength*/ {0x30a0, 0x40}, {0x3098, 0x24}, {0x3099, 0x81}, {0x309a, 0x64}, {0x309b, 0x00}, {0x309d, 0x64}, {0x309e, 0x2d}, {REG_AWB_CTRL_0, 0xc2}, /*set wb manual*/ {REG_AWB_CTRL_1, 0x02}, {REG_AWB_CTRL_2, 0x04}, {REG_AWB_CTRL_8, 0x40}, {0x3329, 0xe3}, /*00},*/ /*stella 1203*/ {0x3306, 0x00}, {0x3316, 0x03}, {0x3079, 0x0a}, /*stella 1203*/ {0x3058, 0x01}, {0x3059, 0xa0}, {0x306b, 0x00}, {0x3065, 0x50}, {0x3067, 0x40}, {0x3069, 0x80}, {0x3071, 0x40},/*50 BLC trigger by gain 40 BLC every frame */ {0x3300, 0xef}, {0x3334, 0x02}, {0x3331, 0x08}, /*BLC level 8813*/ /*stella 1203*/ {0x3332, 0x08}, /*8813*/ {0x3333, 0x41}, /*Stella1221 for adding init size */ {0x30f8, 0x45}, {REG_FRAME_LENGTH_LINES_MSB, ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0xFF00) >> 8}, {REG_FRAME_LENGTH_LINES_LSB, ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0x00FF)}, {REG_LINE_LENGTH_PCK_MSB, ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0xFF00) >> 8}, {REG_LINE_LENGTH_PCK_LSB, ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0x00FF)}, {REG_X_ADDR_START_HIGH, 0x00}, {REG_X_ADDR_START_LOW, 0x04}, /*stella 1203*/ {REG_Y_ADDR_START_HIGH, 0x00}, {REG_Y_ADDR_START_LOW, 0x00}, {REG_X_ADDR_END_HIGH, 0x0c}, {REG_X_ADDR_END_LOW, 0xdb}, /*stella 1203*/ {REG_Y_ADDR_END_HIGH, 0x09}, {REG_Y_ADDR_END_LOW, 0x9f}, {REG_X_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_WIDTH & 0xFF00) >> 8}, {REG_X_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_WIDTH & 0x00FF)}, {REG_Y_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_HEIGHT & 0xFF00) >> 8}, {REG_Y_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_HEIGHT & 0x00FF)}, /*Stella1221 for adding init size */ /* {REG_BINNING_CONTROL, 0x00},*/ /*stella 1203*/ {OV8810_REG_MUL_GAIN, MUL_GAIN_INIT_VALUE}, {0x3082, 0x80}, {0x331e, 0x94}, {0x331f, 0x6e}, {0x3092, 0x00}, {0x3094, 0x01}, {0x3090, 0x2b}, /* for AN version 8a */ /*changed by Stella for 8813*/ {0x30ab, 0x44}, {0x3095, 0x0a}, {0x308d, 0x00}, {0x3082, 0x00}, {0x3080, 0x40}, {0x30aa, 0x59}, {0x30a9, 0x00}, {0x30be, 0x08}, {0x309f, 0x23}, {0x3065, 0x40}, {0x3068, 0x00}, {0x30bf, 0x80}, {0x309c, 0x00}, {0x3084, 0x44}, /*added by stella for 8813*/ {0x3016, 0x03}, /*added by stella for 8813*/ {0x30e9, 0x09}, /*changed by stella for 8813*/ {0x3075, 0x29}, {0x3076, 0x29}, {0x3077, 0x29}, {0x3078, 0x29}, {0x306a, 0x05}, {0x3015, 0x33}, /*changed by stella for 8813*/ /*stella 1203 start*/ {0x3090, 0x36}, {0x333e, 0x00}, {0x306a, 0x05}, /*stella 1203 end*/ {0x3087, 0x41}, {0x3090, 0x97}, /*99, QCT=97*/ {0x309e, 0x1b}, {0x30e3, 0x0e}, {0x30f0, 0x00}, {0x30f2, 0x00}, {0x30f4, 0x90}, /*stella 1203 start*/ {0x3347, 0x00}, {0x3347, 0x00}, #if 0 {0x3092, 0x00}, //marked by QCT {0x30f0, 0x10}, //marked by QCT {0x30f1, 0x56}, //marked by QCT {0x30fb, 0x8e}, //marked by QCT {0x30f3, 0xa7}, //marked by QCT #endif {0x3091, 0x08}, /*QCT for 8813*/ {0x3090, 0x97}, /*QCT for 8813*/ {0x30fb, 0xc9}, /*QCT for 8813*/ {0x308d, 0x02}, {0x30e7, 0x41}, {0x30b3, 0x08}, {0x33e5, 0x00}, /*30e5*/ {0x350e, 0x40}, /*305e*/ {0x301f, 0x00}, {0x309f, 0x23}, {0x3013, 0xc0}, {0x30e1, 0x90}, {0x3058, 0x01}, {0x3500, 0x40}, /* vsync_new */ {REG_BINNING_CONTROL, 0x00}, /*stella 0126*/ /*stella 1203 end*/ }; /*Vincent for LSC calibration*/ static struct reg_addr_val_pair_struct lsc_table_array[] = { {0x3358, 0x1f },//{0x3358, 0x18}, {0x3359, 0x14 },//{0x3359, 0x0f}, {0x335a, 0x0f },//{0x335a, 0x0c}, {0x335b, 0x0d },//{0x335b, 0x0a}, {0x335c, 0x0d },//{0x335c, 0x0a}, {0x335d, 0x0f },//{0x335d, 0x0b}, {0x335e, 0x14 },//{0x335e, 0x0d}, {0x335f, 0x1d },//{0x335f, 0x15}, {0x3360, 0x0f },//{0x3360, 0x0b}, {0x3361, 0x0a },//{0x3361, 0x09}, {0x3362, 0x07 },//{0x3362, 0x06}, {0x3363, 0x06 },//{0x3363, 0x05}, {0x3364, 0x06 },//{0x3364, 0x05}, {0x3365, 0x07 },//{0x3365, 0x06}, {0x3366, 0x09 },//{0x3366, 0x08}, {0x3367, 0x0d },//{0x3367, 0x0b}, {0x3368, 0x09 },//{0x3368, 0x07}, {0x3369, 0x06 },//{0x3369, 0x05}, {0x336a, 0x04 },//{0x336a, 0x03}, {0x336b, 0x03 },//{0x336b, 0x02}, {0x336c, 0x03 },//{0x336c, 0x02}, {0x336d, 0x04 },//{0x336d, 0x03}, {0x336e, 0x06 },//{0x336e, 0x04}, {0x336f, 0x09 },//{0x336f, 0x06}, {0x3370, 0x07 },//{0x3370, 0x05}, {0x3371, 0x04 },//{0x3371, 0x04}, {0x3372, 0x01 },//{0x3372, 0x01}, {0x3373, 0x00 },//{0x3373, 0x00}, {0x3374, 0x00 },//{0x3374, 0x00}, {0x3375, 0x01 },//{0x3375, 0x01}, {0x3376, 0x04 },//{0x3376, 0x03}, {0x3377, 0x07 },//{0x3377, 0x05}, {0x3378, 0x08 },//{0x3378, 0x05}, {0x3379, 0x04 },//{0x3379, 0x03}, {0x337a, 0x01 },//{0x337a, 0x01}, {0x337b, 0x00 },//{0x337b, 0x00}, {0x337c, 0x00 },//{0x337c, 0x00}, {0x337d, 0x01 },//{0x337d, 0x00}, {0x337e, 0x04 },//{0x337e, 0x02}, {0x337f, 0x07 },//{0x337f, 0x05}, {0x3380, 0x09 },//{0x3380, 0x06}, {0x3381, 0x06 },//{0x3381, 0x04}, {0x3382, 0x04 },//{0x3382, 0x03}, {0x3383, 0x02 },//{0x3383, 0x02}, {0x3384, 0x02 },//{0x3384, 0x01}, {0x3385, 0x04 },//{0x3385, 0x02}, {0x3386, 0x06 },//{0x3386, 0x03}, {0x3387, 0x09 },//{0x3387, 0x05}, {0x3388, 0x0f },//{0x3388, 0x0a}, {0x3389, 0x0a },//{0x3389, 0x07}, {0x338a, 0x07 },//{0x338a, 0x05}, {0x338b, 0x07 },//{0x338b, 0x04}, {0x338c, 0x07 },//{0x338c, 0x04}, {0x338d, 0x07 },//{0x338d, 0x05}, {0x338e, 0x0a },//{0x338e, 0x06}, {0x338f, 0x0f },//{0x338f, 0x09}, {0x3390, 0x1d },//{0x3390, 0x12}, {0x3391, 0x12 },//{0x3391, 0x0d}, {0x3392, 0x0d },//{0x3392, 0x09}, {0x3393, 0x0b },//{0x3393, 0x08}, {0x3394, 0x0b },//{0x3394, 0x08}, {0x3395, 0x0d },//{0x3395, 0x09}, {0x3396, 0x12 },//{0x3396, 0x0c}, {0x3397, 0x1a },//{0x3397, 0x11}, {0x3398, 0x0f },//{0x3398, 0x10}, {0x3399, 0x0d },//{0x3399, 0x10}, {0x339a, 0x0e },//{0x339a, 0x10}, {0x339b, 0x0f },//{0x339b, 0x0e}, {0x339c, 0x11 },//{0x339c, 0x0e}, {0x339d, 0x0d },//{0x339d, 0x0f}, {0x339e, 0x12 },//{0x339e, 0x0e}, {0x339f, 0x0e },//{0x339f, 0x0f}, {0x33a0, 0x0f },//{0x33a0, 0x0f}, {0x33a1, 0x0f },//{0x33a1, 0x0f}, {0x33a2, 0x10 },//{0x33a2, 0x0f}, {0x33a3, 0x10 },//{0x33a3, 0x10}, {0x33a4, 0x0f },//{0x33a4, 0x0e}, {0x33a5, 0x0d },//{0x33a5, 0x10}, {0x33a6, 0x0f },//{0x33a6, 0x11}, {0x33a7, 0x10 },//{0x33a7, 0x10}, {0x33a8, 0x10 },//{0x33a8, 0x10}, {0x33a9, 0x0f },//{0x33a9, 0x0f}, {0x33aa, 0x10 },//{0x33aa, 0x0e}, {0x33ab, 0x0e },//{0x33ab, 0x0f}, {0x33ac, 0x10 },//{0x33ac, 0x10}, {0x33ad, 0x11 },//{0x33ad, 0x10}, {0x33ae, 0x11 },//{0x33ae, 0x10}, {0x33af, 0x0f },//{0x33af, 0x0f}, {0x33b0, 0x0f },//{0x33b0, 0x0e}, {0x33b1, 0x0d },//{0x33b1, 0x0f}, {0x33b2, 0x0d },//{0x33b2, 0x0f}, {0x33b3, 0x0e },//{0x33b3, 0x0f}, {0x33b4, 0x0f },//{0x33b4, 0x0f}, {0x33b5, 0x10 },//{0x33b5, 0x0f}, {0x33b6, 0x12 },//{0x33b6, 0x0e}, {0x33b7, 0x0d },//{0x33b7, 0x0d}, {0x33b8, 0x0c },//{0x33b8, 0x0c}, {0x33b9, 0x0c },//{0x33b9, 0x0c}, {0x33ba, 0x0c },//{0x33ba, 0x0d}, {0x33bb, 0x0b },//{0x33bb, 0x0f}, {0x33bc, 0x1b },//{0x33bc, 0x16}, {0x33bd, 0x1b },//{0x33bd, 0x17}, {0x33be, 0x1d },//{0x33be, 0x17}, {0x33bf, 0x1d },//{0x33bf, 0x17}, {0x33c0, 0x1e },//{0x33c0, 0x17}, {0x33c1, 0x1c },//{0x33c1, 0x14}, {0x33c2, 0x1a },//{0x33c2, 0x17}, {0x33c3, 0x17 },//{0x33c3, 0x14}, {0x33c4, 0x15 },//{0x33c4, 0x13}, {0x33c5, 0x16 },//{0x33c5, 0x13}, {0x33c6, 0x19 },//{0x33c6, 0x14}, {0x33c7, 0x1e },//{0x33c7, 0x15}, {0x33c8, 0x16 },//{0x33c8, 0x15}, {0x33c9, 0x12 },//{0x33c9, 0x12}, {0x33ca, 0x10 },//{0x33ca, 0x10}, {0x33cb, 0x10 },//{0x33cb, 0x10}, {0x33cc, 0x14 },//{0x33cc, 0x12}, {0x33cd, 0x19 },//{0x33cd, 0x14}, {0x33ce, 0x16 },//{0x33ce, 0x15}, {0x33cf, 0x12 },//{0x33cf, 0x12}, {0x33d0, 0x10 },//{0x33d0, 0x10}, {0x33d1, 0x11 },//{0x33d1, 0x10}, {0x33d2, 0x14 },//{0x33d2, 0x12}, {0x33d3, 0x1a },//{0x33d3, 0x14}, {0x33d4, 0x18 },//{0x33d4, 0x16}, {0x33d5, 0x15 },//{0x33d5, 0x13}, {0x33d6, 0x13 },//{0x33d6, 0x12}, {0x33d7, 0x14 },//{0x33d7, 0x12}, {0x33d8, 0x17 },//{0x33d8, 0x13}, {0x33d9, 0x1b },//{0x33d9, 0x15}, {0x33da, 0x18 },//{0x33da, 0x18}, {0x33db, 0x1a },//{0x33db, 0x15}, {0x33dc, 0x1b },//{0x33dc, 0x15}, {0x33dd, 0x1b },//{0x33dd, 0x15}, {0x33de, 0x1b },//{0x33de, 0x15}, {0x33df, 0x1c },//{0x33df, 0x14}, {0x3350, 0x06 },//{0x3350, 0x06}, {0x3351, 0xab },//{0x3351, 0xab}, {0x3352, 0x05 },//{0x3352, 0x05}, {0x3353, 0x00 },//{0x3353, 0x00}, {0x3354, 0x04 },//{0x3354, 0x04}, {0x3355, 0xf8 },//{0x3355, 0xf8}, {0x3356, 0x07 },//{0x3356, 0x07}, {0x3357, 0x74 },//{0x3357, 0x74}, /* lsc setting on sensor*/ {0x3300, 0xff}, /*enable lsc on sensor*/ /*move to the last*/ {OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM}, }; /*1632x1224; 24MHz MCLK 96MHz PCLK*/ static struct reg_addr_val_pair_struct ov8810_qtr_settings_array[] = { {0x30f8, 0x45}, {REG_FRAME_LENGTH_LINES_MSB, ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0xFF00) >> 8}, {REG_FRAME_LENGTH_LINES_LSB, ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0x00FF)}, {REG_LINE_LENGTH_PCK_MSB, ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0xFF00) >> 8}, {REG_LINE_LENGTH_PCK_LSB, ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0x00FF)}, {REG_X_ADDR_START_HIGH, 0x00}, {REG_X_ADDR_START_LOW, 0x04}, /*stella 1203*/ {REG_Y_ADDR_START_HIGH, 0x00}, {REG_Y_ADDR_START_LOW, 0x00}, {REG_X_ADDR_END_HIGH, 0x0c}, {REG_X_ADDR_END_LOW, 0xd8}, /*stella 1203=db*/ /*QCT:d8*/ {REG_Y_ADDR_END_HIGH, 0x09}, {REG_Y_ADDR_END_LOW, 0x9f}, {REG_X_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_WIDTH & 0xFF00) >> 8}, {REG_X_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_WIDTH & 0x00FF)}, {REG_Y_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_HEIGHT & 0xFF00) >> 8}, {REG_Y_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_HEIGHT & 0x00FF)}, /*stella1202 for capture over exposure issue due to user space use 2X line count*/ {0x3068, 0x00}, /*changed for color edge, stella 1203*/ {0x307e, 0x00}, {0x3071, 0x40},/*50 BLC trigger by gain 40 BLC every frame */ {REG_ISP_ENABLE_CONTROL_01, 0x0B}, {REG_BINNING_CONTROL, 0x00}, //stella0127 {0x331c, 0x00}, {0x331d, 0x00}, {0x308a, 0x02}, {0x3072, 0x0d}, {0x3319, 0x04}, {0x309e, 0x09}, {0x300e, 0x05}, {0x300f, 0x04}, {0x33e4, 0x07}, /*lsc for 2:1 down sampling*/ }; /*stella1223 end*/ /* 3280x2456 Sensor Raw; 24MHz MCLK 96MHz PCLK*/ static struct reg_addr_val_pair_struct ov8810_full_settings_array[] = { {0x30f8, 0x40}, {REG_FRAME_LENGTH_LINES_MSB, ((OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES) & 0xFF00) >> 8}, {REG_FRAME_LENGTH_LINES_LSB, ((OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES) & 0x00FF)}, {REG_LINE_LENGTH_PCK_MSB, ((OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS) & 0xFF00) >> 8}, {REG_LINE_LENGTH_PCK_LSB, ((OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS) & 0x00FF)}, {REG_X_ADDR_START_HIGH, 0x00}, {REG_X_ADDR_START_LOW, 0x02}, /*stella 1203*/ {REG_Y_ADDR_START_HIGH, 0x00}, {REG_Y_ADDR_START_LOW, 0x00}, {REG_X_ADDR_END_HIGH, 0x0c}, {REG_X_ADDR_END_LOW, 0xdd}, /*stella 1203*/ {REG_Y_ADDR_END_HIGH, 0x09}, {REG_Y_ADDR_END_LOW, 0x9f}, {REG_X_OUTPUT_SIZE_MSB, (OV8810_FULL_SIZE_WIDTH & 0xFF00) >> 8}, {REG_X_OUTPUT_SIZE_LSB, (OV8810_FULL_SIZE_WIDTH & 0x00FF)}, {REG_Y_OUTPUT_SIZE_MSB, (OV8810_FULL_SIZE_HEIGHT & 0xFF00) >> 8}, {REG_Y_OUTPUT_SIZE_LSB, (OV8810_FULL_SIZE_HEIGHT & 0x00FF)}, /*stella1202 for capture over exposure issue due to user space use 2X line count */ {0x3068, 0x00}, /* changed for color edge stella 1203*/ {0x307e, 0x00}, {REG_ISP_ENABLE_CONTROL_01, 0x0B}, {REG_BINNING_CONTROL, 0x00}, //stella0127 {0x331c, 0x28}, {0x331d, 0x21}, {0x308a, 0x01}, {0x3072, 0x01}, {0x3319, 0x06}, {0x309e, 0x1b}, {0x300e, 0x05}, {0x300f, 0x04}, {0x33e4, 0x02}, /*lsc for full resolution*/ }; /* AF Tuning Parameters */ static uint16_t ov8810_step_position_table[OV8810_TOTAL_STEPS_NEAR_TO_FAR+1]; static uint8_t ov8810_damping_threshold = 10; static uint8_t ov8810_damping_course_step = 4; static uint8_t ov8810_damping_fine_step = 10; static uint8_t ov8810_damping_time_wait; static uint16_t ov8810_focus_debug; /*don't init to 0*/ static uint16_t ov8810_use_default_damping = 1; static uint16_t ov8810_use_threshold_damping = 1; /*set to FALSE if too slow*/ /*static uint32_t stored_line_length_ratio = 1 * Q8;*/ /*Andy1217 write Line 1 frame ealier before Gain*/ struct backup_line_gain_struct { uint32_t line; uint8_t mul; uint16_t gain; uint32_t extra_line_length; }; static struct backup_line_gain_struct backup_line_gain[2]; static uint16_t write_cnt; static uint16_t updated_BLC; /* only set to 0x50 after 1st update again*/ uint8_t S3_to_0 = 0x1; /* 0x9 */ /* static Variables*/ static uint16_t step_position_table[OV8810_TOTAL_STEPS_NEAR_TO_FAR+1]; /* FIXME: Changes from here */ struct ov8810_work { struct work_struct work; }; static struct ov8810_work *ov8810_sensorw; static struct i2c_client *ov8810_client; static struct vreg *vreg_af_actuator; struct ov8810_ctrl { const struct msm_camera_sensor_info *sensordata; uint32_t sensormode; uint32_t fps_divider; /* init to 1 * 0x00000400 */ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ uint16_t fps; int16_t curr_lens_pos; uint16_t curr_step_pos; uint16_t my_reg_gain; uint32_t my_reg_line_count; uint16_t total_lines_per_frame; enum ov8810_resolution_t prev_res; enum ov8810_resolution_t pict_res; enum ov8810_resolution_t curr_res; enum ov8810_test_mode_t set_test; unsigned short imgaddr; }; static struct ov8810_ctrl *ov8810_ctrl; static struct platform_device *ov8810_pdev; struct ov8810_waitevent{ uint32_t waked_up; wait_queue_head_t event_wait; }; static struct ov8810_waitevent ov8810_event; static DECLARE_WAIT_QUEUE_HEAD(ov8810_wait_queue); DECLARE_MUTEX(ov8810_sem); /*=============================================================*/ static int ov8810_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, int length) { struct i2c_msg msgs[] = { { .addr = saddr, .flags = 0, .len = 2, .buf = rxdata, }, { .addr = saddr, .flags = I2C_M_RD, .len = length, .buf = rxdata, }, }; CDBG("%s: saddr=0x%X\n", __func__, saddr); CDBG("%s: raddr=0x%X\n", __func__, *rxdata); if (i2c_transfer(ov8810_client->adapter, msgs, 2) < 0) { pr_err("ov8810_i2c_rxdata failed!\n"); return -EIO; } CDBG("%s: rxdata=0x%X\n", __func__, *rxdata); return 0; } static int32_t ov8810_i2c_txdata(unsigned short saddr, unsigned char *txdata, int length) { struct i2c_msg msg[] = { { .addr = saddr, .flags = 0, .len = length, .buf = txdata, }, }; if (i2c_transfer(ov8810_client->adapter, msg, 1) < 0) { pr_err("ov8810_i2c_txdata faild 0x%x\n", ov8810_client->addr); return -EIO; } return 0; } static int32_t ov8810_i2c_read(unsigned short raddr, unsigned short *rdata, int rlen) { int32_t rc = 0; unsigned char buf[2]; int count = 0; if (!rdata) return -EIO; memset(buf, 0, sizeof(buf)); buf[0] = (raddr & 0xFF00) >> 8; buf[1] = (raddr & 0x00FF); retry: rc = ov8810_i2c_rxdata(ov8810_client->addr, buf, rlen); if (rc < 0) { pr_err("ov8810_i2c_read 0x%x failed!\n", raddr); printk(KERN_ERR "starting read retry policy count:%d\n", count); udelay(10); count++; if (count < 20) { if (count > 10) udelay(100); } else return rc; goto retry; } *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]); return rc; } static int32_t ov8810_i2c_write_b(unsigned short saddr, unsigned short waddr, uint8_t bdata) { int32_t rc = -EFAULT; unsigned char buf[3]; int count = 0; CDBG("i2c_write_w_b, addr = 0x%x, val = 0x%x!\n", waddr, bdata); memset(buf, 0, sizeof(buf)); buf[0] = (waddr & 0xFF00) >> 8; buf[1] = (waddr & 0x00FF); buf[2] = bdata; retry: CDBG("i2c_write_b addr = %d, val = %d\n", waddr, bdata); rc = ov8810_i2c_txdata(saddr, buf, 3); if (rc < 0) { pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", waddr, bdata); pr_err(KERN_ERR "starting read retry policy count:%d\n", count); udelay(10); count++; if (count < 20) { if (count > 10) udelay(100); } else return rc; goto retry; } return rc; } /*for LSC calibration*/ static int ov8810_update_lsc_table(struct sensor_cfg_data *cdata) { int i = 0; pr_info("[LSC calibration]ov8810_update_lsc_table\n"); for (i = 0; i < 144; i++) { ov8810_i2c_write_b( ov8810_client->addr, cdata->cfg.lsctable.lsc_table[i].reg_addr, cdata->cfg.lsctable.lsc_table[i].reg_val); pr_info("[LSC calibration]update_lsc_table: 0x%x, 0x%x\n", cdata->cfg.lsctable.lsc_table[i].reg_addr, cdata->cfg.lsctable.lsc_table[i].reg_val); } /*enable lsc on sensor*/ ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xff); /*mirror on*/ ov8810_i2c_write_b(ov8810_client->addr, 0x30f8, 0x45); /*mirror on*/ ov8810_i2c_write_b(ov8810_client->addr, 0x3316, 0x03); return 1; } /*20100330 vincent for LSC calibration*/ static int ov8810_LSC_calibration_set_rawflag(struct sensor_cfg_data *cdata) { global_mode = 1; return 1; } #define MAX_FUSE_ID_INFO 11 static int ov8810_i2c_read_fuseid(struct sensor_cfg_data *cdata) { unsigned short fuse_id[MAX_FUSE_ID_INFO]; int count = 0; ov8810_i2c_write_b(ov8810_client->addr, 0x30d5, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30d6, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30d7, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30d8, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30d9, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30da, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30db, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30dc, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30dd, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30de, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x30df, 0xff); ov8810_i2c_write_b(ov8810_client->addr, 0x303e, 0x55); ov8810_i2c_read(0x30d5, &fuse_id[0], 2); ov8810_i2c_read(0x30d6, &fuse_id[1], 2); ov8810_i2c_read(0x30d7, &fuse_id[2], 2); ov8810_i2c_read(0x30d8, &fuse_id[3], 2); ov8810_i2c_read(0x30d9, &fuse_id[4], 2); ov8810_i2c_read(0x30da, &fuse_id[5], 2); ov8810_i2c_read(0x30db, &fuse_id[6], 2); ov8810_i2c_read(0x30dc, &fuse_id[7], 2); ov8810_i2c_read(0x30dd, &fuse_id[8], 2); ov8810_i2c_read(0x30de, &fuse_id[9], 2); ov8810_i2c_read(0x30df, &fuse_id[10], 2); cdata->cfg.fuse.fuse_id_word1 = (uint32_t) fuse_id[0]; cdata->cfg.fuse.fuse_id_word2 = (uint32_t) fuse_id[1]; cdata->cfg.fuse.fuse_id_word3 = 0; cdata->cfg.fuse.fuse_id_word4 = 0; for (count = 0; count < MAX_FUSE_ID_INFO; count++) pr_info("Ov8810 Get fuse: fuse_id[%d]: %x\n", count, fuse_id[count]); return 0; } static int32_t ov8810_af_i2c_write(uint16_t data) { uint8_t code_val_msb, code_val_lsb; /* S3_to_0; */ uint32_t rc = 0; /* S3_to_0 = 0x9; S[3:0] */ code_val_msb = data >> 4; /* D[9:4] */ code_val_lsb = ((data & 0x000F) << 4) | S3_to_0; CDBG("code value = %d ,D[9:4] = %d ,D[3:0] = %d\n", data, code_val_msb, code_val_lsb); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AF_MSB, code_val_msb); if (rc < 0) { pr_err("Unable to write code_val_msb = %d\n", code_val_msb); return rc; } rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AF_LSB, code_val_lsb); if (rc < 0) { pr_err("Unable to write code_val_lsb = %disclaimer\n", code_val_lsb); return rc; } return rc; } /* ov8810_af_i2c_write */ static int32_t ov8810_move_focus(int direction, int32_t num_steps) { int8_t step_direction; int8_t dest_step_position; uint16_t dest_lens_position, target_dist, small_step; int16_t next_lens_position; int32_t rc = 0; if (num_steps == 0) { return rc; } if (direction == MOVE_NEAR) { step_direction = 1; } else if (direction == MOVE_FAR) { step_direction = -1; } else { pr_err("Illegal focus direction\n"); return -EINVAL;; /* CAMERA_INVALID_PARM; */ } CDBG("%s, interpolate\n", __func__); dest_step_position = ov8810_ctrl->curr_step_pos + (step_direction * num_steps); if (dest_step_position < 0) dest_step_position = 0; else if (dest_step_position > OV8810_TOTAL_STEPS_NEAR_TO_FAR) dest_step_position = OV8810_TOTAL_STEPS_NEAR_TO_FAR; dest_lens_position = ov8810_step_position_table[dest_step_position]; /* Taking small damping steps */ target_dist = step_direction * (dest_lens_position - ov8810_ctrl->curr_lens_pos); if (target_dist == 0) { return rc; } if (ov8810_use_threshold_damping && (step_direction < 0) && (target_dist >= ov8810_step_position_table[ov8810_damping_threshold])) { /* change to variable */ small_step = (uint16_t)(target_dist/ov8810_damping_fine_step); ov8810_damping_time_wait = 1; } else { small_step = (uint16_t)(target_dist/ov8810_damping_course_step); ov8810_damping_time_wait = 4; } for (next_lens_position = ov8810_ctrl->curr_lens_pos + (step_direction * small_step); (step_direction * next_lens_position) <= (step_direction * dest_lens_position); next_lens_position += (step_direction * small_step)) { if (ov8810_af_i2c_write(next_lens_position) < 0) return -EBUSY; ov8810_ctrl->curr_lens_pos = next_lens_position; if (ov8810_ctrl->curr_lens_pos != dest_lens_position) { mdelay(ov8810_damping_time_wait); } } if (ov8810_ctrl->curr_lens_pos != dest_lens_position) { if (ov8810_af_i2c_write(dest_lens_position) < 0) { return -EBUSY; } } /* Storing the current lens Position */ ov8810_ctrl->curr_lens_pos = dest_lens_position; ov8810_ctrl->curr_step_pos = dest_step_position; CDBG("done\n"); return rc; } static int32_t ov8810_set_default_focus(uint8_t af_step) { int16_t position; int32_t rc = 0; ov8810_damping_time_wait = 4; if (ov8810_use_default_damping) { /* when lens is uninitialized */ if (ov8810_ctrl->curr_lens_pos == -1 || (ov8810_focus_debug == 1)) { position = ov8810_step_position_table[ov8810_damping_threshold]; rc = ov8810_af_i2c_write(position); if (rc < 0) { return rc; } ov8810_ctrl->curr_step_pos = ov8810_damping_threshold; ov8810_ctrl->curr_lens_pos = position; mdelay(ov8810_damping_time_wait); } rc = ov8810_move_focus(MOVE_FAR, ov8810_ctrl->curr_step_pos); if (rc < 0) return rc; } else { rc = ov8810_af_i2c_write(ov8810_step_position_table[0]); if (rc < 0) return rc; ov8810_ctrl->curr_step_pos = 0; ov8810_ctrl->curr_lens_pos = ov8810_step_position_table[0]; } return rc; } static void ov8810_get_pict_fps(uint16_t fps, uint16_t *pfps) { /* input fps is preview fps in Q8 format */ uint32_t divider, d1, d2; uint16_t snapshot_height, preview_height, preview_width, snapshot_width; if (ov8810_ctrl->prev_res == QTR_SIZE) { preview_width = OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS ; preview_height = OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel] ; } else { /* full size resolution used for preview. */ preview_width = OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS ; preview_height = OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES ; } if (ov8810_ctrl->pict_res == QTR_SIZE) { snapshot_width = OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS ; snapshot_height = OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel] ; } else { snapshot_width = OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS; snapshot_height = OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES; } d1 = preview_height * 0x00000400 / snapshot_height; d2 = preview_width * 0x00000400 / snapshot_width; divider = (uint32_t) (d1 * d2) / 0x00000400; *pfps = (uint16_t)(fps * divider / 0x00000400); } /* endof ov8810_get_pict_fps */ static uint16_t ov8810_get_prev_lines_pf(void) { if (ov8810_ctrl->prev_res == QTR_SIZE) { return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); } else { return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); } } static uint16_t ov8810_get_prev_pixels_pl(void) { if (ov8810_ctrl->prev_res == QTR_SIZE) { return (OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS); } else { return (OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS); } } static uint16_t ov8810_get_pict_lines_pf(void) { if (ov8810_ctrl->pict_res == QTR_SIZE) { return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); } else { return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); } } static uint16_t ov8810_get_pict_pixels_pl(void) { if (ov8810_ctrl->pict_res == QTR_SIZE) { return (OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS); } else { return (OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS); } } static uint32_t ov8810_get_pict_max_exp_lc(void) { if (ov8810_ctrl->pict_res == QTR_SIZE) { return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); } else { return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); } } static int32_t ov8810_set_fps(struct fps_cfg *fps) { int32_t rc = 0; ov8810_ctrl->fps_divider = fps->fps_div; ov8810_ctrl->pict_fps_divider = fps->pict_fps_div; ov8810_ctrl->fps = fps->f_mult; return rc; } static int32_t ov8810_write_exp_gain (uint16_t mul, uint16_t gain, uint32_t line) { uint16_t aec_msb; uint16_t aec_lsb; int32_t rc = 0; uint32_t total_lines_per_frame; uint32_t total_pixels_per_line; /*uint32_t line_length_ratio = 1 * Q8;*/ /**uint8_t ov8810_offset = 2; */ uint32_t extra_line_length = 0; uint16_t extra_line_msb = 0; uint16_t extra_line_lsb = 0; uint32_t phy_line = 0; uint8_t phy_mul = MUL_GAIN_INIT_VALUE; uint16_t phy_gain = 0; uint32_t phy_extra_line_length = 0; const uint16_t postpone_frames = 4; uint16_t do_write = 1; /* assume do things */ uint16_t ori_reg_mul_gain; uint8_t ori_reg_mul_gain_8bit; CDBG("%s start, mul = %d gain = %d line = %d\n", __func__, mul, gain, line); if (ov8810_ctrl->curr_res == QTR_SIZE) { total_lines_per_frame = (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); total_pixels_per_line = OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS; } else { total_lines_per_frame = (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); total_pixels_per_line = OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS; } if (line > total_lines_per_frame - 4) { extra_line_length = (uint32_t)(line - (total_lines_per_frame-4)); line = total_lines_per_frame - 4; } else { extra_line_length = (uint16_t)0; } phy_line = line; phy_mul = mul; phy_gain = gain; phy_extra_line_length = extra_line_length; /* postpone writing gain only apply to preview */ if (ov8810_ctrl->sensormode == SENSOR_PREVIEW_MODE) { /* need time to wait for aec stable (prevent black preview) */ mdelay(6); CDBG("Stella: write_cnt=%d, pre_line = %d, line = %d," \ "pre_mul = %d mul = %d," \ "pre_gain = %d gain = %d," \ "pre_extra_line_length =%d extra_line_length = %d\n", write_cnt, backup_line_gain[1].line, line, backup_line_gain[1].mul, mul, backup_line_gain[1].gain, gain, backup_line_gain[1].extra_line_length, extra_line_length); if (write_cnt == 0 && ( backup_line_gain[1].line != line || backup_line_gain[1].mul != mul || backup_line_gain[1].gain != gain || backup_line_gain[1].extra_line_length != extra_line_length)) { backup_line_gain[1].line = line; backup_line_gain[1].mul = mul; backup_line_gain[1].gain = gain; backup_line_gain[1].extra_line_length = extra_line_length; phy_line = backup_line_gain[1].line; phy_mul = backup_line_gain[0].mul; phy_gain = backup_line_gain[0].gain; phy_extra_line_length = backup_line_gain[0].extra_line_length; write_cnt++; } else if (write_cnt >= 1 && write_cnt < postpone_frames) { phy_line = backup_line_gain[1].line; phy_mul = backup_line_gain[1].mul; phy_gain = backup_line_gain[1].gain; phy_extra_line_length = backup_line_gain[1].extra_line_length; CDBG("updated_BLC = %d\n", updated_BLC); if (updated_BLC == 5) { /*50 BLC trigger by gain 40 BLC every frame */ pr_info("### BLC to 0x50 ###\n"); #if 0 ov8810_i2c_write_b(ov8810_client->addr, 0x3071, 0x50); #endif } if (updated_BLC <= 5) updated_BLC++; if (write_cnt > 1) do_write = 0; write_cnt++; } else { write_cnt = 0; do_write = 0; } if (do_write) { backup_line_gain[0].line = phy_line; backup_line_gain[0].mul = phy_mul; backup_line_gain[0].gain = phy_gain; backup_line_gain[0].extra_line_length = phy_extra_line_length; } } #if 0 pr_info("Stella: backup_line_gain[0].line = %d\n", backup_line_gain[0].line); pr_info("Stella: backup_line_gain[0].mul = %d\n", backup_line_gain[0].mul); pr_info("Stella: backup_line_gain[0].gain = %d\n", backup_line_gain[0].gain); pr_info("Stella: backup_line_gain[0].extra_line_length = %d\n", backup_line_gain[0].extra_line_length); pr_info("Stella: backup_line_gain[1].line = %d\n", backup_line_gain[1].line); pr_info("Stella: backup_line_gain[1].mul = %d\n", backup_line_gain[1].mul); pr_info("Stella: backup_line_gain[1].gain = %d\n", backup_line_gain[1].gain); pr_info("Stella: backup_line_gain[1].extra_line_length = %d\n", backup_line_gain[1].extra_line_length); pr_info("Stella: phy_line=%d\n", phy_line); pr_info("Stella: phy_gain=%d\n", phy_gain); pr_info("Stella: phy_extra_line_length=%d\n", phy_extra_line_length); #endif extra_line_msb = (uint16_t)(phy_extra_line_length & 0xFF00) >> 8; extra_line_lsb = (uint16_t)(phy_extra_line_length & 0x00FF); aec_msb = (uint16_t)(phy_line & 0xFF00) >> 8; aec_lsb = (uint16_t)(phy_line & 0x00FF); if (!do_write) return rc; /*Move the read function out of group update to prevent hang Weiting0331*/ rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, &ori_reg_mul_gain, 2); if (rc < 0) { pr_err("read OV8810_REG_MUL_GAIN fail\n"); return rc; } /* since we do STREAM ON here, don't do group update for snapshot */ if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { /*for group update top*/ /* weiting0414 prevent capture hang, enable 0x30b7[2] */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x8c); if (rc < 0) return rc; } /* FIXME: prevent black preview by restoring 0x30bf -> 0x80 */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30bf, 0x80); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AEC_MSB, (uint8_t)aec_msb); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AEC_LSB, (uint8_t)aec_lsb); if (rc < 0) return rc; ori_reg_mul_gain_8bit = (uint8_t)((ori_reg_mul_gain & 0xFF00) >> 8); CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", __func__, ori_reg_mul_gain_8bit); ori_reg_mul_gain_8bit = (ori_reg_mul_gain_8bit & 0xFC) | (phy_mul & 0x03); CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", __func__, ori_reg_mul_gain_8bit); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MUL_GAIN, ori_reg_mul_gain_8bit); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_GAIN, (uint8_t)phy_gain); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_EXTRA_VSYNC_WIDTH_MSB, (uint8_t)extra_line_msb); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_EXTRA_VSYNC_WIDTH_LSB, (uint8_t)extra_line_lsb); if (rc < 0) return rc; if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { /* for group update bottom */ /* weiting0414 prevent capture hang , enable 0x30b7[2] */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x84); if (rc < 0) return rc; /* for group update enable */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); if (rc < 0) return rc; /* weiting0414 prevent capture hang , retry I2C write to make sure enable */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); if (rc < 0) return rc; } if (ov8810_ctrl->sensormode == SENSOR_RAW_SNAPSHOT_MODE) { pr_info("sleep 500 ms for safety raw snapshot"); msleep(500); } /* STREAM ON for SNAPSHOT */ if (ov8810_ctrl->sensormode == SENSOR_SNAPSHOT_MODE) { pr_info("ov8810_ctrl: STREAM ON for SNAPSHOT\n"); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); if (rc < 0) return rc; msleep(50); } /*stored_line_length_ratio = line_length_ratio;*/ return rc; } /* endof ov8810_write_exp_gain*/ /* ### this function is not called for userspace ### */ static int32_t ov8810_set_pict_exp_gain (uint16_t mul, uint16_t gain, uint32_t line) { int32_t rc = 0; rc = ov8810_write_exp_gain(mul, gain, line); return rc; } /* endof ov8810_set_pict_exp_gain*/ /* remove test code */ #if 0 static int32_t ov8810_test(enum ov8810_test_mode_t mo) { int32_t rc = 0; if (mo == TEST_OFF) { return rc; } /* Activate the Color bar test pattern */ if (mo == TEST_1) { rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_COLOR_BAR_ENABLE_REG, 0xa0); if (rc < 0) { return rc; } rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3085, 0x20); if (rc < 0) { return rc; } rc = ov8810_i2c_write_b(ov8810_client->addr, 0x306c, 0x00); if (rc < 0) { return rc; } rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_COLOR_BAR_PATTERN_SEL_REG, 0x02); if (rc < 0) { return rc; } } return rc; } #endif uint32_t Crc32CheckSumByte(uint8_t *pData, uint32_t uiLen, uint32_t preValue) { const uint32_t crc32table[256] = { /* 0x00 */ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, /* 0x04 */ 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, /* 0x08 */ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, /* 0x0C */ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, /* 0x10 */ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, /* 0x14 */ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, /* 0x18 */ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, /* 0x1C */ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, /* 0x20 */ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, /* 0x24 */ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, /* 0x28 */ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, /* 0x2C */ 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, /* 0x30 */ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, /* 0x34 */ 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, /* 0x38 */ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, /* 0x3C */ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, /* 0x40 */ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, /* 0x44 */ 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, /* 0x48 */ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, /* 0x4C */ 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, /* 0x50 */ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, /* 0x54 */ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, /* 0x58 */ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, /* 0x5C */ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, /* 0x60 */ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, /* 0x64 */ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, /* 0x68 */ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, /* 0x6C */ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, /* 0x70 */ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, /* 0x74 */ 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, /* 0x78 */ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, /* 0x7C */ 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, /* 0x80 */ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, /* 0x84 */ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, /* 0x88 */ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, /* 0x8C */ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, /* 0x90 */ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, /* 0x94 */ 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, /* 0x98 */ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, /* 0x9C */ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, /* 0xA0 */ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, /* 0xA4 */ 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, /* 0xA8 */ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, /* 0xAC */ 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, /* 0xB0 */ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, /* 0xB4 */ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, /* 0xB8 */ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, /* 0xBC */ 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, /* 0xC0 */ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, /* 0xC4 */ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, /* 0xC8 */ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, /* 0xCC */ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, /* 0xD0 */ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, /* 0xD4 */ 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, /* 0xD8 */ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, /* 0xDC */ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, /* 0xE0 */ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, /* 0xE4 */ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, /* 0xE8 */ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, /* 0xEC */ 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, /* 0xF0 */ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, /* 0xF4 */ 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, /* 0xF8 */ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, /* 0xFC */ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,}; uint32_t i, CheckSum, cvalue; CheckSum = preValue; for (i = 0; i < uiLen; i++) { cvalue = *pData; CheckSum = (CheckSum>>8) ^ crc32table[(CheckSum & 0xFF) ^ (cvalue & 0xFF)]; pData++; } return CheckSum; } static int32_t HTC_update_ov8810_lsc_registers(void) { int i; struct awb_lsc_struct_type *awb_lsc_data_ptr; awb_lsc_data_ptr = (struct awb_lsc_struct_type *)get_cam_awb_cal(); for (i = 0; i < 8; i++) { pr_info(KERN_INFO"[LSC calibration] read AWB table 0x%x\n", awb_lsc_data_ptr->caBuff[i]); } for (i = 0; i < LSC_table_length; i++) { pr_info("[LSC calibration] read LSC table 0x%x, 0x%x\n", awb_lsc_data_ptr->LSC_table[i].reg_addr, awb_lsc_data_ptr->LSC_table[i].reg_val); } if (awb_lsc_data_ptr->LSC_table_CRC == Crc32CheckSumByte( (uint8_t *) awb_lsc_data_ptr->LSC_table, 150 * sizeof(struct reg_addr_val_pair_struct), 0) && awb_lsc_data_ptr->LSC_table_CRC != 0) { pr_info("[LSC calibration]checksum pass,use calibrated LSC\n"); for (i = 0; i < LSC_table_length; i++) { ov8810_i2c_write_b(ov8810_client->addr, awb_lsc_data_ptr->LSC_table[i].reg_addr, awb_lsc_data_ptr->LSC_table[i].reg_val); } /*enable lsc on sensor*/ ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xff); /*move to the last*/ ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); } else {/*use default LSC table*/ pr_info("[LSC calibration]checksum fail\n"); return false; } return true; } static int32_t initialize_ov8810_registers(void) { int32_t i, array_length; int32_t rc = 0; mdelay(5); ov8810_i2c_write_b( ov8810_client->addr, OV8810_REG_SOFTWARE_RESET, OV8810_SOFTWARE_RESET); mdelay(5); ov8810_i2c_write_b( ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_SW_STANDBY); mdelay(1); array_length = sizeof(ov8810_init_settings_array) / sizeof(ov8810_init_settings_array[0]); /* Configure sensor for Preview mode and Snapshot mode */ for (i = 0; i < array_length; i++) { rc = ov8810_i2c_write_b(ov8810_client->addr, ov8810_init_settings_array[i].reg_addr, ov8810_init_settings_array[i].reg_val); if (rc < 0) return rc; } /*use calibrated LSC table*/ if (HTC_update_ov8810_lsc_registers()) { pr_info("[LSC calibration] use calibrated LSC table done!\n"); } else {/*use default LSC table*/ array_length = sizeof(lsc_table_array) / sizeof(lsc_table_array[0]); for (i = 0; i < array_length; i++) { rc = ov8810_i2c_write_b(ov8810_client->addr, lsc_table_array[i].reg_addr, lsc_table_array[i].reg_val); } pr_info("[LSC calibration] use default LSC table done\n"); } return rc; } /* end of initialize_ov8810_ov8m0vc_registers. */ static int32_t ov8810_setting(int rt) { int32_t rc = 0; int32_t i, array_length; static int16_t did_snapshot; uint16_t ori_reg_mul_gain; uint8_t ori_reg_mul_gain_8bit; uint16_t i2c_ret = 0; write_cnt = 0; pr_info("ov8810_setting rt = %d\n", rt); if (rt == FULL_SIZE) { ov8810_i2c_read(0x30b7, &i2c_ret, 1); pr_info("0x30b7, i2c_ret = 0x%X\n", i2c_ret); /*Retry writing group update bottom to ensure capture settings can be updated Weiting0331*/ while (i2c_ret != 0x84) { /* for group update bottom */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x84); if (rc < 0) return rc; /* for group update enable */ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); if (rc < 0) return rc; msleep(50); ov8810_i2c_read(0x30b7, &i2c_ret, 1); pr_info("retry 0x30b7, i2c_ret = 0x%X\n", i2c_ret); }; } rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_SW_STANDBY); if (rc < 0) { return rc; } ov8810_i2c_read(OV8810_REG_MODE_SELECT, &i2c_ret, 1); pr_info("OV8810_REG_MODE_SELECT, i2c_ret = 0x%X\n", i2c_ret); switch (rt) { case QTR_SIZE: array_length = sizeof(ov8810_qtr_settings_array) / sizeof(ov8810_qtr_settings_array[0]); /* Configure sensor for XGA preview mode */ for (i = 0; i < array_length; i++) { rc = ov8810_i2c_write_b(ov8810_client->addr, ov8810_qtr_settings_array[i].reg_addr, ov8810_qtr_settings_array[i].reg_val); if (rc < 0) { return rc; } } /* reconfigure the qtr height to adjust frame rate */ { uint16_t fl_line = 0; fl_line = OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_FRAME_LENGTH_LINES_MSB, (fl_line & 0xFF00) >> 8); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_FRAME_LENGTH_LINES_LSB, fl_line & 0x00FF); if (rc < 0) return rc; #if 0 if (cam_mode_sel > 0) { pr_info("andy write binning ctrl 0x00, cam_mode_sel %d\n", cam_mode_sel); rc = ov8810_i2c_write_b(ov8810_client->addr, //weiting ori c0 REG_BINNING_CONTROL, 0x00); if (rc < 0) return rc; } #endif } #if 1 /* this is supposed to prevent abnormal color when restart preview */ if (!did_snapshot) { memset(&backup_line_gain, 0, sizeof(struct backup_line_gain_struct)); backup_line_gain[0].line = 0x4c4; backup_line_gain[0].mul = MUL_GAIN_INIT_VALUE; backup_line_gain[0].gain = 8; /*0x30;*/ backup_line_gain[0].extra_line_length = 0; } CDBG("backup_line_gain[0].line = %d" \ "backup_line_gain[0].mul = %d" \ "backup_line_gain[0].gain = %d" \ "backup_line_gain[0].extra_line_length = %d", backup_line_gain[0].line, backup_line_gain[0].mul, backup_line_gain[0].gain, backup_line_gain[0].extra_line_length); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AEC_MSB, (uint8_t)((backup_line_gain[0].line & 0xFF00) >> 8)); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AEC_LSB, (uint8_t)(backup_line_gain[0].line & 0x00FF)); if (rc < 0) return rc; rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, &ori_reg_mul_gain, 2); if (rc < 0) { pr_err("read OV8810_REG_MUL_GAIN fail\n"); return rc; } ori_reg_mul_gain_8bit = (uint8_t)((ori_reg_mul_gain & 0xFF00)>>8); CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", __func__, ori_reg_mul_gain_8bit); ori_reg_mul_gain_8bit = (ori_reg_mul_gain_8bit & 0xFC) | (backup_line_gain[0].mul & 0x03); CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", __func__, ori_reg_mul_gain_8bit); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MUL_GAIN, ori_reg_mul_gain_8bit); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_GAIN, (uint8_t)(backup_line_gain[0].gain & 0x00FF)); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_EXTRA_VSYNC_WIDTH_MSB, (uint8_t)((backup_line_gain[0].extra_line_length & 0xFF00) >> 8)); if (rc < 0) return rc; rc = ov8810_i2c_write_b(ov8810_client->addr, REG_EXTRA_VSYNC_WIDTH_LSB, (uint8_t)(backup_line_gain[0].extra_line_length & 0x00FF)); if (rc < 0) return rc; #endif did_snapshot = 0; ov8810_ctrl->curr_res = QTR_SIZE; break; case FULL_SIZE: if (rc < 0) return rc; array_length = sizeof(ov8810_full_settings_array) / sizeof(ov8810_full_settings_array[0]); /* Configure sensor for QXGA capture mode */ for (i = 0; i < array_length; i++) { rc = ov8810_i2c_write_b(ov8810_client->addr, ov8810_full_settings_array[i].reg_addr, ov8810_full_settings_array[i].reg_val); if (rc < 0) return rc; } did_snapshot = 1; ov8810_ctrl->curr_res = FULL_SIZE; break; default: rc = -EFAULT; return rc; } /*disablt LSC for calibration*/ pr_info("[LSC calibration] global_mode=%d!!!!\n", global_mode); /*take raw picture for LSC calibration*/ if (global_mode) { /*disable sensor LSC*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xef); /*mirror off*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30f8, 0x00); /*mirror off*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3316, 0x02); pr_info("[LSC calibration]turn off LSC!Mirror On\n"); /*fix gain & linecount*/ /*Gain=0x9,exp=008d*/ /*so luma taget = 100 to mfg light source*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3000, 0x9); /*AEC_MSB*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3002, 0x00); /*AEC_LSB*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3003, 0x8d); pr_info("[LSC calibration]fix gain & linecount\n"); global_mode = 0; } if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { msleep(50); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); if (rc < 0) return rc; updated_BLC = 0; } /* remove test code rc = ov8810_test(ov8810_ctrl->set_test); if (rc < 0) return rc; */ return rc; } /*endof ov8810_setting*/ static int32_t ov8810_video_config(int mode) { int32_t rc = 0; static int pre_sel = 0; int cur_sel = (cam_mode_sel > 1)?1:0; ov8810_ctrl->sensormode = mode; pr_info("%s cam_mode_sel %d cur_sel %d \n", __func__, cam_mode_sel, cur_sel); if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res || pre_sel != cur_sel ) { rc = ov8810_setting(ov8810_ctrl->prev_res); if (rc < 0) return rc; } else { ov8810_ctrl->curr_res = ov8810_ctrl->prev_res; } pre_sel = cur_sel; ov8810_ctrl->sensormode = mode; return rc; } /*end of ov354_video_config*/ static int32_t ov8810_snapshot_config(int mode) { int32_t rc = 0; ov8810_ctrl->sensormode = mode; if (ov8810_ctrl->curr_res != ov8810_ctrl->pict_res) { rc = ov8810_setting(ov8810_ctrl->pict_res); if (rc < 0) return rc; } else { ov8810_ctrl->curr_res = ov8810_ctrl->pict_res; } ov8810_ctrl->sensormode = mode; return rc; } /*end of ov8810_snapshot_config*/ static int32_t ov8810_raw_snapshot_config(int mode) { int32_t rc = 0; ov8810_ctrl->sensormode = mode; if (ov8810_ctrl->curr_res != ov8810_ctrl->pict_res) { rc = ov8810_setting(ov8810_ctrl->pict_res); if (rc < 0) return rc; } else { ov8810_ctrl->curr_res = ov8810_ctrl->pict_res; } /* Update sensor resolution */ ov8810_ctrl->sensormode = mode; return rc; } /*end of ov8810_raw_snapshot_config*/ static int32_t ov8810_set_sensor_mode(int mode, int res) { int32_t rc = 0; struct msm_camera_sensor_info *sinfo = ov8810_pdev->dev.platform_data; switch (mode) { case SENSOR_PREVIEW_MODE: rc = ov8810_video_config(mode); break; case SENSOR_SNAPSHOT_MODE: pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); rc = ov8810_snapshot_config(mode); break; case SENSOR_RAW_SNAPSHOT_MODE: /*global_mode = 1; //20100330 vincent lsc calibration*/ pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); rc = ov8810_raw_snapshot_config(mode); break; default: rc = -EINVAL; break; } return rc; } static int32_t ov8810_power_down(void) { return 0; } static int ov8810_probe_read_id(const struct msm_camera_sensor_info *data) { int32_t rc = 0; uint16_t chipidh = 0; /*, chipidl;*/ uint16_t def_chipid = 0; msleep(20); pr_info("%s, ov8810_probe_init_sensor 1\n", __func__); /* 3. Read sensor Model ID: */ if (ov8810_i2c_read(OV8810_PIDH_REG, &chipidh, 2) < 0) { rc = -1; pr_err("read sensor id fail\n"); } pr_info("ov8810 model_id + ver = 0x%x\n", chipidh); /* 4. Compare sensor ID to OV8810 ID: */ def_chipid = (((OV8810_PID << 8) & 0xFF00) + (OV8810_VER & 0x00FF)); pr_info("%s, Expected id=0x%x\n", __func__, def_chipid); if (chipidh < def_chipid) { rc = -ENODEV; pr_err("read sensor id incorrect\n"); } pr_info("%s, vreg_get vreg_af_actuator\n", __func__); vreg_af_actuator = vreg_get(0, "gp5"); if (IS_ERR(vreg_af_actuator)) return PTR_ERR(vreg_af_actuator); pr_info(" ov8810_probe_init_sensor finishes\n"); return rc; } static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) { int i; int32_t rc = 0; /*stella0122*/ uint16_t ov8810_nl_region_boundary = 5; /*3;*/ uint16_t ov8810_nl_region_code_per_step = 35; /*101;*/ uint16_t ov8810_l_region_code_per_step = 20; /*18;*/ int timeout; pr_info("Calling ov8810_sensor_open_init\n"); down(&ov8810_sem); if (data == NULL) { pr_info("data is a NULL pointer\n"); return -EINVAL; } /*check whether resume done*/ timeout = wait_event_interruptible_timeout( ov8810_event.event_wait, ov8810_event.waked_up, 30*HZ); pr_info("wait event : %d timeout:%d\n", ov8810_event.waked_up, timeout); if (timeout == 0) { up(&ov8810_sem); return rc; } msm_camio_probe_on(ov8810_pdev); ov8810_ctrl = kzalloc(sizeof(struct ov8810_ctrl), GFP_KERNEL); if (!ov8810_ctrl) { pr_err("ov8810_init failed!\n"); rc = -ENOMEM; goto init_done; } ov8810_ctrl->curr_lens_pos = -1; ov8810_ctrl->fps_divider = 1 * 0x00000400; ov8810_ctrl->pict_fps_divider = 1 * 0x00000400; ov8810_ctrl->set_test = TEST_OFF; ov8810_ctrl->prev_res = QTR_SIZE; ov8810_ctrl->pict_res = FULL_SIZE; ov8810_ctrl->curr_res = INVALID_SIZE; if (data) ov8810_ctrl->sensordata = data; /*switch pclk and mclk between main cam and 2nd cam*/ /*only for supersonic*/ pr_info("doing clk switch (ov8810)\n"); if(data->camera_clk_switch != NULL) data->camera_clk_switch(); /* enable mclk first */ msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); msleep(20); msm_camio_camif_pad_reg_reset(); msleep(20); /*PWD and RST config*/ pr_info("%s, GPIO(%d) sensor_pwd 0\n", __func__, data->sensor_pwd); rc = gpio_request(data->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(data->sensor_pwd, 0); else pr_err("GPIO (%d) request faile\n", data->sensor_pwd); gpio_free(data->sensor_pwd); mdelay(5); rc = gpio_request(data->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(data->sensor_reset, 1); else pr_err("GPIO (%d) request faile\n", data->sensor_reset); gpio_free(data->sensor_reset); /*read sensor id*/ rc = ov8810_probe_read_id(data); ov8810_ctrl->sensormode = SENSOR_PREVIEW_MODE ; pr_info("%s, initialize_ov8810_registers: %d\n", __func__, __LINE__); if (rc < 0) goto init_fail; #if 0 /*move to probe up sensor*/ /* Initialize Sensor registers */ rc = initialize_ov8810_registers(); if (rc < 0) return rc; pr_info("%s, ov8810_setting preview %d\n", __func__, __LINE__); if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res) { rc = ov8810_setting(ov8810_ctrl->prev_res); if (rc < 0) goto init_fail; } else { ov8810_ctrl->curr_res = ov8810_ctrl->prev_res; } #endif pr_info("%s, enable AF actuator %d\n", __func__, __LINE__); /* enable AF actuator */ rc = vreg_enable(vreg_af_actuator); if (!rc) { rc = vreg_set_level(vreg_af_actuator, 2800); /*2v8*/ if (rc) { pr_err("vreg_af_actuator vreg_set_level 2v8 failed!\n"); goto init_fail; } } else { pr_err("vreg_af_actuator vreg_enable failed!\n"); goto init_fail; } msleep(20); pr_info("%s, set step_position_table %d\n", __func__, __LINE__); ov8810_ctrl->fps = 30*Q8; step_position_table[0] = 0; for (i = 1; i <= OV8810_TOTAL_STEPS_NEAR_TO_FAR; i++) { if (i <= ov8810_nl_region_boundary) { ov8810_step_position_table[i] = ov8810_step_position_table[i-1] + ov8810_nl_region_code_per_step; } else { ov8810_step_position_table[i] = ov8810_step_position_table[i-1] + ov8810_l_region_code_per_step; } } /* generate test pattern */ pr_info("%s, generate test pattern, %d, rc=%d\n", __func__, __LINE__, rc); if (rc < 0) goto init_fail; else goto init_done; /* reset the driver state */ init_fail: pr_err("%s: init_fail\n", __func__); vreg_disable(vreg_af_actuator); if (ov8810_ctrl) { kfree(ov8810_ctrl); ov8810_ctrl = NULL; } init_done: up(&ov8810_sem); pr_info("%s: init_done\n", __func__); return rc; } /*endof ov8810_sensor_open_init*/ static int ov8810_init_client(struct i2c_client *client) { /* Initialize the MSM_CAMI2C Chip */ init_waitqueue_head(&ov8810_wait_queue); return 0; } static const struct i2c_device_id ov8810_i2c_id[] = { { "ov8810", 0}, { } }; static int ov8810_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; pr_info("ov8810_probe called!\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("i2c_check_functionality failed\n"); goto probe_failure; } ov8810_sensorw = kzalloc(sizeof(struct ov8810_work), GFP_KERNEL); if (!ov8810_sensorw) { pr_err("kzalloc failed.\n"); rc = -ENOMEM; goto probe_failure; } i2c_set_clientdata(client, ov8810_sensorw); ov8810_init_client(client); ov8810_client = client; msleep(50); pr_info("ov8810_probe successed! rc = %d\n", rc); return 0; probe_failure: pr_err("ov8810_probe failed! rc = %d\n", rc); return rc; } static int ov8810_probe_init_done(const struct msm_camera_sensor_info *data) { gpio_request(data->sensor_pwd, "ov8810"); gpio_direction_output(data->sensor_pwd, 1); gpio_free(data->sensor_pwd); mdelay(1); return 0; } static int ov8810_suspend(struct platform_device *pdev, pm_message_t state) { int rc; struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; if (!sinfo->need_suspend) return 0; ov8810_event.waked_up = 0; pr_info("ov8810: camera suspend\n"); pr_info("%s, vreg_af_actuator vreg_disable\n", __func__); vreg_disable(vreg_af_actuator); rc = gpio_request(sinfo->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_reset, 0); else pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", sinfo->sensor_reset); gpio_free(sinfo->sensor_reset); msleep(10); rc = gpio_request(sinfo->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_pwd, 0); else pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", sinfo->sensor_pwd); gpio_free(sinfo->sensor_pwd); pr_info("ov8810:suspend done\n"); return rc; } static void ov8810_resume(struct early_suspend *handler) { int rc = 0; struct msm_camera_sensor_info *sinfo = ov8810_pdev->dev.platform_data; pr_info("ov8810_resume\n"); /*check whether need resume*/ if (!sinfo->need_suspend) return; /*check whether already suspend*/ if (ov8810_event.waked_up == 1) { pr_info("Ov8810: No nesesary to do Resume\n"); return; } mdelay(5); /*power down setup*/ pr_info("%s, sensor_pwd 0\n", __func__); rc = gpio_request(sinfo->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_pwd, 0); else pr_err("GPIO (%d) request faile\n", sinfo->sensor_pwd); gpio_free(sinfo->sensor_pwd); mdelay(5); /*reset setup */ rc = gpio_request(sinfo->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_reset, 1); else pr_err("GPIO (%d) request faile\n", sinfo->sensor_reset); gpio_free(sinfo->sensor_reset); /*init msm,clk ,GPIO,enable*/ pr_info("%s, msm_camio_probe_on\n", __func__); msm_camio_probe_on(ov8810_pdev); msm_camio_clk_enable(CAMIO_MDC_CLK); /*set MCLK*/ pr_info("%s, msm_camio_clk_rate_set = %d\n", __func__, OV8810_DEFAULT_CLOCK_RATE); msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); msleep(100); /*read sensor id*/ rc = ov8810_probe_read_id(sinfo); if (rc < 0) pr_err("OV8810 resume faile :can not read sensor ID\n"); /* Initialize Sensor registers */ rc = initialize_ov8810_registers(); if (rc < 0) return; msleep(20); /*resume done*/ ov8810_probe_init_done(sinfo); /*turn off MCLK*/ msm_camio_probe_off(ov8810_pdev); msm_camio_clk_disable(CAMIO_MDC_CLK); ov8810_event.waked_up = 1; pr_info("ov8810:resume done\n"); wake_up(&ov8810_event.event_wait); return; } static int __exit ov8810_i2c_remove(struct i2c_client *client) { struct ov8810_work_t *sensorw = i2c_get_clientdata(client); free_irq(client->irq, sensorw); deinit_suspend(); ov8810_client = NULL; kfree(sensorw); return 0; } static struct i2c_driver ov8810_i2c_driver = { .id_table = ov8810_i2c_id, .probe = ov8810_i2c_probe, .remove = __exit_p(ov8810_i2c_remove), .driver = { .name = "ov8810", }, }; static struct early_suspend early_suspend_ov8810 = { .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN+1, .resume = ov8810_resume, .suspend = NULL, }; static const char *Ov8810Vendor = "OmniVision"; static const char *Ov8810NAME = "ov8810"; static const char *Ov8810Size = "8M"; static ssize_t sensor_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; sprintf(buf, "%s %s %s\n", Ov8810Vendor, Ov8810NAME, Ov8810Size); ret = strlen(buf) + 1; return ret; } DEFINE_MUTEX(cam_mode_lock); static ssize_t sensor_read_cam_mode(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t length; mutex_lock(&cam_mode_lock); length = sprintf(buf, "%d\n", cam_mode_sel); mutex_unlock(&cam_mode_lock); return length; } static ssize_t sensor_set_cam_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { uint32_t tmp = 0; mutex_lock(&cam_mode_lock); tmp = buf[0] - 0x30; /* only get the first char */ cam_mode_sel = tmp; mutex_unlock(&cam_mode_lock); return count; } static ssize_t sensor_read_node(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t length; length = sprintf(buf, "%d\n", sensor_probe_node); return length; } static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); static DEVICE_ATTR(cam_mode, 0644, sensor_read_cam_mode, sensor_set_cam_mode); static DEVICE_ATTR(node, 0444, sensor_read_node, NULL); static struct kobject *android_ov8810; static int ov8810_sysfs_init(void) { int ret = 0; pr_info("ov8810:kobject creat and add\n"); android_ov8810 = kobject_create_and_add("android_camera", NULL); if (android_ov8810 == NULL) { pr_info("ov8810_sysfs_init: subsystem_register failed\n"); ret = -ENOMEM; return ret ; } pr_info("Ov8810:sysfs_create_file\n"); ret = sysfs_create_file(android_ov8810, &dev_attr_sensor.attr); if (ret) { pr_info("ov8810_sysfs_init: sysfs_create_file failed\n"); ret = -EFAULT; goto error; } ret = sysfs_create_file(android_ov8810, &dev_attr_cam_mode.attr); if (ret) { pr_info("ov8810_sysfs_init: dev_attr_cam_mode failed\n"); ret = -EFAULT; goto error; } ret = sysfs_create_file(android_ov8810, &dev_attr_node.attr); if (ret) { pr_info("ov8810_sysfs_init: dev_attr_node failed\n"); ret = -EFAULT; goto error; } return ret; error: kobject_del(android_ov8810); return ret; } int ov8810_sensor_config(void __user *argp) { struct sensor_cfg_data cdata; long rc = 0; if (copy_from_user(&cdata, (void *)argp, sizeof(struct sensor_cfg_data))) return -EFAULT; down(&ov8810_sem); CDBG("ov8810_sensor_config: cfgtype = %d\n", cdata.cfgtype); switch (cdata.cfgtype) { case CFG_GET_PICT_FPS: ov8810_get_pict_fps( cdata.cfg.gfps.prevfps, &(cdata.cfg.gfps.pictfps)); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PREV_L_PF: cdata.cfg.prevl_pf = ov8810_get_prev_lines_pf(); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PREV_P_PL: cdata.cfg.prevp_pl = ov8810_get_prev_pixels_pl(); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_L_PF: cdata.cfg.pictl_pf = ov8810_get_pict_lines_pf(); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_P_PL: cdata.cfg.pictp_pl = ov8810_get_pict_pixels_pl(); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_MAX_EXP_LC: cdata.cfg.pict_max_exp_lc = ov8810_get_pict_max_exp_lc(); if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_SET_FPS: case CFG_SET_PICT_FPS: rc = ov8810_set_fps(&(cdata.cfg.fps)); break; case CFG_SET_EXP_GAIN: rc = ov8810_write_exp_gain( cdata.cfg.exp_gain.mul, cdata.cfg.exp_gain.gain, cdata.cfg.exp_gain.line); break; case CFG_SET_PICT_EXP_GAIN: rc = ov8810_set_pict_exp_gain( cdata.cfg.exp_gain.mul, cdata.cfg.exp_gain.gain, cdata.cfg.exp_gain.line); break; case CFG_SET_MODE: rc = ov8810_set_sensor_mode(cdata.mode, cdata.rs); break; case CFG_PWR_DOWN: rc = ov8810_power_down(); break; case CFG_MOVE_FOCUS: rc = ov8810_move_focus( cdata.cfg.focus.dir, cdata.cfg.focus.steps); break; case CFG_SET_DEFAULT_FOCUS: rc = ov8810_set_default_focus( cdata.cfg.focus.steps); break; case CFG_SET_EFFECT: rc = ov8810_set_default_focus( cdata.cfg.effect); break; case CFG_I2C_IOCTL_R_OTP:{ rc = ov8810_i2c_read_fuseid(&cdata); if (copy_to_user (argp, &cdata, sizeof(struct sensor_cfg_data)) ) rc = -EFAULT; } break; case CFG_SET_OV_LSC: rc = ov8810_update_lsc_table(&cdata); break; /*20100330 vincent for lsc calibration*/ case CFG_SET_OV_LSC_RAW_CAPTURE: rc = ov8810_LSC_calibration_set_rawflag(&cdata); break; default: rc = -EFAULT; break; } prevent_suspend(); up(&ov8810_sem); return rc; } static int ov8810_sensor_release(void) { int rc = -EBADF; down(&ov8810_sem); msleep(35); gpio_request(ov8810_ctrl->sensordata->sensor_pwd, "ov8810"); gpio_direction_output(ov8810_ctrl->sensordata->sensor_pwd, 1); gpio_free(ov8810_ctrl->sensordata->sensor_pwd); pr_info("vreg_af_actuator vreg_disable\n"); vreg_disable(vreg_af_actuator); msleep(20); pr_info("%s, %d\n", __func__, __LINE__); msm_camio_probe_off(ov8810_pdev); if (ov8810_ctrl) { kfree(ov8810_ctrl); ov8810_ctrl = NULL; } allow_suspend(); pr_info("ov8810_release completed\n"); up(&ov8810_sem); return rc; } static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, struct msm_sensor_ctrl *s) { int rc = 0; rc = i2c_add_driver(&ov8810_i2c_driver); if (rc < 0 || ov8810_client == NULL) { rc = -ENOTSUPP; goto probe_fail; } pr_info("ov8810 s->node %d\n", s->node); sensor_probe_node = s->node; /*switch pclk and mclk between main cam and 2nd cam*/ /*only for supersonic*/ pr_info("Ov8810: doing clk switch (ov8810)\n"); if(info->camera_clk_switch != NULL) info->camera_clk_switch(); mdelay(5); /*power down setup*/ rc = gpio_request(info->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(info->sensor_pwd, 0); else pr_err("GPIO (%d) request faile\n", info->sensor_pwd); gpio_free(info->sensor_pwd); mdelay(5); /*reset setup */ rc = gpio_request(info->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(info->sensor_reset, 1); else pr_err("GPIO (%d) request faile\n", info->sensor_reset); gpio_free(info->sensor_reset); /*set MCLK*/ pr_info("%s, msm_camio_clk_rate_set %d\n", __func__, OV8810_DEFAULT_CLOCK_RATE); msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); msleep(100); /*read sensor id*/ rc = ov8810_probe_read_id(info); if (rc < 0) goto probe_fail; /* Initialize Sensor registers */ rc = initialize_ov8810_registers(); if (rc < 0) return rc; init_suspend(); s->s_init = ov8810_sensor_open_init; s->s_release = ov8810_sensor_release; s->s_config = ov8810_sensor_config; msleep(20); ov8810_probe_init_done(info); /*register late resuem*/ register_early_suspend(&early_suspend_ov8810); /*init wait event*/ init_waitqueue_head(&ov8810_event.event_wait); /*init waked_up value*/ ov8810_event.waked_up = 1; /*write sysfs*/ ov8810_sysfs_init(); pr_info("%s: ov8810_probe_init_done %d\n", __func__, __LINE__); goto probe_done; probe_fail: pr_err("SENSOR PROBE FAILS!\n"); probe_done: return rc; } static int ov8810_vreg_enable(struct platform_device *pdev) { struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; int rc; pr_info("%s camera vreg on\n", __func__); if (sdata->camera_power_on == NULL) { pr_err("sensor platform_data didnt register\n"); return -EIO; } rc = sdata->camera_power_on(); return rc; } static int ov8810_vreg_disable(struct platform_device *pdev) { struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; int rc; printk(KERN_INFO "%s camera vreg off\n", __func__); if (sdata->camera_power_off == NULL) { pr_err("sensor platform_data didnt register\n"); return -EIO; } rc = sdata->camera_power_off(); return rc; } static int __ov8810_probe(struct platform_device *pdev) { int rc; printk("__ov8810_probe\n"); ov8810_pdev = pdev; rc = ov8810_vreg_enable(pdev); if (rc < 0) pr_err("__ov8810_probe fail sensor power on error\n"); return msm_camera_drv_start(pdev, ov8810_sensor_probe); } static struct platform_driver msm_camera_driver = { .probe = __ov8810_probe, .driver = { .name = "msm_camera_ov8810", .owner = THIS_MODULE, }, .suspend = ov8810_suspend, }; static int __init ov8810_init(void) { return platform_driver_register(&msm_camera_driver); } module_init(ov8810_init);