/* 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 <linux/delay.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/earlysuspend.h>
#include <linux/wakelock.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include <mach/camera.h>
#include <mach/vreg.h>
#include <asm/mach-types.h>
#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);
