1352 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1352 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Copyright (C) 2007-2008 HTC Corporation.
 | |
|  *
 | |
|  * This software is licensed under the terms of the GNU General Public
 | |
|  * License version 2, as published by the Free Software Foundation, and
 | |
|  * may be copied, distributed, and modified under those terms.
 | |
|  *
 | |
|  * 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.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/irq.h>
 | |
| #include <linux/miscdevice.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/input.h>
 | |
| #include <linux/workqueue.h>
 | |
| #include <linux/freezer.h>
 | |
| #include <linux/netlink.h>
 | |
| #include <linux/skbuff.h>
 | |
| #include <linux/clk.h>
 | |
| #include <linux/wakelock.h>
 | |
| #include <net/sock.h>
 | |
| #include <asm/gpio.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <asm/io.h>
 | |
| #include <asm/mach-types.h>
 | |
| #include <mach/msm_iomap.h>
 | |
| #include <mach/msm_rpcrouter.h>
 | |
| #include <mach/vreg.h>
 | |
| #include <mach/board.h>
 | |
| #include <linux/mt9t013.h> /* define ioctls */
 | |
| 
 | |
| 
 | |
| #define ALLOW_USPACE_RW		0
 | |
| 
 | |
| static const uint32_t fps_divider = 1;
 | |
| 
 | |
| #define AF_I2C_ID	0x18  /* actuator's slave address */
 | |
| 
 | |
| static struct i2c_client *pclient;
 | |
| 
 | |
| /* we need this to set the clock rate */
 | |
| static struct clk *vfe_clk;
 | |
| 
 | |
| /* camif clocks */
 | |
| static struct clk *vfe_mdc_clk;
 | |
| static struct clk *mdc_clk;
 | |
| 
 | |
| static int mdc_clk_enabled;
 | |
| static int vfe_mdc_clk_enabled;
 | |
| static int vfe_clk_enabled;
 | |
| static int opened;
 | |
| static int pclk_set;
 | |
| 
 | |
| static const struct mt9t013_reg_pat mt9t013_reg_pattern = { .reg = {
 | |
| 	{ /* preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */
 | |
| 		10,	/*vt_pix_clk_div        REG=0x0300*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		1,	/*vt_sys_clk_div        REG=0x0302*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		3,	/*2,  pre_pll_clk_div   REG=0x0304*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		80,	/*40, pll_multiplier   REG=0x0306*/
 | |
| 			/*60 for 30fps preview, 40 for 20fps preview*/
 | |
| 		10,	/*op_pix_clk_div        REG=0x0308*/
 | |
| 		1,	/*op_sys_clk_div        REG=0x030A*/
 | |
| 		16,	/*scale_m       REG=0x0404*/
 | |
| 		0x0111,	/*row_speed     REG=0x3016*/
 | |
| 		8,	/*x_addr_start  REG=0x3004*/
 | |
| 		2053,	/*x_addr_end    REG=0x3008*/
 | |
| 		8,	/*y_addr_start  REG=0x3002*/
 | |
| 		1541,	/*y_addr_end    REG=0x3006*/
 | |
| 		0x046C,	/*read_mode     REG=0x3040*/
 | |
| 		1024,	/*x_output_size REG=0x034C*/
 | |
| 		768,	/*y_output_size REG=0x034E*/
 | |
| 		3540,	/*2616,        line_length_pck       REG=0x300C*/
 | |
| 		861,	/*916, frame_length_lines     REG=0x300A*/
 | |
| 		16,	/*coarse_integration_time     REG=0x3012*/
 | |
| 		1461	/*fine_integration_time REG=0x3014*/
 | |
| 	},
 | |
| 	{ /* snapshot */
 | |
| 		10,	/*vt_pix_clk_div        REG=0x0300*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		1,	/*vt_sys_clk_div        REG=0x0302*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		3,	/*2,  pre_pll_clk_div   REG=0x0304*/
 | |
| 			/*update get_snapshot_fps if this change*/
 | |
| 		80,	/*40, pll_multiplier   REG=0x0306*/
 | |
| 			/*50 for 15fps snapshot, 40 for 10fps snapshot*/
 | |
| 		10,	/*op_pix_clk_div        REG=0x0308*/
 | |
| 		1,	/*op_sys_clk_div        REG=0x030A*/
 | |
| 		16,	/*scale_m       REG=0x0404*/
 | |
| 		0x0111,	/*row_speed     REG=0x3016*/
 | |
| 		8,	/*0,     x_addr_start  REG=0x3004*/
 | |
| 		2063,	/*2061,       x_addr_end    REG=0x3008*/
 | |
| 		8,	/*2,     y_addr_start  REG=0x3002*/
 | |
| 		1551,	/*1545,       y_addr_end    REG=0x3006*/
 | |
| 		0x0024,	/*read_mode     REG=0x3040*/
 | |
| 		2063,	/*output_size REG=0x034C*/
 | |
| 		1544,	/*y_output_size REG=0x034E*/
 | |
| 		4800,	/*2952,        line_length_pck       REG=0x300C*/
 | |
| 		1629,	/*frame_length_lines    REG=0x300A*/
 | |
| 		16,	/*coarse_integration_time       REG=0x3012*/
 | |
| 		733	/*fine_integration_time REG=0x3014*/
 | |
| 	}
 | |
| }};
 | |
| 
 | |
| #define MT9T013_MU3M0VC_REG_MODEL_ID		0x0000
 | |
| #define MT9T013_MU3M0VC_MODEL_ID		0x2600
 | |
| #define REG_GROUPED_PARAMETER_HOLD		0x0104
 | |
| #define GROUPED_PARAMETER_HOLD			0x0100
 | |
| #define GROUPED_PARAMETER_UPDATE		0x0000
 | |
| #define REG_COARSE_INTEGRATION_TIME		0x3012
 | |
| #define REG_VT_PIX_CLK_DIV			0x0300
 | |
| #define REG_VT_SYS_CLK_DIV			0x0302
 | |
| #define REG_PRE_PLL_CLK_DIV			0x0304
 | |
| #define REG_PLL_MULTIPLIER			0x0306
 | |
| #define REG_OP_PIX_CLK_DIV			0x0308
 | |
| #define REG_OP_SYS_CLK_DIV			0x030A
 | |
| #define REG_SCALE_M				0x0404
 | |
| #define REG_FRAME_LENGTH_LINES			0x300A
 | |
| #define REG_LINE_LENGTH_PCK			0x300C
 | |
| #define REG_X_ADDR_START			0x3004
 | |
| #define REG_Y_ADDR_START			0x3002
 | |
| #define REG_X_ADDR_END				0x3008
 | |
| #define REG_Y_ADDR_END				0x3006
 | |
| #define REG_X_OUTPUT_SIZE			0x034C
 | |
| #define REG_Y_OUTPUT_SIZE			0x034E
 | |
| #define REG_FINE_INTEGRATION_TIME		0x3014
 | |
| #define REG_ROW_SPEED				0x3016
 | |
| #define MT9T013_REG_RESET_REGISTER		0x301A
 | |
| #define MT9T013_RESET_REGISTER_PWON		0x10CC   /*enable paralled and start streaming*/
 | |
| #define MT9T013_RESET_REGISTER_PWOFF		0x1008 //0x10C8   /*stop streaming*/
 | |
| #define REG_READ_MODE				0x3040
 | |
| #define REG_GLOBAL_GAIN				0x305E
 | |
| #define REG_TEST_PATTERN_MODE			0x3070
 | |
| 
 | |
| static struct wake_lock mt9t013_wake_lock;
 | |
| 
 | |
| static inline void init_suspend(void)
 | |
| {
 | |
| 	wake_lock_init(&mt9t013_wake_lock, WAKE_LOCK_IDLE, "mt9t013");
 | |
| }
 | |
| 
 | |
| static inline void deinit_suspend(void)
 | |
| {
 | |
| 	wake_lock_destroy(&mt9t013_wake_lock);
 | |
| }
 | |
| 
 | |
| static inline void prevent_suspend(void)
 | |
| {
 | |
| 	wake_lock(&mt9t013_wake_lock);
 | |
| }
 | |
