diff options
Diffstat (limited to 'drivers/media/video/mxc/capture/mt9v111.c')
-rw-r--r-- | drivers/media/video/mxc/capture/mt9v111.c | 1085 |
1 files changed, 769 insertions, 316 deletions
diff --git a/drivers/media/video/mxc/capture/mt9v111.c b/drivers/media/video/mxc/capture/mt9v111.c index c95a20683924..dfdd455db3dd 100644 --- a/drivers/media/video/mxc/capture/mt9v111.c +++ b/drivers/media/video/mxc/capture/mt9v111.c @@ -18,6 +18,9 @@ * * @ingroup Camera */ + +//#define MT9V111_DEBUG + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -40,7 +43,6 @@ static mt9v111_conf mt9v111_device; /*! * Holds the current frame rate. */ -static int reset_frame_rate = MT9V111_FRAME_RATE; struct sensor { const struct mt9v111_platform_data *platform_data; @@ -49,33 +51,45 @@ struct sensor { struct v4l2_pix_format pix; struct v4l2_captureparm streamcap; bool on; + bool used; /* control settings */ int brightness; - int hue; - int contrast; int saturation; - int red; - int green; - int blue; + int sharpness; + int gain; int ae_mode; -} mt9v111_data; - -extern void gpio_sensor_active(void); -extern void gpio_sensor_inactive(void); +}; static int mt9v111_probe(struct i2c_client *client, const struct i2c_device_id *id); static int mt9v111_remove(struct i2c_client *client); static const struct i2c_device_id mt9v111_id[] = { - {"mt9v111", 0}, + {"mt9v111_1", 2}, + {"mt9v111_2", 3}, {}, }; +struct sensor mt9v111_data[ARRAY_SIZE(mt9v111_id)-1]; + MODULE_DEVICE_TABLE(i2c, mt9v111_id); +static int mt9v111_suspend(struct i2c_client *client, pm_message_t mesg) +{ + pr_debug("In mt9v111_suspend\n"); + + return 0; +} + +static int mt9v111_resume(struct i2c_client *client) +{ + pr_debug("In mt9v111_resume\n"); + + return 0; +} + static struct i2c_driver mt9v111_i2c_driver = { .driver = { .owner = THIS_MODULE, @@ -84,37 +98,52 @@ static struct i2c_driver mt9v111_i2c_driver = { .probe = mt9v111_probe, .remove = mt9v111_remove, .id_table = mt9v111_id, -/* To add power management add .suspend and .resume functions */ + .suspend = mt9v111_suspend, + .resume = mt9v111_resume, }; /* * Function definitions */ -#ifdef MT9V111_DEBUG -static inline int mt9v111_read_reg(u8 reg) +static int mt9v111_id_from_name ( const char * name ) +{ + int id = -1; + + if( name == NULL || ( strlen(name) < strlen("mt9v111_n") ) ) + return -1; + + id = (int)simple_strtol(name+strlen("mt9v111_"),NULL,0) - 1; + if( id >= ARRAY_SIZE(mt9v111_id) ) { + printk("Invalid sensor index %d for %s\n",id,name); + return -1; + } + + return id; +} + +static inline int mt9v111_read_reg(int sensorid , u8 reg) { - int val = i2c_smbus_read_word_data(mt9v111_data.i2c_client, reg); + int val = i2c_smbus_read_word_data(mt9v111_data[sensorid].i2c_client, reg); if (val != -1) val = cpu_to_be16(val); return val; } -#endif /*! * Writes to the register via I2C. */ -static inline int mt9v111_write_reg(u8 reg, u16 val) +static inline int mt9v111_write_reg(int sensorid , u8 reg, u16 val) { - pr_debug("In mt9v111_write_reg (0x%x, 0x%x)\n", reg, val); + pr_debug("[%d] In mt9v111_write_reg (0x%x, 0x%x)\n", sensorid , reg, val); pr_debug(" write reg %x val %x.\n", reg, val); - return i2c_smbus_write_word_data(mt9v111_data.i2c_client, + return i2c_smbus_write_word_data(mt9v111_data[sensorid].i2c_client, reg, cpu_to_be16(val)); } /*! - * Initialize mt9v111_sensor_lib + * Initialize mt9v111_sensor_lib_datasheet * Libarary for Sensor configuration through I2C * * @param coreReg Core Registers @@ -122,7 +151,7 @@ static inline int mt9v111_write_reg(u8 reg, u16 val) * * @return status */ -static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg) +static u8 mt9v111_sensor_lib_datasheet(int sensorid , mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg) { u8 reg; u16 data; @@ -130,200 +159,103 @@ static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg) pr_debug("In mt9v111_sensor_lib\n"); + /* IFP R51(0x33)=5137,R57(0x39)=290,R59(0x3B)=1068,R62(0x3E)=4095,R89(0x59)=504,R90(0x5A)=605,R92(0x5C)=8222,R93(0x5D)=10021,R100(0x64)=4477 */ + /* * setup to IFP registers */ reg = MT9V111I_ADDR_SPACE_SEL; data = ifpReg->addrSpaceSel; - mt9v111_write_reg(reg, data); - - /* Operation Mode Control */ - reg = MT9V111I_MODE_CONTROL; - data = ifpReg->modeControl; - mt9v111_write_reg(reg, data); - - /* Output format */ - reg = MT9V111I_FORMAT_CONTROL; - data = ifpReg->formatControl; /* Set bit 12 */ - mt9v111_write_reg(reg, data); - - /* AE limit 4 */ - reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE; - data = ifpReg->gainLimitAE; - mt9v111_write_reg(reg, data); - - reg = MT9V111I_OUTPUT_FORMAT_CTRL2; - data = ifpReg->outputFormatCtrl2; - mt9v111_write_reg(reg, data); - - reg = MT9V111I_AE_SPEED; - data = ifpReg->AESpeed; - mt9v111_write_reg(reg, data); - - /* output image size */ - reg = MT9V111i_H_PAN; - data = 0x8000 | ifpReg->HPan; - mt9v111_write_reg(reg, data); - - reg = MT9V111i_H_ZOOM; - data = 0x8000 | ifpReg->HZoom; - mt9v111_write_reg(reg, data); - - reg = MT9V111i_H_SIZE; - data = 0x8000 | ifpReg->HSize; - mt9v111_write_reg(reg, data); - - reg = MT9V111i_V_PAN; - data = 0x8000 | ifpReg->VPan; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); - reg = MT9V111i_V_ZOOM; - data = 0x8000 | ifpReg->VZoom; - mt9v111_write_reg(reg, data); + reg = MT9V111I_LIMIT_SHARP_SATU_CTRL; + data = ifpReg->limitSharpSatuCtrl; + mt9v111_write_reg(sensorid,reg, data); - reg = MT9V111i_V_SIZE; - data = 0x8000 | ifpReg->VSize; - mt9v111_write_reg(reg, data); - - reg = MT9V111i_H_PAN; - data = ~0x8000 & ifpReg->HPan; - mt9v111_write_reg(reg, data); -#if 0 reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM; data = ifpReg->upperShutterDelayLi; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111I_IPF_BLACK_LEVEL_SUB; + data = ifpReg->ipfBlackLevelSub; + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111I_GAIN_THRE_CCAM_ADJ; + data = ifpReg->agimnThreCamAdj; + mt9v111_write_reg(sensorid,reg, data); reg = MT9V111I_SHUTTER_60; data = ifpReg->shutter_width_60; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111I_AUTO_EXPOSURE_17; + data = ifpReg->auto_exposure_17; + mt9v111_write_reg(sensorid,reg, data); reg = MT9V111I_SEARCH_FLICK_60; data = ifpReg->search_flicker_60; - mt9v111_write_reg(reg, data); -#endif + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111I_RESERVED93; + data = ifpReg->reserved93; + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111I_RESERVED100; + data = ifpReg->reserved100; + mt9v111_write_reg(sensorid,reg, data); /* * setup to sensor core registers */ reg = MT9V111I_ADDR_SPACE_SEL; data = coreReg->addressSelect; - mt9v111_write_reg(reg, data); - - /* enable changes and put the Sync bit on */ - reg = MT9V111S_OUTPUT_CTRL; - data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); - /* min PIXCLK - Default */ - reg = MT9V111S_PIXEL_CLOCK_SPEED; - data = coreReg->pixelClockSpeed; - mt9v111_write_reg(reg, data); + /* Core R5=46, R7[4]=0 (DEFAULT) ,R33=58369*/ - /* Setup image flipping / Dark rows / row/column skip */ - reg = MT9V111S_READ_MODE; - data = coreReg->readMode; - mt9v111_write_reg(reg, data); - - /* zoom 0 */ - reg = MT9V111S_DIGITAL_ZOOM; - data = coreReg->digitalZoom; - mt9v111_write_reg(reg, data); - - /* min H-blank */ reg = MT9V111S_HOR_BLANKING; data = coreReg->horizontalBlanking; - mt9v111_write_reg(reg, data); - - /* min V-blank */ - reg = MT9V111S_VER_BLANKING; - data = coreReg->verticalBlanking; - mt9v111_write_reg(reg, data); - - reg = MT9V111S_SHUTTER_WIDTH; - data = coreReg->shutterWidth; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); - reg = MT9V111S_SHUTTER_DELAY; - data = ifpReg->upperShutterDelayLi; - mt9v111_write_reg(reg, data); - - /* changes become effective */ - reg = MT9V111S_OUTPUT_CTRL; - data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000; - mt9v111_write_reg(reg, data); + reg = MT9V111S_RESERVED33; + data = coreReg->reserved33; + mt9v111_write_reg(sensorid,reg, data); return error; } -/*! - * MT9V111 frame rate calculate - * - * @param frame_rate int * - * @param mclk int - * @return None - */ -static void mt9v111_rate_cal(int *frame_rate, int mclk) +void mt9v111_config_datasheet(void) { - int num_clock_per_row; - int max_rate = 0; + pr_debug("In mt9v111_config_datasheet\n"); - pr_debug("In mt9v111_rate_cal\n"); + mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA; - num_clock_per_row = (MT9V111_MAX_WIDTH + 114 + MT9V111_HORZBLANK_MIN) - * 2; - max_rate = mclk / (num_clock_per_row * - (MT9V111_MAX_HEIGHT + MT9V111_VERTBLANK_DEFAULT)); + /* MT9V111I_ADDR_SPACE_SEL */ + mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP; - if ((*frame_rate > max_rate) || (*frame_rate == 0)) { - *frame_rate = max_rate; - } + /* Recommended values for 30fps @ 27MHz from datasheet*/ - mt9v111_device.coreReg->verticalBlanking - = mclk / (*frame_rate * num_clock_per_row) - MT9V111_MAX_HEIGHT; + /* Core R5=132, R6=10 , R7[4]=0 (DEFAULT) ,R33=58369*/ - reset_frame_rate = *frame_rate; -} + mt9v111_device.coreReg->horizontalBlanking = 132; + mt9v111_device.coreReg->verticalBlanking = 10; + mt9v111_device.coreReg->reserved33 = 58369; -/*! - * MT9V111 sensor configuration - */ -void mt9v111_config(void) -{ - pr_debug("In mt9v111_config\n"); + /* IFP R51(0x33)=5137,R57(0x39)=290,R59(0x3B)=1068,R62(0x3E)=4095,R89(0x59)=504,R90(0x5A)=605,R92(0x5C)=8222,R93(0x5D)=10021,R100(0x64)=4477 */ - mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA; - mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP; + mt9v111_device.ifpReg->limitSharpSatuCtrl = 5137; + mt9v111_device.ifpReg->upperShutterDelayLi = 290; + mt9v111_device.ifpReg->ipfBlackLevelSub = 1068; + mt9v111_device.ifpReg->agimnThreCamAdj = 4095; - mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT; - mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH; - mt9v111_device.coreReg->zoomColStart = 0; - mt9v111_device.coreReg->zomRowStart = 0; - mt9v111_device.coreReg->digitalZoom = 0x0; - - mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT; - mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN; - mt9v111_device.coreReg->pixelClockSpeed = 0; - mt9v111_device.coreReg->readMode = 0xd0a1; - - mt9v111_device.ifpReg->outputFormatCtrl2 = 0; - mt9v111_device.ifpReg->gainLimitAE = 0x300; - mt9v111_device.ifpReg->AESpeed = 0x80; - - /* here is the default value */ - mt9v111_device.ifpReg->formatControl = 0xc800; - mt9v111_device.ifpReg->modeControl = 0x708e; - mt9v111_device.ifpReg->awbSpeed = 0x4514; - mt9v111_device.coreReg->shutterWidth = 0xf8; - - /* output size */ - mt9v111_device.ifpReg->HPan = 0; - mt9v111_device.ifpReg->HZoom = MT9V111_MAX_WIDTH; - mt9v111_device.ifpReg->HSize = MT9V111_MAX_WIDTH; - mt9v111_device.ifpReg->VPan = 0; - mt9v111_device.ifpReg->VZoom = MT9V111_MAX_HEIGHT; - mt9v111_device.ifpReg->VSize = MT9V111_MAX_HEIGHT; + mt9v111_device.ifpReg->shutter_width_60 = 504; + mt9v111_device.ifpReg->auto_exposure_17 = 605; + mt9v111_device.ifpReg->search_flicker_60 = 8222; + mt9v111_device.ifpReg->reserved93 = 10021; + mt9v111_device.ifpReg->reserved100 = 4477; } + /*! * mt9v111 sensor set saturtionn * @@ -331,7 +263,7 @@ void mt9v111_config(void) * @return Error code of 0. */ -static int mt9v111_set_saturation(int saturation) +static int mt9v111_set_saturation(int sensorid , int saturation) { u8 reg; u16 data; @@ -357,6 +289,9 @@ static int mt9v111_set_saturation(int saturation) case 25: mt9v111_device.ifpReg->awbSpeed = 0x6514; break; + case 0: + mt9v111_device.ifpReg->awbSpeed = 0x7514; + break; default: mt9v111_device.ifpReg->awbSpeed = 0x4514; break; @@ -364,12 +299,348 @@ static int mt9v111_set_saturation(int saturation) reg = MT9V111I_ADDR_SPACE_SEL; data = mt9v111_device.ifpReg->addrSpaceSel; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); /* Operation Mode Control */ reg = MT9V111I_AWB_SPEED; data = mt9v111_device.ifpReg->awbSpeed; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +#if 0 +/*! + * mt9v111 sensor set digital zoom + * + * @param on/off int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalzoom(int sensorid , unsigned int on) +{ + u8 reg; + u16 data; + pr_debug("In mt9v111_set_digitalzoom(%d)\n",on); + + if( on > 1 ) + return -1; + + mt9v111_device.coreReg->digitalZoom = on; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.coreReg->addressSelect; + mt9v111_write_reg(sensorid,reg, data); + + /* Operation Mode Control */ + reg = MT9V111S_DIGITAL_ZOOM; + data = mt9v111_device.coreReg->digitalZoom; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +/*! + * mt9v111 sensor set digital pan + * + * @param pan_level int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalpan (int sensorid , int pan_level) +{ + u8 reg; + u16 data; + pr_debug("In mt9v111_set_digitalpan(%d)\n", + pan_level); + + mt9v111_device.ifpReg->HPan = 8; + if (pan_level & 0xFFFF0000) { + pan_level = (0xFFFFFFFF - pan_level); + pan_level = pan_level / 0x14; + mt9v111_device.ifpReg->HPan = + mt9v111_device.ifpReg->HPan - (pan_level & 0x3FF); + } else { + pan_level = pan_level / 0x14; + mt9v111_device.ifpReg->HPan = + mt9v111_device.ifpReg->HPan + (pan_level - 1); + } + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + /* Operation Mode Control */ + reg = MT9V111i_H_PAN; + data = mt9v111_device.ifpReg->HPan; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +/*! + * mt9v111 sensor set digital tilt + * + * @param tilt_level int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitaltilt (int sensorid , int tilt_level) +{ + u8 reg; + u16 data; + pr_debug("In mt9v111_set_digitaltilt(%d)\n", + tilt_level); + + mt9v111_device.ifpReg->VPan = 8; + if( tilt_level & 0xFFFF0000 ) { + tilt_level = (0xFFFFFFFF - tilt_level); + tilt_level = tilt_level / 0x14; + mt9v111_device.ifpReg->VPan = mt9v111_device.ifpReg->VPan - (tilt_level & 0x3FF); + } + else { + tilt_level = tilt_level / 0x14; + mt9v111_device.ifpReg->VPan = mt9v111_device.ifpReg->VPan + (tilt_level - 1); + } + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + /* Operation Mode Control */ + reg = MT9V111i_V_PAN; + data = mt9v111_device.ifpReg->VPan; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +/*! + * mt9v111 sensor set output resolution + * + * @param resolution res + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_outputresolution(int sensorid , MT9V111_OutputResolution res) +{ + u8 reg; + u16 data; + int zoom = 0; + + pr_debug("In mt9v111_set_outputresolution(%d)\n",res); + + switch (res) { + case MT9V111_OutputResolution_VGA: + /* 640x480 */ + mt9v111_device.ifpReg->HSize = 0x0280; + mt9v111_device.ifpReg->VSize = 0x01E0; + break; + + case MT9V111_OutputResolution_QVGA: + /* 320x240 */ + mt9v111_device.ifpReg->HSize = 0x0140; + mt9v111_device.ifpReg->VSize = 0x00F0; + break; + + case MT9V111_OutputResolution_CIF: + /* 352x288 */ + mt9v111_device.ifpReg->HSize = 0x0160; + mt9v111_device.ifpReg->VSize = 0x0120; + mt9v111_device.ifpReg->HZoom = 0x0160; + mt9v111_device.ifpReg->VZoom = 0x0120; + zoom = 1; + break; + + case MT9V111_OutputResolution_QCIF: + /* 176X220 */ + mt9v111_device.ifpReg->HSize = 0x00B0; + mt9v111_device.ifpReg->VSize = 0x0090; + mt9v111_device.ifpReg->HZoom = 0x00B0; + mt9v111_device.ifpReg->VZoom = 0x0090; + zoom = 1; + break; + + case MT9V111_OutputResolution_QQVGA: + /* 2048*1536 */ + mt9v111_device.ifpReg->HSize = 0x00A0; + mt9v111_device.ifpReg->VSize = 0x0078; + mt9v111_device.ifpReg->HZoom = 0x00A0; + mt9v111_device.ifpReg->VZoom = 0x0078; + zoom = 1; + break; + + case MT9V111_OutputResolution_SXGA: + /* 1280x1024 */ + break; + + default: + break; + } + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111i_V_SIZE; + data = mt9v111_device.ifpReg->VSize; + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111i_H_SIZE; + data = mt9v111_device.ifpReg->HSize; + mt9v111_write_reg(sensorid,reg, data); + + if ( zoom ) { + reg = MT9V111i_V_ZOOM; + data = mt9v111_device.ifpReg->VZoom; + mt9v111_write_reg(sensorid,reg, data); + + reg = MT9V111i_H_ZOOM; + data = mt9v111_device.ifpReg->HZoom; + mt9v111_write_reg(sensorid,reg, data); + } + + return 0; +} + +/*! + * mt9v111 sensor set digital flash + * + * @param flash_level int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalflash (int sensorid , int flash_level) +{ + u8 reg; + u16 data = mt9v111_read_reg(sensorid,MT9V111i_FLASH_CTRL); + pr_debug("In mt9v111_set_digitalflash(%d)\n", + flash_level); + + if(flash_level) { + data &= (0xFF00); + data |= ((flash_level & 0x00FF) | (1<<13)); + } + else { + data &= ~(1<<13); + } + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + /* Operation Mode Control */ + reg = MT9V111i_FLASH_CTRL; + mt9v111_device.ifpReg->flashCtrl = data; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +/*! + * mt9v111 sensor set digital monochrome + * + * @param on int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalmonochrome (int sensorid , int on) +{ + u8 reg; + u16 data = mt9v111_read_reg(sensorid,MT9V111I_FORMAT_CONTROL); + pr_debug("In mt9v111_set_digitalmonochrome(%d)\n", + on); + + /* clear the monochrome bit field */ + data &= ~(1<<5); + + /* enable or disable monochrome mode */ + if( on ) + data |= (0<<5); + else + data |= (1<<5); + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + /* Operation Mode Control */ + reg = MT9V111I_FORMAT_CONTROL; + mt9v111_device.ifpReg->formatControl = data; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} +#endif + +/*! + * mt9v111 sensor set digital sharpness + * + * @param value int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalsharpness (int sensorid , int value) +{ + u8 reg; + u16 data ; + + pr_debug("In mt9v111_set_digitalsharpness(%d)\n",value); + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + data = mt9v111_read_reg(sensorid,MT9V111I_APERTURE_GAIN); + + /* erase current and remove auto reduce sharpness in low light */ + data &= ~(0x000F); + data |= (value & (0x000F)); + if( data > (0x000F) ) + return -1; + + /* Operation Mode Control */ + reg = MT9V111I_APERTURE_GAIN; + mt9v111_device.ifpReg->apertureGain = data; + mt9v111_write_reg(sensorid,reg, data); + + return 0; +} + +/*! + * mt9v111 sensor set digital brightness + * + * @param value int + + * @return 0 on success, -1 on error. + */ +static int mt9v111_set_digitalbrightness (int sensorid , int value) +{ + u8 reg; + u16 data; + u32 max_brightness, min_brightness; + + data = mt9v111_read_reg(sensorid,MT9V111I_CLIP_LIMIT_OUTPUT_LUMI); + max_brightness = data >> 8; + min_brightness = (u8)data; + + if( value > max_brightness ) + value = max_brightness; + else if( value < min_brightness ) + value = min_brightness; + + reg = MT9V111I_ADDR_SPACE_SEL; + data = mt9v111_device.ifpReg->addrSpaceSel; + mt9v111_write_reg(sensorid,reg, data); + + data = mt9v111_read_reg(sensorid,MT9V111I_AE_PRECISION_TARGET); + data &= 0xFF00; /* Clear target luminance */ + data |= ((u8)value ); + + /* Operation Mode Control */ + reg = MT9V111I_AE_PRECISION_TARGET; + mt9v111_device.ifpReg->AEPrecisionTarget = data; + mt9v111_write_reg(sensorid,reg, data); return 0; } @@ -380,7 +651,7 @@ static int mt9v111_set_saturation(int saturation) * @param ae_mode int * @return Error code of 0 (no Error) */ -static int mt9v111_set_ae_mode(int ae_mode) +static int mt9v111_set_ae_mode(int sensorid , int ae_mode) { u8 reg; u16 data; @@ -408,19 +679,20 @@ static int mt9v111_set_ae_mode(int ae_mode) /* V4L2_EXPOSURE_MANUAL = 1 needs register setting of 0x308E */ mt9v111_device.ifpReg->modeControl &= 0x3fff; mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 14; - mt9v111_data.ae_mode = ae_mode; + mt9v111_data[sensorid].ae_mode = ae_mode; reg = MT9V111I_ADDR_SPACE_SEL; data = mt9v111_device.ifpReg->addrSpaceSel; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); reg = MT9V111I_MODE_CONTROL; data = mt9v111_device.ifpReg->modeControl; - mt9v111_write_reg(reg, data); + mt9v111_write_reg(sensorid,reg, data); return 0; } +#if 0 /*! * mt9v111 sensor get AE measurement window mode configuration * @@ -435,6 +707,7 @@ static void mt9v111_get_ae_mode(int *ae_mode) *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2; } } +#endif #ifdef MT9V111_DEBUG /*! @@ -442,33 +715,33 @@ static void mt9v111_get_ae_mode(int *ae_mode) * * @return none */ -static void mt9v111_test_pattern(bool flag) +static void mt9v111_test_pattern(int sensorid , bool flag) { u16 data; /* switch to sensor registers */ - mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA); + mt9v111_write_reg(sensorid,MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA); if (flag == true) { testpattern = MT9V111S_OUTCTRL_TEST_MODE; - data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF; - mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data); + data = mt9v111_read_reg(sensorid,MT9V111S_ROW_NOISE_CTRL) & 0xBF; + mt9v111_write_reg(sensorid,MT9V111S_ROW_NOISE_CTRL, data); - mt9v111_write_reg(MT9V111S_TEST_DATA, 0); + mt9v111_write_reg(sensorid,MT9V111S_TEST_DATA, 0); /* changes take effect */ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000; - mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data); + mt9v111_write_reg(sensorid,MT9V111S_OUTPUT_CTRL, data); } else { testpattern = 0; - data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40; + data = mt9v111_read_reg(sensorid,MT9V111S_ROW_NOISE_CTRL) | 0x40; mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data); /* changes take effect */ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000; - mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data); + mt9v111_write_reg(sensorid,MT9V111S_OUTPUT_CTRL, data); } } #endif @@ -507,6 +780,7 @@ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) p->u.bt656.clock_curr = MT9V111_MCLK; p->if_type = V4L2_IF_TYPE_BT656; p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.bt_sync_correct = 1; // translates to CSI ext vsync p->u.bt656.clock_min = MT9V111_CLK_MIN; p->u.bt656.clock_max = MT9V111_CLK_MAX; @@ -534,10 +808,12 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) sensor->on = on; - if (on) - gpio_sensor_active(); - else - gpio_sensor_inactive(); + if(on) { + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */, true, true); + } + else { + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */, false, false); + } return 0; } @@ -554,19 +830,23 @@ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) int ret = 0; struct v4l2_captureparm *cparm = &a->parm.capture; /* s->priv points to mt9v111_data */ + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); pr_debug("In mt9v111:ioctl_g_parm\n"); + if( sensorid < 0 ) + return ret; + switch (a->type) { /* This is the only case currently handled. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cparm->capability = mt9v111_data.streamcap.capability; + cparm->capability = mt9v111_data[sensorid].streamcap.capability; cparm->timeperframe = - mt9v111_data.streamcap.timeperframe; - cparm->capturemode = mt9v111_data.streamcap.capturemode; + mt9v111_data[sensorid].streamcap.timeperframe; + cparm->capturemode = mt9v111_data[sensorid].streamcap.capturemode; ret = 0; break; @@ -605,9 +885,13 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) int ret = 0; struct v4l2_captureparm *cparm = &a->parm.capture; /* s->priv points to mt9v111_data */ + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); pr_debug("In mt9v111:ioctl_s_parm\n"); + if( sensorid < 0 ) + return ret; + switch (a->type) { /* This is the only case currently handled. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -617,13 +901,13 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) * Changing the frame rate is not allowed on this *camera. */ if (cparm->timeperframe.denominator != - mt9v111_data.streamcap.timeperframe.denominator) { + mt9v111_data[sensorid].streamcap.timeperframe.denominator) { pr_err("ERROR: mt9v111: ioctl_s_parm: " \ "This camera does not allow frame rate " "changes.\n"); ret = -EINVAL; } else { - mt9v111_data.streamcap.timeperframe = + mt9v111_data[sensorid].streamcap.timeperframe = cparm->timeperframe; /* Call any camera functions to match settings. */ } @@ -635,7 +919,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) "unsupported capture mode\n"); ret = -EINVAL; } else { - mt9v111_data.streamcap.capturemode = + mt9v111_data[sensorid].streamcap.capturemode = cparm->capturemode; /* Call any camera functions to match settings. */ /* Right now this camera only supports 1 mode. */ @@ -685,6 +969,7 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return 0; } +#if 0 /*! * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl * @s: pointer to standard V4L2 device structure @@ -700,6 +985,7 @@ static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) return 0; } +#endif /*! * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl @@ -712,66 +998,29 @@ static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) */ static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + pr_debug("In mt9v111:ioctl_g_ctrl\n"); + if( sensorid < 0 ) + return 0; + switch (vc->id) { case V4L2_CID_BRIGHTNESS: pr_debug(" V4L2_CID_BRIGHTNESS\n"); - vc->value = mt9v111_data.brightness; - break; - case V4L2_CID_CONTRAST: - pr_debug(" V4L2_CID_CONTRAST\n"); - vc->value = mt9v111_data.contrast; + vc->value = mt9v111_data[sensorid].brightness; break; case V4L2_CID_SATURATION: pr_debug(" V4L2_CID_SATURATION\n"); - vc->value = mt9v111_data.saturation; - break; - case V4L2_CID_HUE: - pr_debug(" V4L2_CID_HUE\n"); - vc->value = mt9v111_data.hue; - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - pr_debug( - " V4L2_CID_AUTO_WHITE_BALANCE\n"); - vc->value = 0; - break; - case V4L2_CID_DO_WHITE_BALANCE: - pr_debug( - " V4L2_CID_DO_WHITE_BALANCE\n"); - vc->value = 0; - break; - case V4L2_CID_RED_BALANCE: - pr_debug(" V4L2_CID_RED_BALANCE\n"); - vc->value = mt9v111_data.red; - break; - case V4L2_CID_BLUE_BALANCE: - pr_debug(" V4L2_CID_BLUE_BALANCE\n"); - vc->value = mt9v111_data.blue; - break; - case V4L2_CID_GAMMA: - pr_debug(" V4L2_CID_GAMMA\n"); - vc->value = 0; + vc->value = mt9v111_data[sensorid].saturation; break; case V4L2_CID_EXPOSURE: pr_debug(" V4L2_CID_EXPOSURE\n"); - vc->value = mt9v111_data.ae_mode; - break; - case V4L2_CID_AUTOGAIN: - pr_debug(" V4L2_CID_AUTOGAIN\n"); - vc->value = 0; + vc->value = mt9v111_data[sensorid].ae_mode; break; case V4L2_CID_GAIN: pr_debug(" V4L2_CID_GAIN\n"); - vc->value = 0; - break; - case V4L2_CID_HFLIP: - pr_debug(" V4L2_CID_HFLIP\n"); - vc->value = 0; - break; - case V4L2_CID_VFLIP: - pr_debug(" V4L2_CID_VFLIP\n"); - vc->value = 0; + vc->value = mt9v111_data[sensorid].gain; break; default: pr_debug(" Default case\n"); @@ -794,56 +1043,34 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { int retval = 0; + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); pr_debug("In mt9v111:ioctl_s_ctrl %d\n", vc->id); + if( sensorid < 0 ) + return retval; + switch (vc->id) { case V4L2_CID_BRIGHTNESS: pr_debug(" V4L2_CID_BRIGHTNESS\n"); - break; - case V4L2_CID_CONTRAST: - pr_debug(" V4L2_CID_CONTRAST\n"); + mt9v111_set_digitalbrightness(sensorid,vc->value); + mt9v111_data[sensorid].brightness = vc->value; break; case V4L2_CID_SATURATION: pr_debug(" V4L2_CID_SATURATION\n"); - retval = mt9v111_set_saturation(vc->value); - break; - case V4L2_CID_HUE: - pr_debug(" V4L2_CID_HUE\n"); - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - pr_debug( - " V4L2_CID_AUTO_WHITE_BALANCE\n"); - break; - case V4L2_CID_DO_WHITE_BALANCE: - pr_debug( - " V4L2_CID_DO_WHITE_BALANCE\n"); - break; - case V4L2_CID_RED_BALANCE: - pr_debug(" V4L2_CID_RED_BALANCE\n"); - break; - case V4L2_CID_BLUE_BALANCE: - pr_debug(" V4L2_CID_BLUE_BALANCE\n"); - break; - case V4L2_CID_GAMMA: - pr_debug(" V4L2_CID_GAMMA\n"); + retval = mt9v111_set_saturation(sensorid,vc->value); + mt9v111_data[sensorid].saturation = vc->value; break; case V4L2_CID_EXPOSURE: pr_debug(" V4L2_CID_EXPOSURE\n"); - retval = mt9v111_set_ae_mode(vc->value); - break; - case V4L2_CID_AUTOGAIN: - pr_debug(" V4L2_CID_AUTOGAIN\n"); + retval = mt9v111_set_ae_mode(sensorid,vc->value); + mt9v111_data[sensorid].ae_mode = vc->value; break; case V4L2_CID_GAIN: pr_debug(" V4L2_CID_GAIN\n"); - break; - case V4L2_CID_HFLIP: - pr_debug(" V4L2_CID_HFLIP\n"); - break; - case V4L2_CID_VFLIP: - pr_debug(" V4L2_CID_VFLIP\n"); + mt9v111_set_digitalsharpness(sensorid,vc->value); + mt9v111_data[sensorid].gain = vc->value; break; default: pr_debug(" Default case\n"); @@ -854,13 +1081,41 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) return retval; } +static void mt9v111_ifp_reset ( int sensorid ) +{ + mt9v111_write_reg(sensorid,MT9V111S_ADDR_SPACE_SEL, 0x0001); + mt9v111_write_reg(sensorid,MT9V111I_SOFT_RESET, 0x0001); + msleep(100); + mt9v111_write_reg(sensorid,MT9V111I_SOFT_RESET, 0x0000); + msleep(100); +} + +static void mt9v111_sensor_reset ( int sensorid ) +{ + mt9v111_write_reg(sensorid,MT9V111S_ADDR_SPACE_SEL, 0x0004); + mt9v111_write_reg(sensorid,MT9V111S_RESET, 0x0001); + msleep(100); + mt9v111_write_reg(sensorid,MT9V111S_RESET, 0x0000); + msleep(100); +} + /*! * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT * @s: pointer to standard V4L2 device structure */ static int ioctl_init(struct v4l2_int_device *s) { - pr_debug("In mt9v111:ioctl_init\n"); + int sensorid = 0; + + sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + if( sensorid < 0 ) + return 0; + + pr_debug("In mt9v111:ioctl_init for sensor %d\n",sensorid); + + mt9v111_sensor_reset(sensorid); + mt9v111_ifp_reset(sensorid); + mt9v111_sensor_lib_datasheet(sensorid,mt9v111_device.coreReg, mt9v111_device.ifpReg); return 0; } @@ -873,19 +1128,146 @@ static int ioctl_init(struct v4l2_int_device *s) */ static int ioctl_dev_init(struct v4l2_int_device *s) { + int sensorid = 0; uint32_t clock_rate = MT9V111_MCLK; + sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + if( sensorid < 0 ) + return 0; + pr_debug("In mt9v111:ioctl_dev_init\n"); - gpio_sensor_active(); + set_mclk_rate(&clock_rate, 0); // Both sensors use mclk0 on Digi ccwmx51 + + mt9v111_sensor_reset(sensorid); + mt9v111_ifp_reset(sensorid); + mt9v111_sensor_lib_datasheet(sensorid,mt9v111_device.coreReg, mt9v111_device.ifpReg); - set_mclk_rate(&clock_rate); - mt9v111_rate_cal(&reset_frame_rate, clock_rate); - mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg); + return 0; +} + +/* list of image formats supported by sensor */ +static const struct v4l2_fmtdesc mt9v111_formats[] = { + { + .description = "RGB565", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + .description = "YUV422 UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +#define MT9V111_NUM_CAPTURE_FORMATS ARRAY_SIZE(mt9v111_formats) + +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= MT9V111_NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + fmt->flags = mt9v111_formats[index].flags; + strlcpy(fmt->description, mt9v111_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = mt9v111_formats[index].pixelformat; return 0; } +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + unsigned short reg; + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + struct sensor *sensor = s->priv; + /* s->priv points to mt9v111_data */ + + if( sensorid < 0 ) + return -ENODEV; + + /* Select IFP registers */ + mt9v111_write_reg (sensorid,MT9V111S_ADDR_SPACE_SEL, 0x0001); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + /*MT9V111I_OUTPUT_FORMAT_CTRL2*/ + reg = mt9v111_read_reg (sensorid,MT9V111I_OUTPUT_FORMAT_CTRL2); + reg &= ~(0x3 << 6); + mt9v111_write_reg (sensorid,MT9V111I_OUTPUT_FORMAT_CTRL2, reg); + + /* MT9V111I_FORMAT_CONTROL */ + reg = mt9v111_read_reg(sensorid,MT9V111I_FORMAT_CONTROL); + reg |= 1 << 12; + mt9v111_write_reg(sensorid,MT9V111I_FORMAT_CONTROL, reg); + break; + + case V4L2_PIX_FMT_YUV444: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUYV: + /* MT9V111I_FORMAT_CONTROL */ + reg = mt9v111_read_reg(sensorid,MT9V111I_FORMAT_CONTROL); + reg &= ~(1 << 12); + mt9v111_write_reg(sensorid,MT9V111I_FORMAT_CONTROL, reg); + break; + + default: + return -EINVAL; + } + + sensor->pix.width = f->fmt.pix.width; + sensor->pix.height = f->fmt.pix.height; + sensor->pix.sizeimage = f->fmt.pix.sizeimage; + sensor->pix.pixelformat = f->fmt.pix.pixelformat; + return 0; +} + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + int i; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + for( i=0 ; i < MT9V111_NUM_CAPTURE_FORMATS ; i++) { + if( f->fmt.pix.pixelformat == mt9v111_formats[i].pixelformat ) + return 0; + } + + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ioctl_get_register(struct v4l2_int_device *s,struct v4l2_dbg_register * dreg) +{ + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */ , true, true); + dreg->val = mt9v111_read_reg (sensorid,dreg->reg); + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */ , false, false); + return 0; +} + +static int ioctl_set_register(struct v4l2_int_device *s,struct v4l2_dbg_register * dreg) +{ + int sensorid = mt9v111_id_from_name(((struct sensor *)s->priv)->v4l2_int_device->name); + + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */ , true, true); + mt9v111_write_reg (sensorid,dreg->reg, dreg->val); + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, 0 /* cam->csi */ , false, false); + return 0; +} +#endif + /*! * This structure defines all the ioctls for this module and links them to the * enumeration. @@ -910,16 +1292,23 @@ static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = { /*! * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. */ -/* {vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, */ + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, + +#ifdef CONFIG_VIDEO_ADV_DEBUG + {vidioc_int_g_register_num, + (v4l2_int_ioctl_func *) ioctl_get_register}, + {vidioc_int_s_register_num, + (v4l2_int_ioctl_func *) ioctl_set_register}, +#endif /*! * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. * This ioctl is used to negotiate the image capture size and * pixel format without actually making it take effect. */ -/* {vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, */ + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, @@ -928,7 +1317,7 @@ static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = { * format, returns error code if format not supported or HW can't be * correctly configured. */ -/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */ + {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, @@ -937,20 +1326,56 @@ static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = { {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, }; -static struct v4l2_int_slave mt9v111_slave = { - .ioctls = mt9v111_ioctl_desc, - .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc), +static struct v4l2_int_slave mt9v111_slave[] = { + { + .ioctls = mt9v111_ioctl_desc, + .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc), + .attach_to = "mxc_v4l2_cap_1", + }, + { + .ioctls = mt9v111_ioctl_desc, + .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc), + .attach_to = "mxc_v4l2_cap_2", + }, }; -static struct v4l2_int_device mt9v111_int_device = { - .module = THIS_MODULE, - .name = "mt9v111", - .type = v4l2_int_type_slave, - .u = { - .slave = &mt9v111_slave, +static struct v4l2_int_device mt9v111_int_device [] = { + { + .module = THIS_MODULE, + .type = v4l2_int_type_slave, + .u = { + .slave = &mt9v111_slave[0], + }, + }, + { + .module = THIS_MODULE, + .type = v4l2_int_type_slave, + .u = { + .slave = &mt9v111_slave[1], + }, }, }; +static int mt9v111_read_id( int sensoridx ) +{ + int sensorid = 0; + int ret = 0; + + mt9v111_write_reg (sensoridx,MT9V111S_ADDR_SPACE_SEL, 0x0004); + + sensorid = mt9v111_read_reg (sensoridx,MT9V111S_CHIP_VERSION); + if( sensorid == 0x823a ) + { + printk(KERN_INFO" MT9V111 ID %x\n",sensorid); + } + else + { + printk(KERN_ERR" MT9V111 Could not detect sensor (read %x)\n",sensorid); + ret = -ENODEV; + } + return ret; +} + /*! * mt9v111 I2C probe function * Function set in i2c_driver struct. @@ -962,32 +1387,56 @@ static int mt9v111_probe(struct i2c_client *client, const struct i2c_device_id *id) { int retval; + int sensorid; pr_debug("In mt9v111_probe device id is %s\n", id->name); + sensorid = mt9v111_id_from_name(id->name); + + if( sensorid < 0 ) + return -ENODEV; + /* Set initial values for the sensor struct. */ - memset(&mt9v111_data, 0, sizeof(mt9v111_data)); - mt9v111_data.i2c_client = client; + memset(&mt9v111_data[sensorid], 0, sizeof(struct sensor)); + mt9v111_data[sensorid].i2c_client = client; pr_debug(" client name is %s\n", client->name); - mt9v111_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; - mt9v111_data.pix.width = MT9V111_MAX_WIDTH; - mt9v111_data.pix.height = MT9V111_MAX_HEIGHT; - mt9v111_data.streamcap.capability = 0; /* No higher resolution or frame - * frame rate changes supported. - */ - mt9v111_data.streamcap.timeperframe.denominator = MT9V111_FRAME_RATE; - mt9v111_data.streamcap.timeperframe.numerator = 1; + mt9v111_data[sensorid].pix.pixelformat = V4L2_PIX_FMT_UYVY; + mt9v111_data[sensorid].pix.width = MT9V111_MAX_WIDTH; + mt9v111_data[sensorid].pix.height = MT9V111_MAX_HEIGHT; + mt9v111_data[sensorid].streamcap.capability = 0; /* No higher resolution or frame + * frame rate changes supported.*/ + mt9v111_data[sensorid].streamcap.timeperframe.denominator = MT9V111_FRAME_RATE; + mt9v111_data[sensorid].streamcap.timeperframe.numerator = 1; + + strcpy(mt9v111_int_device[sensorid].name,id->name); + pr_debug(" video device name is %s\n", mt9v111_data[sensorid].v4l2_int_device->name); + mt9v111_data[sensorid].v4l2_int_device = &mt9v111_int_device[sensorid]; + mt9v111_int_device[sensorid].priv = &mt9v111_data[sensorid]; + + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, sensorid , true, true); + + if( mt9v111_read_id(sensorid) != 0) { + printk(KERN_ERR"mt9v111_probe: No sensor found\n"); + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, sensorid , false, false); + return -ENXIO; + } - mt9v111_int_device.priv = &mt9v111_data; +#ifdef MT9V111_DEBUG + mt9v111_test_pattern(1); +#endif + + ipu_csi_enable_mclk_if(CSI_MCLK_I2C, sensorid , false, false); pr_debug(" type is %d (expect %d)\n", - mt9v111_int_device.type, v4l2_int_type_slave); + mt9v111_int_device[sensorid].type, v4l2_int_type_slave); pr_debug(" num ioctls is %d\n", - mt9v111_int_device.u.slave->num_ioctls); + mt9v111_int_device[sensorid].u.slave->num_ioctls); /* This function attaches this structure to the /dev/video0 device. * The pointer in priv points to the mt9v111_data structure here.*/ - retval = v4l2_int_device_register(&mt9v111_int_device); + retval = v4l2_int_device_register(&mt9v111_int_device[sensorid]); + if( retval == 0 ) + mt9v111_data[sensorid].used = 1; return retval; } @@ -998,9 +1447,14 @@ static int mt9v111_probe(struct i2c_client *client, */ static int mt9v111_remove(struct i2c_client *client) { + int i; + pr_debug("In mt9v111_remove\n"); - v4l2_int_device_unregister(&mt9v111_int_device); + for ( i=0 ; i < ARRAY_SIZE(mt9v111_int_device) ; i++ ) { + if( mt9v111_data[i].used ) + v4l2_int_device_unregister(&mt9v111_int_device[i]); + } return 0; } @@ -1033,13 +1487,13 @@ static __init int mt9v111_init(void) memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg)); /* Set contents of the just created structures. */ - mt9v111_config(); + mt9v111_config_datasheet(); /* Tells the i2c driver what functions to call for this driver. */ err = i2c_add_driver(&mt9v111_i2c_driver); if (err != 0) pr_err("%s:driver registration failed, error=%d \n", - __func__, err); + __func__, err); return err; } @@ -1055,7 +1509,6 @@ static void __exit mt9v111_clean(void) pr_debug("In mt9v111_clean()\n"); i2c_del_driver(&mt9v111_i2c_driver); - gpio_sensor_inactive(); if (mt9v111_device.coreReg) { kfree(mt9v111_device.coreReg); |