| 
 | |
| static inline void allow_suspend(void)
 | |
| {
 | |
| 	wake_unlock(&mt9t013_wake_lock);
 | |
| }
 | |
| 
 | |
| #define CLK_GET(clk) do {						\
 | |
| 	if (!clk) {							\
 | |
| 		clk = clk_get(NULL, #clk);				\
 | |
| 		printk(KERN_INFO 					\
 | |
| 			"mt9t013: clk_get(%s): %p\n", #clk, clk);	\
 | |
| 	}								\
 | |
| } while(0)
 | |
| 
 | |
| DECLARE_MUTEX(sem);
 | |
| 
 | |
| static struct msm_camera_legacy_device_platform_data  *cam;
 | |
| 
 | |
| #define out_dword(addr, val) \
 | |
| 	(*((volatile unsigned long  *)(addr)) = ((unsigned long)(val)))
 | |
| 
 | |
| #define out_dword_masked_ns(io, mask, val, current_reg_content)	    \
 | |
|   (void) out_dword(io, ((current_reg_content & (uint32_t)(~(mask))) | \
 | |
| 			 ((uint32_t)((val) & (mask)))))
 | |
| 
 | |
| #define __inpdw(port) (*((volatile uint32_t *) (port)))
 | |
| #define in_dword_masked(addr, mask) (__inpdw(addr) & (uint32_t)mask )
 | |
| 
 | |
| #define HWIO_MDDI_CAMIF_CFG_ADDR MSM_MDC_BASE
 | |
| #define HWIO_MDDI_CAMIF_CFG_RMSK 0x1fffff
 | |
| #define HWIO_MDDI_CAMIF_CFG_IN \
 | |
|   in_dword_masked(HWIO_MDDI_CAMIF_CFG_ADDR, HWIO_MDDI_CAMIF_CFG_RMSK)
 | |
| 
 | |
| #define HWIO_MDDI_CAMIF_CFG_OUTM(m,v) \
 | |
|   out_dword_masked_ns(HWIO_MDDI_CAMIF_CFG_ADDR,m,v,HWIO_MDDI_CAMIF_CFG_IN);
 | |
| #define __msmhwio_outm(hwiosym, mask, val) HWIO_##hwiosym##_OUTM(mask, val)
 | |
| #define HWIO_OUTM(hwiosym, mask, val) __msmhwio_outm(hwiosym, mask, val)
 | |
| 
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_SEL_BMSK 0x2
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_BMSK 0x60000
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_BMSK 0x80000
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_BMSK 0x100000
 | |
| 
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_SEL_SHFT 0x1
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_SHFT 0x11
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_SHFT 0x13
 | |
| #define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_SHFT 0x14
 | |
| 
 | |
| #define __msmhwio_shft(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_SHFT
 | |
| #define HWIO_SHFT(hwio_regsym, hwio_fldsym) __msmhwio_shft(hwio_regsym, hwio_fldsym)
 | |
| 
 | |
| #define __msmhwio_fmsk(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_BMSK
 | |
| #define HWIO_FMSK(hwio_regsym, hwio_fldsym) __msmhwio_fmsk(hwio_regsym, hwio_fldsym)
 | |
| 
 | |
| #define HWIO_APPS_RESET_ADDR (MSM_CLK_CTL_BASE + 0x00000210)
 | |
| #define HWIO_APPS_RESET_RMSK 0x1fff
 | |
| #define HWIO_APPS_RESET_VFE_BMSK 1
 | |
| #define HWIO_APPS_RESET_VFE_SHFT 0 
 | |
| #define HWIO_APPS_RESET_IN in_dword_masked(HWIO_APPS_RESET_ADDR, HWIO_APPS_RESET_RMSK)
 | |
| #define HWIO_APPS_RESET_OUTM(m,v) out_dword_masked_ns(HWIO_APPS_RESET_ADDR,m,v,HWIO_APPS_RESET_IN)
 | |
| 
 | |
| struct mt9t013_data {
 | |
| 	struct work_struct work;
 | |
| };
 | |
| 
 | |
| static DECLARE_WAIT_QUEUE_HEAD(g_data_ready_wait_queue);
 | |
| 
 | |
| static int mt9t013_i2c_sensor_init(struct mt9t013_init *init);
 | |
| static int mt9t013_i2c_sensor_setting(unsigned long arg);
 | |
| static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line,
 | |
| 					uint16_t gain);
 | |
| static int mt9t013_i2c_move_focus(uint16_t position);
 | |
| static int mt9t013_i2c_set_default_focus(uint8_t step);
 | |
| static int mt9t013_i2c_power_up(void);
 | |
| static int mt9t013_i2c_power_down(void);
 | |
| static int mt9t013_camif_pad_reg_reset(void);
 | |
| static int mt9t013_lens_power(int on);
 | |
| 
 | |
| int mt_i2c_lens_tx_data(unsigned char slave_addr, char* txData, int length)
 | |
| {
 | |
| 	int rc;
 | |
| 	struct i2c_msg msg[] = {
 | |
| 		{
 | |
| 			.addr = slave_addr,
 | |
| 			.flags = 0,
 | |
| 			.len = length,
 | |
| 			.buf = txData,		
 | |
| 		},
 | |
| 	};
 | |
| 
 | |
| #if 0
 | |
| 	{
 | |
| 		int i;
 | |
| 		/* printk(KERN_INFO "mt_i2c_lens_tx_data: af i2c client addr = %x,"
 | |
| 		   " register addr = 0x%02x%02x:\n", slave_addr, txData[0], txData[1]); 
 | |
| 		*/
 | |
| 		for (i = 0; i < length - 2; i++)
 | |
| 			printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, txData[i+2]);
 | |
| 	}
 | |
| #endif
 | |
|     
 | |
| 	rc = i2c_transfer(pclient->adapter, msg, 1);
 | |
| 	if (rc < 0) {
 | |
| 		printk(KERN_ERR "mt_i2c_lens_tx_data: i2c_transfer error %d\n", rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_lens_write(unsigned char slave_addr, unsigned char u_addr, unsigned char u_data)
 | |
| {
 | |
| 	unsigned char buf[2] = { u_addr, u_data };
 | |
| 	return mt_i2c_lens_tx_data(slave_addr, buf, sizeof(buf));
 | |
| }
 | |
| 
 | |
| static int mt_i2c_rx_data(char* rxData, int length)
 | |
| {
 | |
| 	int rc;
 | |
| 	struct i2c_msg msgs[] = {
 | |
| 		{
 | |
| 			.addr = pclient->addr,
 | |
| 			.flags = 0,      
 | |
| 			.len = 2,
 | |
| 			.buf = rxData,
 | |
| 		},
 | |
| 		{
 | |
| 			.addr = pclient->addr,
 | |
| 			.flags = I2C_M_RD,
 | |
| 			.len = length,
 | |
| 			.buf = rxData,
 | |
| 		},
 | |
| 	};
 | |
| 
 | |
| 	rc = i2c_transfer(pclient->adapter, msgs, 2);
 | |
| 	if (rc < 0) {
 | |
| 		printk(KERN_ERR "mt9t013: mt_i2c_rx_data error %d\n", rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| #if 0
 | |
| 	else {
 | |
| 		int i;
 | |
| 		for (i = 0; i < length; i++)
 | |
| 			printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, rxData[i]);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int mt_i2c_tx_data(char* txData, int length)
 | |
| {
 | |
| 	int rc; 
 | |
| 
 | |
| 	struct i2c_msg msg[] = {
 | |
| 		{
 | |
| 			.addr = pclient->addr,
 | |
| 			.flags = 0,
 | |
| 			.len = length,
 | |
| 			.buf = txData,		
 | |
| 		},
 | |
| 	};
 | |
|     
 | |
| 	rc = i2c_transfer(pclient->adapter, msg, 1);
 | |
| 	if (rc < 0) {
 | |
| 		printk(KERN_ERR "mt9t013: mt_i2c_tx_data error %d\n", rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_write(unsigned short u_addr, unsigned short u_data)
 | |
| {
 | |
| 	int rc;
 | |
| 	unsigned char buf[4];
 | |
| 
 | |
| 	buf[0] = (u_addr & 0xFF00) >> 8;
 | |
| 	buf[1] = u_addr & 0x00FF;
 | |
| 	buf[2] = (u_data & 0xFF00) >> 8;
 | |
| 	buf[3] = u_data & 0x00FF;
 | |
| 
 | |
| 	rc = mt_i2c_tx_data(buf, sizeof(buf));
 | |
| 	if(rc < 0)
 | |
| 		printk(KERN_ERR "mt9t013: txdata error %d add:0x%02x data:0x%02x\n",
 | |
| 			rc, u_addr, u_data);
 | |
| 	return rc;	
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_read(unsigned short u_addr, unsigned short *pu_data)
 | |
| {
 | |
| 	int rc;
 | |
| 	unsigned char buf[2];
 | |
| 
 | |
| 	buf[0] = (u_addr & 0xFF00)>>8;
 | |
| 	buf[1] = (u_addr & 0x00FF);
 | |
| 	rc = mt_i2c_rx_data(buf, 2);
 | |
| 	if (!rc)
 | |
| 		*pu_data = buf[0]<<8 | buf[1];
 | |
| 	else printk(KERN_ERR "mt9t013: i2c read failed\n");
 | |
| 	return rc;	
 | |
| }
 | |
| 
 | |
| static int msm_camio_clk_enable (int clk_type)
 | |
| {
 | |
| 	struct clk *clk = NULL;
 | |
| 	int *enabled = NULL;
 | |
| 
 | |
| 	switch (clk_type) {
 | |
| 	case CAMIO_VFE_MDC_CLK:
 | |
| 		CLK_GET(vfe_mdc_clk);
 | |
| 		clk = vfe_mdc_clk;
 | |
| 		enabled = &vfe_mdc_clk_enabled;
 | |
| 		break;
 | |
| 	case CAMIO_MDC_CLK:
 | |
| 		CLK_GET(mdc_clk);
 | |
| 		clk = mdc_clk;
 | |
| 		enabled = &mdc_clk_enabled;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (clk != NULL && !*enabled) {
 | |
| 		int rc = clk_enable(clk);
 | |
| 		*enabled = !rc;	
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	return -EINVAL; 
 | |
| }
 | |
| 
 | |
| static int msm_camio_clk_disable(int clk_type)
 | |
| {
 | |
| 	int rc = 0;
 | |
| 	struct clk *clk = NULL;
 | |
| 	int *enabled = NULL;
 | |
| 
 | |
| 	switch (clk_type) {
 | |
| 	case CAMIO_VFE_MDC_CLK:
 | |
| 		clk = vfe_mdc_clk;
 | |
| 		enabled = &vfe_mdc_clk_enabled;
 | |
| 		break;
 | |
| 	case CAMIO_MDC_CLK:
 | |
| 		clk = mdc_clk;
 | |
| 		enabled = &mdc_clk_enabled;
 | |
| 		break;
 | |
| 	default:
 | |
| 		rc = -1;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (clk != NULL && *enabled) {
 | |
| 		clk_disable(clk);
 | |
| 		*enabled = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return -EINVAL;
 | |
| }
 | |
| 
 | |
| static int msm_camio_vfe_clk_enable(void)
 | |
| {
 | |
| 	CLK_GET(vfe_clk);
 | |
| 	if (vfe_clk && !vfe_clk_enabled) {
 | |
| 		vfe_clk_enabled = !clk_enable(vfe_clk);
 | |
| 		printk(KERN_INFO "mt9t013: enable vfe_clk\n");
 | |
| 	}
 | |
| 	return vfe_clk_enabled ? 0 : -EIO;
 | |
| }
 | |
| 
 | |
| static int msm_camio_clk_rate_set(int rate)
 | |
| {
 | |
| 	int rc = msm_camio_vfe_clk_enable();
 | |
| 	if (!rc && vfe_clk_enabled)
 | |
| 		rc = clk_set_rate(vfe_clk, rate);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int clk_select(int internal)
 | |
| {
 | |
| 	int rc = -EIO;
 | |
| 	printk(KERN_INFO "mt9t013: clk select %d\n", internal);
 | |
| 	CLK_GET(vfe_clk);
 | |
| 	if (vfe_clk != NULL) {
 | |
| 		extern int clk_set_flags(struct clk *clk, unsigned long flags);
 | |
| 		rc = clk_set_flags(vfe_clk, 0x00000100 << internal);
 | |
| 		if (!rc && internal) rc = msm_camio_vfe_clk_enable();
 | |
| 	}
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static void mt9t013_sensor_init(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	printk(KERN_INFO "mt9t013: init\n");
 | |
| 	if (!pclient)
 | |
| 		return;
 | |
| 
 | |
| 	/*pull hi reset*/
 | |
| 	printk(KERN_INFO "mt9t013: mt9t013_register_init\n");
 | |
| 	ret = gpio_request(cam->sensor_reset, "mt9t013");
 | |
| 	if (!ret) {
 | |
| 		gpio_direction_output(cam->sensor_reset, 1);
 | |
| 		printk(KERN_INFO "mt9t013: camera sensor_reset set as 1\n");
 | |
| 	} else
 | |
| 		printk(KERN_ERR "mt9t013 error: request gpio %d failed: "
 | |
| 				"%d\n", cam->sensor_reset, ret);
 | |
| 	mdelay(2);
 | |
| 
 | |
| 	/* pull down power down */
 | |
| 	ret = gpio_request(cam->sensor_pwd, "mt9t013");
 | |
| 	if (!ret || ret == -EBUSY)
 | |
| 		gpio_direction_output(cam->sensor_pwd, 0);
 | |
| 	else printk(KERN_ERR "mt913t013 error: request gpio %d failed: "
 | |
| 			"%d\n", cam->sensor_pwd, ret);
 | |
| 	gpio_free(cam->sensor_pwd);
 | |
| 
 | |
| 	/* enable clk */
 | |
| 	msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
 | |
| 	msm_camio_clk_enable(CAMIO_MDC_CLK);
 | |
| 
 | |
| 	/* reset CAMIF */
 | |
| 	mt9t013_camif_pad_reg_reset();
 | |
| 
 | |
| 	/* set mclk */
 | |
| 	ret = msm_camio_clk_rate_set(24000000);
 | |
| 	if(ret < 0)
 | |
| 		printk(KERN_ERR "camio clk rate select error\n");
 | |
| 	mdelay(2);
 | |
| 
 | |
| 	/* enable gpio */
 | |
| 	cam->config_gpio_on();
 | |
| 	/* delay 2 ms */
 | |
| 	mdelay(2);
 | |
| 
 | |
| 	/* reset sensor sequency */
 | |
| 	gpio_direction_output(cam->sensor_reset, 0);
 | |
| 	mdelay(2);
 | |
| 	gpio_direction_output(cam->sensor_reset, 1);
 | |
| 	gpio_free(cam->sensor_reset);
 | |
| 	mdelay(2);
 | |
| 
 | |
| 	printk(KERN_INFO "mt9t013: camera sensor init sequence done\n");
 | |
| }
 | |
| 
 | |
| #define CLK_DISABLE_AND_PUT(clk) do {					\
 | |
| 	if (clk) {							\
 | |
| 		if (clk##_enabled) {					\
 | |
| 			printk(KERN_INFO "mt9t013: disabling "#clk"\n");\
 | |
| 			clk_disable(clk);				\
 | |
| 			clk##_enabled = 0;				\
 | |
| 		}							\
 | |
| 		printk(KERN_INFO 					\
 | |
| 			"mt9t013: clk_put(%s): %p\n", #clk, clk);	\
 | |
| 		clk_put(clk);						\
 | |
| 		clk = NULL; 						\
 | |
| 	}								\
 | |
| } while(0)
 | |
| 
 | |
| static void mt9t013_sensor_suspend(void)
 | |
| {
 | |
| 	printk(KERN_INFO "mt9t013: camera sensor suspend sequence\n");
 | |
| 	if (!pclient) {
 | |
| 		return;
 | |
| 	}
 | |
| 	/*disable clk*/
 | |
| 	msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
 | |
| 	msm_camio_clk_disable(CAMIO_MDC_CLK);
 | |
| 	CLK_DISABLE_AND_PUT(vfe_clk); /* this matches clk_select(1) */
 | |
| 	/* disable gpios */
 | |
| 	cam->config_gpio_off();
 | |
| 	printk(KERN_INFO "mt9t013: camera sensor suspend sequence done\n");
 | |
| }
 | |
| 
 | |
| static int mt9t013_open(struct inode *ip, struct file *fp)
 | |
| {
 | |
| 	int rc = -EBUSY;
 | |
| 	down(&sem);
 | |
| 	printk(KERN_INFO "mt9t013: open\n");
 | |
| 	if (!opened) {
 | |
| 		printk(KERN_INFO "mt9t013: prevent collapse on idle\n");
 | |
| 		prevent_suspend();
 | |
| 		cam->config_gpio_on();
 | |
| 		opened = 1;
 | |
| 		rc = 0;
 | |
| 	}
 | |
| 	up(&sem);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int mt9t013_release(struct inode *ip, struct file *fp)
 | |
| {
 | |
| 	int rc = -EBADF;
 | |
| 	printk(KERN_INFO "mt9t013: release\n");
 | |
| 	down(&sem);
 | |
| 	if (opened) {
 | |
| 		printk(KERN_INFO "mt9t013: release clocks\n");
 | |
| 
 | |
| 
 | |
| 		/* mt9t013_i2c_power_down() should be called before closing MCLK */
 | |
| 		/* otherwise I2C_WRITE will always fail                          */
 | |
| 		mt9t013_i2c_power_down();
 | |
| 
 | |
| 		CLK_DISABLE_AND_PUT(mdc_clk);
 | |
| 		CLK_DISABLE_AND_PUT(vfe_mdc_clk);
 | |
| 		CLK_DISABLE_AND_PUT(vfe_clk);
 | |
| 		mt9t013_lens_power(0);
 | |
| 
 | |
| 		cam->config_gpio_off();
 | |
| 
 | |
| 		printk(KERN_INFO "mt9t013: allow collapse on idle\n");
 | |
| 		allow_suspend();
 | |
| 		rc = pclk_set = opened = 0;
 | |
| 	}
 | |
| 	up(&sem);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| #undef CLK_DISABLE_AND_PUT
 | |
| 
 | |
| #define CHECK() ({ 							\
 | |
| 	if (!mdc_clk_enabled || !vfe_mdc_clk_enabled) { 		\
 | |
| 		printk(KERN_ERR "mt9t013 error: one or more clocks"	\
 | |
| 			" are NULL.\n"); 				\
 | |
| 		rc = -EIO; 						\
 | |
| 	} 								\
 | |
| 	!rc; })
 | |
| 
 | |
| static int mt9t013_camif_pad_reg_reset(void)
 | |
| {
 | |
| 	int rc = clk_select(1);
 | |
| 	if(rc < 0) {
 | |
| 		printk(KERN_ERR "mt9t013 error switching to internal clock\n");
 | |
| 		return rc;
 | |
| 	}
 | |
| 	HWIO_OUTM (MDDI_CAMIF_CFG,
 | |
| 		HWIO_FMSK (MDDI_CAMIF_CFG, CAM_SEL) |
 | |
| 		HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) |
 | |
| 		HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_INVERT),
 | |
| 		1 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_SEL) |
 | |
| 		3 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) |
 | |
| 		0 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_INVERT));
 | |
| 	msleep(10);
 | |
| 	HWIO_OUTM (MDDI_CAMIF_CFG,
 | |
| 		HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
 | |
| 		1 << HWIO_SHFT (MDDI_CAMIF_CFG,
 | |
| 		CAM_PAD_REG_SW_RESET));
 | |
| 	msleep(10);
 | |
| 	HWIO_OUTM (MDDI_CAMIF_CFG,
 | |
| 		HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
 | |
| 		0 << HWIO_SHFT (MDDI_CAMIF_CFG,
 | |
| 		CAM_PAD_REG_SW_RESET));
 | |
| 	msleep(10);
 | |
| 	rc = clk_select(0); /* external */
 | |
| 	if(rc < 0) {
 | |
| 		printk(KERN_ERR "mt9t013 error switching to external clock\n");
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| #if ALLOW_USPACE_RW
 | |
| #define COPY_FROM_USER(size) ({                                         \
 | |
|         if (copy_from_user(rwbuf, argp, size)) rc = -EFAULT;            \
 | |
|         !rc; })
 | |
| #endif
 | |
| 
 | |
| static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 | |
| {
 | |
| 	void __user *argp = (void __user *)arg;
 | |
| 	int rc = 0;
 | |
| 	
 | |
| #if ALLOW_USPACE_RW
 | |
| 	unsigned short addr = 0;
 | |
| 	unsigned short data = 0;
 | |
| 	char rwbuf[4];
 | |
| #endif
 | |
| 
 | |
| 	down(&sem);
 | |
| 
 | |
| 	switch(cmd) {
 | |
| #if ALLOW_USPACE_RW
 | |
| 	case MT9T013_I2C_IOCTL_W:
 | |
| 		if (/* CHECK() && */ COPY_FROM_USER(4)) {
 | |
| 			addr = *((unsigned short *)rwbuf);
 | |
| 			data = *((unsigned short *)(rwbuf+2));
 | |
| 			rc = mt9t013_i2c_write(addr, data);
 | |
| 		} else
 | |
| 			printk(KERN_ERR "mt9t013: write: err %d\n", rc);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_R:
 | |
| 		if (/* CHECK() && */ COPY_FROM_USER(4)) {
 | |
| 			addr = *((unsigned short*) rwbuf);
 | |
| 			rc = mt9t013_i2c_read(addr, (unsigned short *)(rwbuf+2));
 | |
| 			if (!rc) {
 | |
| 				if (copy_to_user(argp, rwbuf, 4)) {
 | |
| 					printk(KERN_ERR "mt9t013: read: err " \
 | |
| 							"writeback -EFAULT\n");
 | |
| 					rc = -EFAULT;
 | |
| 				}
 | |
| 			}
 | |
| 		} else
 | |
| 			printk(KERN_ERR "mt9t013: read: err %d\n", rc);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_AF_W:
 | |
| 		if (/* CHECK() && */ COPY_FROM_USER(3))
 | |
| 			rc = mt9t013_i2c_lens_write(*rwbuf, *(rwbuf + 1), *(rwbuf + 2));
 | |
| 		else
 | |
| 			printk(KERN_ERR "mt9t013: af write: err %d\n", rc);
 | |
| 		break;
 | |
| #endif /* ALLOW_USPACE_RW */
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET:
 | |
| 		printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET\n"); 
 | |
| 		if (CHECK())
 | |
| 			rc = mt9t013_camif_pad_reg_reset();
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET_2:
 | |
| 		printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET_2 (pclk_set %d)\n",
 | |
| 				pclk_set);
 | |
| 		if (!pclk_set)
 | |
| 			rc = -EIO;
 | |
| 		else if (CHECK()) {
 | |
| 			HWIO_OUTM (MDDI_CAMIF_CFG,
 | |
| 				HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
 | |
| 					1 << HWIO_SHFT (MDDI_CAMIF_CFG,
 | |
| 					CAM_PAD_REG_SW_RESET));
 | |
| 			msleep(10);
 | |
| 			HWIO_OUTM (MDDI_CAMIF_CFG,
 | |
| 				HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET),
 | |
| 				0 << HWIO_SHFT (MDDI_CAMIF_CFG,
 | |
| 				CAM_PAD_REG_SW_RESET));
 | |
| 			msleep(10);
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CAMIF_APPS_RESET:
 | |
| 		printk(KERN_INFO "mt9t013: CAMIF_APPS_RESET\n"); 
 | |
| 		if (CHECK()) {
 | |
| 			rc = clk_select(1);
 | |
| 			if(rc < 0) {
 | |
| 				printk(KERN_ERR "mt9t013 error switching to internal clock\n");
 | |
| 				break;	
 | |
| 			}
 | |
| 			HWIO_OUTM (APPS_RESET,
 | |
| 				HWIO_FMSK(APPS_RESET,VFE),
 | |
| 				1 << HWIO_SHFT(APPS_RESET,VFE));
 | |
| 			udelay(10);
 | |
| 			HWIO_OUTM (APPS_RESET,
 | |
| 				HWIO_FMSK(APPS_RESET,VFE),
 | |
| 				0 << HWIO_SHFT(APPS_RESET,VFE));
 | |
| 			udelay(10);
 | |
| 			rc = clk_select(0); /* external */
 | |
| 			if(rc < 0) {
 | |
| 				printk(KERN_ERR "mt9t013 error switching to external clock\n");
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case CAMERA_LENS_POWER_ON:
 | |
| 		rc = mt9t013_lens_power(1);
 | |
| 		break;
 | |
| 
 | |
| 	case CAMERA_LENS_POWER_OFF:
 | |
| 		rc = mt9t013_lens_power(0);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CLK_ENABLE:
 | |
| 		printk(KERN_INFO "mt9t013: clk enable %ld\n", arg);
 | |
| 		rc = msm_camio_clk_enable(arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CLK_DISABLE:
 | |
| 		printk(KERN_INFO "mt9t013: clk disable %ld\n", arg);
 | |
| 		rc = msm_camio_clk_disable(arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_CLK_SELECT:
 | |
| 		printk(KERN_INFO "mt9t013: clk select %ld\n", arg);
 | |
| 		rc = clk_select(!!arg);
 | |
| 		break;
 | |
|   
 | |
| 	case MT9T013_I2C_IOCTL_CLK_FREQ_PROG:
 | |
| 		printk(KERN_INFO "mt9t013: clk rate select %ld\n", arg);
 | |
| 		rc = msm_camio_clk_rate_set(arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_GET_REGISTERS:
 | |
| 		printk(KERN_INFO "mt9t013: get registers\n");
 | |
| 		if (copy_to_user(argp, &mt9t013_reg_pattern.reg, sizeof(mt9t013_reg_pattern.reg)))
 | |
| 			rc = -EFAULT;
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_SENSOR_SETTING:
 | |
| 		printk(KERN_INFO "mt9t013: sensor setting 0x%lx\n", arg);
 | |
| 		rc = mt9t013_i2c_sensor_setting(arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_EXPOSURE_GAIN: {
 | |
| 		struct mt9t013_exposure_gain exp;
 | |
| 		if (copy_from_user(&exp, argp, sizeof(exp))) {
 | |
| 			printk(KERN_ERR "mt9t013: (exposure gain) invalid user pointer\n");
 | |
| 			rc = -EFAULT;
 | |
| 			break;
 | |
| 		}
 | |
| 		rc = mt9t013_i2c_exposure_gain(exp.mode, exp.line, exp.gain); 
 | |
| 	}
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_MOVE_FOCUS:
 | |
| 		printk(KERN_INFO "mt9t013: move focus %ld\n", arg);
 | |
| 		rc = mt9t013_i2c_move_focus((uint16_t)arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_SET_DEFAULT_FOCUS:
 | |
| 		printk(KERN_INFO "mt9t013: set default focus %ld\n", arg);
 | |
| 		rc = mt9t013_i2c_set_default_focus((uint8_t)arg);
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_POWER_DOWN:
 | |
| 		rc = mt9t013_i2c_power_down();
 | |
| 		break;
 | |
| 
 | |
| 	case MT9T013_I2C_IOCTL_INIT: {
 | |
| 		struct mt9t013_init init;
 | |
| 		printk(KERN_INFO "mt9t013: init\n");
 | |
| 		if (copy_from_user(&init, argp, sizeof(init))) {
 | |
| 			printk(KERN_ERR "mt9t013: (init) invalid user pointer\n");
 | |
| 			rc = -EFAULT;
 | |
| 			break;
 | |
| 		}
 | |
| 		rc = mt9t013_i2c_sensor_init(&init);
 | |
| 		if (copy_to_user(argp, &init, sizeof(init)))
 | |
| 			rc = -EFAULT;
 | |
| 	}
 | |
| 		break;
 | |
| 
 | |
| 	case CAMERA_CONFIGURE_GPIOS:
 | |
| 	case CAMERA_UNCONFIGURE_GPIOS: 
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		printk(KERN_INFO "mt9t013: unknown ioctl %d\n", cmd);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	up(&sem);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| #undef CHECK
 | |
| 
 | |
| static int mt9t013_lens_power(int on)
 | |
| {
 | |
| 	int rc;
 | |
| 	printk(KERN_INFO "mt9t013: lens power %d\n", on);
 | |
| 	rc = gpio_request(cam->vcm_pwd, "mt9t013");
 | |
| 	if (!rc)
 | |
| 		gpio_direction_output(cam->vcm_pwd, !on);
 | |
| 	else printk(KERN_ERR "mt9t013 error: request gpio %d failed:"
 | |
| 		" %d\n", cam->vcm_pwd, rc);
 | |
| 	gpio_free(cam->vcm_pwd);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| #define I2C_WRITE(reg,data) if (!mt9t013_i2c_write(reg, data) < 0) return -EIO
 | |
| #define MT9T013_MU3M0VC_RESET_DELAY_MSECS    66
 | |
| 
 | |
| static int mt9t013_i2c_sensor_init(struct mt9t013_init *init)
 | |
| {
 | |
| 	int rc;
 | |
| 	
 | |
| 	/* RESET the sensor via I2C register */
 | |
| 	I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc & 0xfffe);
 | |
| 	msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS);
 | |
| 
 | |
| 	if ((rc = mt9t013_i2c_read(MT9T013_MU3M0VC_REG_MODEL_ID, &init->chipid)) < 0) {
 | |
| 		printk(KERN_ERR "mt9t013: could not read chip id: %d\n", rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| 	printk(KERN_INFO "mt9t013: chip id: %d\n", init->chipid);
 | |
| 
 | |
| 	if (init->chipid != MT9T013_MU3M0VC_MODEL_ID) {
 | |
| 		printk(KERN_INFO "mt9t013: chip id %d is invalid\n",
 | |
| 			init->chipid);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	I2C_WRITE(0x306E, 0x9080);
 | |
| 	I2C_WRITE(0x301A, 0x10CC);
 | |
| 	I2C_WRITE(0x3064, 0x0805);
 | |
| 	msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS);
 | |
| 
 | |
| 	if ((rc = mt9t013_i2c_sensor_setting(CAMSENSOR_REG_INIT |
 | |
| 					((init->preview ? 0 : 1) << 1))) < 0) {
 | |
| 		printk(KERN_INFO "mt9t013: failed to configure the sensor\n");
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	mt9t013_i2c_power_up();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_mu3m0vc_set_lc(void)
 | |
| {
 | |
| 	/* lens shading 85% TL84 */
 | |
| 	I2C_WRITE(0x360A, 0x0290); // P_RD_P0Q0
 | |
| 	I2C_WRITE(0x360C, 0xC92D); // P_RD_P0Q1
 | |
| 	I2C_WRITE(0x360E, 0x0771); // P_RD_P0Q2
 | |
| 	I2C_WRITE(0x3610, 0xE38C); // P_RD_P0Q3
 | |
| 	I2C_WRITE(0x3612, 0xD74F); // P_RD_P0Q4
 | |
| 	I2C_WRITE(0x364A, 0x168C); // P_RD_P1Q0
 | |
| 	I2C_WRITE(0x364C, 0xCACB); // P_RD_P1Q1
 | |
| 	I2C_WRITE(0x364E, 0x8C4C); // P_RD_P1Q2
 | |
| 	I2C_WRITE(0x3650, 0x0BEA); // P_RD_P1Q3
 | |
| 	I2C_WRITE(0x3652, 0xDC0F); // P_RD_P1Q4
 | |
| 	I2C_WRITE(0x368A, 0x70B0); // P_RD_P2Q0
 | |
| 	I2C_WRITE(0x368C, 0x200B); // P_RD_P2Q1
 | |
| 	I2C_WRITE(0x368E, 0x30B2); // P_RD_P2Q2
 | |
| 	I2C_WRITE(0x3690, 0xD04F); // P_RD_P2Q3
 | |
| 	I2C_WRITE(0x3692, 0xACF5); // P_RD_P2Q4
 | |
| 	I2C_WRITE(0x36CA, 0xF7C9); // P_RD_P3Q0
 | |
| 	I2C_WRITE(0x36CC, 0x2AED); // P_RD_P3Q1
 | |
| 	I2C_WRITE(0x36CE, 0xA652); // P_RD_P3Q2
 | |
| 	I2C_WRITE(0x36D0, 0x8192); // P_RD_P3Q3
 | |
| 	I2C_WRITE(0x36D2, 0x3A15); // P_RD_P3Q4
 | |
| 	I2C_WRITE(0x370A, 0xDA30); // P_RD_P4Q0
 | |
| 	I2C_WRITE(0x370C, 0x2E2F); // P_RD_P4Q1
 | |
| 	I2C_WRITE(0x370E, 0xBB56); // P_RD_P4Q2
 | |
| 	I2C_WRITE(0x3710, 0x8195); // P_RD_P4Q3
 | |
| 	I2C_WRITE(0x3712, 0x02F9); // P_RD_P4Q4
 | |
| 	I2C_WRITE(0x3600, 0x0230); // P_GR_P0Q0
 | |
| 	I2C_WRITE(0x3602, 0x58AD); // P_GR_P0Q1
 | |
| 	I2C_WRITE(0x3604, 0x18D1); // P_GR_P0Q2
 | |
| 	I2C_WRITE(0x3606, 0x260D); // P_GR_P0Q3
 | |
| 	I2C_WRITE(0x3608, 0xF530); // P_GR_P0Q4
 | |
| 	I2C_WRITE(0x3640, 0x17EB); // P_GR_P1Q0
 | |
| 	I2C_WRITE(0x3642, 0x3CAB); // P_GR_P1Q1
 | |
| 	I2C_WRITE(0x3644, 0x87CE); // P_GR_P1Q2
 | |
| 	I2C_WRITE(0x3646, 0xC02E); // P_GR_P1Q3
 | |
| 	I2C_WRITE(0x3648, 0xF48F); // P_GR_P1Q4
 | |
| 	I2C_WRITE(0x3680, 0x5350); // P_GR_P2Q0
 | |
| 	I2C_WRITE(0x3682, 0x7EAF); // P_GR_P2Q1
 | |
| 	I2C_WRITE(0x3684, 0x4312); // P_GR_P2Q2
 | |
| 	I2C_WRITE(0x3686, 0xC652); // P_GR_P2Q3
 | |
| 	I2C_WRITE(0x3688, 0xBC15); // P_GR_P2Q4
 | |
| 	I2C_WRITE(0x36C0, 0xB8AD); // P_GR_P3Q0
 | |
| 	I2C_WRITE(0x36C2, 0xBDCD); // P_GR_P3Q1
 | |
| 	I2C_WRITE(0x36C4, 0xE4B2); // P_GR_P3Q2
 | |
| 	I2C_WRITE(0x36C6, 0xB50F); // P_GR_P3Q3
 | |
| 	I2C_WRITE(0x36C8, 0x5B95); // P_GR_P3Q4
 | |
| 	I2C_WRITE(0x3700, 0xFC90); // P_GR_P4Q0
 | |
| 	I2C_WRITE(0x3702, 0x8C51); // P_GR_P4Q1
 | |
| 	I2C_WRITE(0x3704, 0xCED6); // P_GR_P4Q2
 | |
| 	I2C_WRITE(0x3706, 0xB594); // P_GR_P4Q3
 | |
| 	I2C_WRITE(0x3708, 0x0A39); // P_GR_P4Q4
 | |
| 	I2C_WRITE(0x3614, 0x0230); // P_BL_P0Q0
 | |
| 	I2C_WRITE(0x3616, 0x160D); // P_BL_P0Q1
 | |
| 	I2C_WRITE(0x3618, 0x08D1); // P_BL_P0Q2
 | |
| 	I2C_WRITE(0x361A, 0x98AB); // P_BL_P0Q3
 | |
| 	I2C_WRITE(0x361C, 0xEA50); // P_BL_P0Q4
 | |
| 	I2C_WRITE(0x3654, 0xB4EA); // P_BL_P1Q0
 | |
| 	I2C_WRITE(0x3656, 0xEA6C); // P_BL_P1Q1
 | |
| 	I2C_WRITE(0x3658, 0xFE08); // P_BL_P1Q2
 | |
| 	I2C_WRITE(0x365A, 0x2C6E); // P_BL_P1Q3
 | |
| 	I2C_WRITE(0x365C, 0xEB0E); // P_BL_P1Q4
 | |
| 	I2C_WRITE(0x3694, 0x6DF0); // P_BL_P2Q0
 | |
| 	I2C_WRITE(0x3696, 0x3ACF); // P_BL_P2Q1
 | |
| 	I2C_WRITE(0x3698, 0x3E0F); // P_BL_P2Q2
 | |
| 	I2C_WRITE(0x369A, 0xB2B1); // P_BL_P2Q3
 | |
| 	I2C_WRITE(0x369C, 0xC374); // P_BL_P2Q4
 | |
| 	I2C_WRITE(0x36D4, 0xF2AA); // P_BL_P3Q0
 | |
| 	I2C_WRITE(0x36D6, 0x8CCC); // P_BL_P3Q1
 | |
| 	I2C_WRITE(0x36D8, 0xDEF2); // P_BL_P3Q2
 | |
| 	I2C_WRITE(0x36DA, 0xFA11); // P_BL_P3Q3
 | |
| 	I2C_WRITE(0x36DC, 0x42F5); // P_BL_P3Q4
 | |
| 	I2C_WRITE(0x3714, 0xF4F1); // P_BL_P4Q0
 | |
| 	I2C_WRITE(0x3716, 0xF6F0); // P_BL_P4Q1
 | |
| 	I2C_WRITE(0x3718, 0x8FD6); // P_BL_P4Q2
 | |
| 	I2C_WRITE(0x371A, 0xEA14); // P_BL_P4Q3
 | |
| 	I2C_WRITE(0x371C, 0x6338); // P_BL_P4Q4
 | |
| 	I2C_WRITE(0x361E, 0x0350); // P_GB_P0Q0
 | |
| 	I2C_WRITE(0x3620, 0x91AE); // P_GB_P0Q1
 | |
| 	I2C_WRITE(0x3622, 0x0571); // P_GB_P0Q2
 | |
| 	I2C_WRITE(0x3624, 0x100D); // P_GB_P0Q3
 | |
| 	I2C_WRITE(0x3626, 0xCA70); // P_GB_P0Q4
 | |
| 	I2C_WRITE(0x365E, 0xE6CB); // P_GB_P1Q0
 | |
| 	I2C_WRITE(0x3660, 0x50ED); // P_GB_P1Q1
 | |
| 	I2C_WRITE(0x3662, 0x3DAE); // P_GB_P1Q2
 | |
| 	I2C_WRITE(0x3664, 0xAA4F); // P_GB_P1Q3
 | |
| 	I2C_WRITE(0x3666, 0xDC50); // P_GB_P1Q4
 | |
| 	I2C_WRITE(0x369E, 0x5470); // P_GB_P2Q0
 | |
| 	I2C_WRITE(0x36A0, 0x1F6E); // P_GB_P2Q1
 | |
| 	I2C_WRITE(0x36A2, 0x6671); // P_GB_P2Q2
 | |
| 	I2C_WRITE(0x36A4, 0xC010); // P_GB_P2Q3
 | |
| 	I2C_WRITE(0x36A6, 0x8DF5); // P_GB_P2Q4
 | |
| 	I2C_WRITE(0x36DE, 0x0B0C); // P_GB_P3Q0
 | |
| 	I2C_WRITE(0x36E0, 0x84CE); // P_GB_P3Q1
 | |
| 	I2C_WRITE(0x36E2, 0x8493); // P_GB_P3Q2
 | |
| 	I2C_WRITE(0x36E4, 0xA610); // P_GB_P3Q3
 | |
| 	I2C_WRITE(0x36E6, 0x50B5); // P_GB_P3Q4
 | |
| 	I2C_WRITE(0x371E, 0x9651); // P_GB_P4Q0
 | |
| 	I2C_WRITE(0x3720, 0x1EAB); // P_GB_P4Q1
 | |
| 	I2C_WRITE(0x3722, 0xAF76); // P_GB_P4Q2
 | |
| 	I2C_WRITE(0x3724, 0xE4F4); // P_GB_P4Q3
 | |
| 	I2C_WRITE(0x3726, 0x79F8); // P_GB_P4Q4
 | |
| 	I2C_WRITE(0x3782, 0x0410); // Original LC 2 // POLY_ORIGIN_C
 | |
| 	I2C_WRITE(0x3784, 0x0320); // POLY_ORIGIN_R
 | |
| 	I2C_WRITE(0x3780, 0x8000); // POLY_SC_ENABLE
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_set_pclk(int rt, int div_adj)
 | |
| {
 | |
| 	int rc;
 | |
| 	if ((rc = mt9t013_i2c_power_down()) < 0) return rc; 
 | |
| 	I2C_WRITE(REG_VT_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_pix_clk_div);
 | |
| 	I2C_WRITE(REG_VT_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_sys_clk_div);
 | |
| 	I2C_WRITE(REG_PRE_PLL_CLK_DIV, mt9t013_reg_pattern.reg[rt].pre_pll_clk_div * div_adj);
 | |
| 	I2C_WRITE(REG_PLL_MULTIPLIER, mt9t013_reg_pattern.reg[rt].pll_multiplier);
 | |
| 	I2C_WRITE(REG_OP_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_pix_clk_div);
 | |
| 	I2C_WRITE(REG_OP_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_sys_clk_div);
 | |
| 	if ((rc = mt9t013_i2c_power_up()) < 0) return rc; 
 | |
| 	pclk_set = 1;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_sensor_setting(unsigned long arg) 
 | |
| {
 | |
| 	uint32_t update = arg & 1;
 | |
| 	uint32_t rt = (arg & 2) >> 1;
 | |
| 
 | |
| 	if (rt > 1 || update > 1) {
 | |
| 		printk(KERN_ERR "mt9t013: invalid values %d of rt or %d of update\n",
 | |
| 			rt, update);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	switch (update) {
 | |
| 	case CAMSENSOR_REG_UPDATE_PERIODIC: {
 | |
| 		uint16_t pclk_div_adj = arg >> 16;
 | |
| 
 | |
| 		printk(KERN_INFO "CAMSENSOR_REG_UPDATE_PERIODIC (rt %d)\n", rt);
 | |
| 		
 | |
| 		if (!pclk_div_adj || pclk_div_adj > 2) {
 | |
| 			printk(KERN_ERR "mt9t013: invalid value %d of pclk_div_adj\n",
 | |
| 					pclk_div_adj); 
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		
 | |
| 		if (mt9t013_set_pclk(rt, pclk_div_adj) < 0)
 | |
| 			return -EIO;
 | |
| 
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
 | |
| 		I2C_WRITE(REG_ROW_SPEED, mt9t013_reg_pattern.reg[rt].row_speed);
 | |
| 		I2C_WRITE(REG_X_ADDR_START, mt9t013_reg_pattern.reg[rt].x_addr_start);
 | |
| 		I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end);
 | |
| 		I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start);
 | |
| 		I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end);
 | |
| 
 | |
| 		if (machine_is_sapphire()) {
 | |
| 			if (rt == 0) {
 | |
| 				I2C_WRITE(REG_READ_MODE, 0x046F);
 | |
| 			} else {
 | |
| 				I2C_WRITE(REG_READ_MODE, 0x0027);
 | |
| 			}
 | |
| 		} else {
 | |
| 			I2C_WRITE(REG_READ_MODE,
 | |
| 				mt9t013_reg_pattern.reg[rt].read_mode);
 | |
| 		}
 | |
| 
 | |
| 		I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m);
 | |
| 		I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size);
 | |
| 		I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size);
 | |
| 		I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck);
 | |
| 		I2C_WRITE(REG_FRAME_LENGTH_LINES, (uint16_t) (mt9t013_reg_pattern.reg[rt].frame_length_lines * fps_divider));
 | |
| 		I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time);
 | |
| 		I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time);
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
 | |
| 	}
 | |
| 		break;
 | |
| 		
 | |
| 	case CAMSENSOR_REG_INIT:
 | |
| 		printk(KERN_INFO "CAMSENSOR_REG_INIT (rt %d)\n", rt);
 | |
| 
 | |
| 		if (mt9t013_set_pclk(rt, 1) < 0) return -EIO;
 | |
| 
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
 | |
| 		
 | |
| 		/* additional power saving mode ok around 38.2MHz */
 | |
| 		I2C_WRITE(0x3084, 0x2409);
 | |
| 		I2C_WRITE(0x3092, 0x0A49);
 | |
| 		I2C_WRITE(0x3094, 0x4949);
 | |
| 		I2C_WRITE(0x3096, 0x4949);
 | |
| 
 | |
| 		/* set preview or snapshot mode */
 | |
| 		I2C_WRITE(REG_ROW_SPEED,
 | |
| 			mt9t013_reg_pattern.reg[rt].row_speed);
 | |
| 		I2C_WRITE(REG_X_ADDR_START,
 | |
| 			mt9t013_reg_pattern.reg[rt].x_addr_start);
 | |
| 		I2C_WRITE(REG_X_ADDR_END,
 | |
| 			mt9t013_reg_pattern.reg[rt].x_addr_end);
 | |
| 		I2C_WRITE(REG_Y_ADDR_START,
 | |
| 			mt9t013_reg_pattern.reg[rt].y_addr_start);
 | |
| 		I2C_WRITE(REG_Y_ADDR_END,
 | |
| 			mt9t013_reg_pattern.reg[rt].y_addr_end);
 | |
| 
 | |
| 		if (machine_is_sapphire()) {
 | |
| 			if (rt == 0) {
 | |
| 				I2C_WRITE(REG_READ_MODE, 0x046F);
 | |
| 			} else {
 | |
| 				I2C_WRITE(REG_READ_MODE, 0x0027);
 | |
| 			}
 | |
| 		} else {
 | |
| 			I2C_WRITE(REG_READ_MODE,
 | |
| 				mt9t013_reg_pattern.reg[rt].read_mode);
 | |
| 		}
 | |
| 
 | |
| 		I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m);
 | |
| 		I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size);
 | |
| 		I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size);
 | |
| 		I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck);
 | |
| 		I2C_WRITE(REG_FRAME_LENGTH_LINES, mt9t013_reg_pattern.reg[rt].frame_length_lines);
 | |
| 		I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time);
 | |
| 		I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time);
 | |
| 
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
 | |
| 
 | |
| 		/* load lens shading */
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
 | |
| 		if(mt9t013_mu3m0vc_set_lc() < 0) return -EIO;
 | |
| 		I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
 | |
| 		break;
 | |
| 		
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line,
 | |
| 					uint16_t gain)
 | |
| {
 | |
| 	static const uint16_t max_legal_gain  = 0x01FF;
 | |
| 	
 | |
| 	if (gain > max_legal_gain) gain = max_legal_gain;
 | |
| 
 | |
| 	gain |= 0x200; /* set digital gain */
 | |
| 
 | |
| 	/*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);*/
 | |
| 	I2C_WRITE(REG_GLOBAL_GAIN, gain);
 | |
| 	I2C_WRITE(REG_COARSE_INTEGRATION_TIME, line);
 | |
| 	/*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);*/
 | |
| 	if (mode == 1) {
 | |
| 		/* RESET REGISTER RESTART */
 | |
| 		I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc|0x0002);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #define I2C_AF_WRITE(command, data) if (mt9t013_i2c_lens_write(AF_I2C_ID >> 1, command, data) < 0) return -EIO;
 | |
| 
 | |
| static int mt9t013_i2c_move_focus(uint16_t position)
 | |
| {
 | |
| 	uint8_t code_val_msb = (position >> 2) | ((position << 4) >> 6);
 | |
| 	uint8_t code_val_lsb = (position & 0x03) << 6;
 | |
| 
 | |
| 	I2C_AF_WRITE(code_val_msb, code_val_lsb);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_set_default_focus(uint8_t step)
 | |
| {
 | |
| 	I2C_AF_WRITE(0x01, step);
 | |
|     	return 0; 
 | |
| }
 | |
| 
 | |
| static int powered;
 | |
| 
 | |
| static int mt9t013_i2c_power_up(void)
 | |
| {
 | |
| 	printk(KERN_INFO "mt9t013: power up\n");
 | |
| 	if (powered) {
 | |
| 		printk(KERN_INFO "mt9t013: already powered up\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWON);
 | |
| 	mdelay(5);
 | |
| 	powered = 1;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int mt9t013_i2c_power_down(void)
 | |
| {
 | |
| 	int i = 0, try_more = 100;
 | |
| 
 | |
| 	printk(KERN_INFO "mt9t013: power down\n");
 | |
| 	if (!powered) {
 | |
| 		printk(KERN_INFO "mt9t013: already powered down\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF); */
 | |
| 	/* Modified by Horng for more tries while I2C write fail                */
 | |
| 	/* -------------------------------------------------------------------- */
 | |
| 	while(mt9t013_i2c_write(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF) < 0)
 | |
| 	{
 | |
| 		if (i >= try_more)
 | |
| 			return -EIO;
 | |
| 		else {
 | |
| 			i++;
 | |
| 			printk(KERN_INFO "mt9p012: in mt9p012_i2c_power_down() call mt9p012_i2c_write() failed !!!  (try %d times)\n", i);
 | |
| 			mdelay(i+5);
 | |
| 		}
 | |
| 	}
 | |
| 	/* -------------------------------------------------------------------- */
 | |
| 	mdelay(5);
 | |
| 	powered = pclk_set = 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #undef I2C_WRITE
 | |
| #undef I2C_AF_WRITE
 | |
| 
 | |
| static int mt9t013_init_client(struct i2c_client *client)
 | |
| {
 | |
| 	/* Initialize the MT9T013 Chip */
 | |
| 	init_waitqueue_head(&g_data_ready_wait_queue);
 | |
| 	return 0;
 | |
| }   
 | |
| 
 | |
| static struct file_operations mt9t013_fops = {
 | |
|         .owner 	= THIS_MODULE,
 | |
|         .open 	= mt9t013_open,
 | |
|         .release = mt9t013_release,
 | |
|         .unlocked_ioctl = mt9t013_ioctl,
 | |
| };
 | |
| 
 | |
| static struct miscdevice mt9t013_device = {
 | |
|         .minor 	= MISC_DYNAMIC_MINOR,
 | |
|         .name 	= "mt9t013",
 | |
|         .fops 	= &mt9t013_fops,
 | |
| };
 | |
| 
 | |
| static const char *MT9T013Vendor = "micron";
 | |
| static const char *MT9T013NAME = "mt9t013";
 | |
| static const char *MT9T013Size = "3M";
 | |
| 
 | |
| 
 | |
| 
 | |
| 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", MT9T013Vendor, MT9T013NAME, MT9T013Size);
 | |
| 	ret = strlen(buf) + 1;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL);
 | |
| 
 | |
| 
 | |
| static struct kobject *android_mt9t013 = NULL;
 | |
| 
 | |
| static int mt9t013_sysfs_init(void)
 | |
| {
 | |
| 	int ret ;
 | |
| 	printk(KERN_INFO "mt9t013:kobject creat and add\n");
 | |
| 	android_mt9t013 = kobject_create_and_add("android_camera", NULL);
 | |
| 	if (android_mt9t013 == NULL) {
 | |
| 		printk(KERN_INFO "mt9t013_sysfs_init: subsystem_register " \
 | |
| 		"failed\n");
 | |
| 		ret = -ENOMEM;
 | |
| 		return ret ;
 | |
| 	}
 | |
| 	printk(KERN_INFO "mt9t013:sysfs_create_file\n");
 | |
| 	ret = sysfs_create_file(android_mt9t013, &dev_attr_sensor.attr);
 | |
| 	if (ret) {
 | |
| 		printk(KERN_INFO "mt9t013_sysfs_init: sysfs_create_file " \
 | |
| 		"failed\n");
 | |
| 		kobject_del(android_mt9t013);
 | |
| 	}
 | |
| 	return 0 ;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static int mt9t013_probe(
 | |
| 	struct i2c_client *client, const struct i2c_device_id *id)
 | |
| {
 | |
| 	struct mt9t013_data *mt;
 | |
| 	int err = 0;
 | |
| 	printk(KERN_INFO "mt9t013: probe\n");
 | |
| 
 | |
| 	if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 | |
| 		goto exit_check_functionality_failed;		
 | |
| 
 | |
| 	if(!(mt = kzalloc( sizeof(struct mt9t013_data), GFP_KERNEL))) {
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit_alloc_data_failed;
 | |
| 	}
 | |
| 
 | |
| 	i2c_set_clientdata(client, mt);
 | |
| 	mt9t013_init_client(client);
 | |
| 	pclient = client;
 | |
| 	mt9t013_sensor_init();
 | |
| 	mt9t013_sensor_suspend();
 | |
| 	
 | |
| 	/* Register a misc device */
 | |
| 	err = misc_register(&mt9t013_device);
 | |
| 	if(err) {
 | |
| 		printk(KERN_ERR "mt9t013_probe: misc_register failed \n");
 | |
| 		goto exit_misc_device_register_failed;
 | |
| 	}
 | |
| 	init_suspend();
 | |
| 	mt9t013_sysfs_init();
 | |
| 	return 0;
 | |
| 	
 | |
| exit_misc_device_register_failed:
 | |
| exit_alloc_data_failed:
 | |
| exit_check_functionality_failed:
 | |
| 	
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| 	
 | |
| static int mt9t013_remove(struct i2c_client *client)
 | |
| {
 | |
| 	struct mt9t013_data *mt = i2c_get_clientdata(client);
 | |
| 	free_irq(client->irq, mt);
 | |
| 	deinit_suspend();
 | |
| 	pclient = NULL;
 | |
| 	misc_deregister(&mt9t013_device);
 | |
| 	kfree(mt);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct i2c_device_id mt9t013_id[] = {
 | |
| 	{ "mt9t013", 0 },
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| static struct i2c_driver mt9t013_driver = {
 | |
| 	.probe = mt9t013_probe,
 | |
| 	.remove = mt9t013_remove,
 | |
| 	.id_table = mt9t013_id,
 | |
| 	.driver = {		
 | |
| 		.name   = "mt9t013",
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static int mt9t013_plat_probe(struct platform_device *pdev __attribute__((unused)))
 | |
| {
 | |
|        int rc = -EFAULT;
 | |
| 
 | |
|        if(pdev->dev.platform_data)
 | |
|        {
 | |
|                printk(KERN_INFO "pdev->dev.platform_data is not NULL\n");
 | |
|                cam = pdev->dev.platform_data;
 | |
|                rc = i2c_add_driver(&mt9t013_driver);
 | |
|        }
 | |
|        return rc;
 | |
| }
 | |
| 
 | |
| static struct platform_driver mt9t013_plat_driver = {
 | |
|         .probe = mt9t013_plat_probe,
 | |
|         .driver = {
 | |
|                 .name = "camera",
 | |
|                 .owner = THIS_MODULE,
 | |
|         },
 | |
| };
 | |
| 
 | |
| static int __init mt9t013_init(void)
 | |
| {
 | |
|        return platform_driver_register(&mt9t013_plat_driver);
 | |
| }
 | |
| 
 | |
| module_init(mt9t013_init);
 | |
| 
 | |
| MODULE_AUTHOR("Kidd Chen");
 | |
| MODULE_DESCRIPTION("MT9T013 Driver");
 | |
| MODULE_LICENSE("GPL");
 |