2008-12-29 12:04:59 +03:00
/*
* Driver for MT9T031 CMOS Image Sensor from Micron
*
* Copyright ( C ) 2008 , Guennadi Liakhovetski , DENX Software Engineering < lg @ denx . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2010-02-09 13:59:12 +03:00
# include <linux/device.h>
2008-12-29 12:04:59 +03:00
# include <linux/i2c.h>
# include <linux/log2.h>
2010-02-09 13:59:12 +03:00
# include <linux/pm.h>
# include <linux/slab.h>
# include <linux/videodev2.h>
2008-12-29 12:04:59 +03:00
# include <media/soc_camera.h>
2010-02-09 13:59:12 +03:00
# include <media/v4l2-chip-ident.h>
# include <media/v4l2-subdev.h>
2008-12-29 12:04:59 +03:00
2009-12-11 17:15:05 +03:00
/*
* mt9t031 i2c address 0x5d
2009-08-25 18:43:33 +04:00
* The platform has to define i2c_board_info and link to it from
2009-12-11 17:15:05 +03:00
* struct soc_camera_link
*/
2008-12-29 12:04:59 +03:00
/* mt9t031 selected register addresses */
# define MT9T031_CHIP_VERSION 0x00
# define MT9T031_ROW_START 0x01
# define MT9T031_COLUMN_START 0x02
# define MT9T031_WINDOW_HEIGHT 0x03
# define MT9T031_WINDOW_WIDTH 0x04
# define MT9T031_HORIZONTAL_BLANKING 0x05
# define MT9T031_VERTICAL_BLANKING 0x06
# define MT9T031_OUTPUT_CONTROL 0x07
# define MT9T031_SHUTTER_WIDTH_UPPER 0x08
# define MT9T031_SHUTTER_WIDTH 0x09
# define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
# define MT9T031_FRAME_RESTART 0x0b
# define MT9T031_SHUTTER_DELAY 0x0c
# define MT9T031_RESET 0x0d
# define MT9T031_READ_MODE_1 0x1e
# define MT9T031_READ_MODE_2 0x20
# define MT9T031_READ_MODE_3 0x21
# define MT9T031_ROW_ADDRESS_MODE 0x22
# define MT9T031_COLUMN_ADDRESS_MODE 0x23
# define MT9T031_GLOBAL_GAIN 0x35
# define MT9T031_CHIP_ENABLE 0xF8
# define MT9T031_MAX_HEIGHT 1536
# define MT9T031_MAX_WIDTH 2048
# define MT9T031_MIN_HEIGHT 2
2009-08-25 18:50:46 +04:00
# define MT9T031_MIN_WIDTH 18
2008-12-29 12:04:59 +03:00
# define MT9T031_HORIZONTAL_BLANK 142
# define MT9T031_VERTICAL_BLANK 25
# define MT9T031_COLUMN_SKIP 32
# define MT9T031_ROW_SKIP 20
# define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
SOCAM_MASTER | SOCAM_DATAWIDTH_10 )
struct mt9t031 {
2009-08-25 18:43:33 +04:00
struct v4l2_subdev subdev ;
2009-08-25 18:50:46 +04:00
struct v4l2_rect rect ; /* Sensor window */
2008-12-29 12:04:59 +03:00
int model ; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
u16 xskip ;
u16 yskip ;
2009-08-25 18:53:23 +04:00
unsigned int gain ;
2009-12-11 17:14:46 +03:00
unsigned short y_skip_top ; /* Lines to skip at the top */
2009-08-25 18:53:23 +04:00
unsigned int exposure ;
2009-08-25 18:50:46 +04:00
unsigned char autoexposure ;
2008-12-29 12:04:59 +03:00
} ;
2009-08-25 18:43:33 +04:00
static struct mt9t031 * to_mt9t031 ( const struct i2c_client * client )
{
return container_of ( i2c_get_clientdata ( client ) , struct mt9t031 , subdev ) ;
}
2009-04-24 19:57:01 +04:00
static int reg_read ( struct i2c_client * client , const u8 reg )
2008-12-29 12:04:59 +03:00
{
s32 data = i2c_smbus_read_word_data ( client , reg ) ;
return data < 0 ? data : swab16 ( data ) ;
}
2009-04-24 19:57:01 +04:00
static int reg_write ( struct i2c_client * client , const u8 reg ,
2008-12-29 12:04:59 +03:00
const u16 data )
{
2009-04-24 19:57:01 +04:00
return i2c_smbus_write_word_data ( client , reg , swab16 ( data ) ) ;
2008-12-29 12:04:59 +03:00
}
2009-04-24 19:57:01 +04:00
static int reg_set ( struct i2c_client * client , const u8 reg ,
2008-12-29 12:04:59 +03:00
const u16 data )
{
int ret ;
2009-04-24 19:57:01 +04:00
ret = reg_read ( client , reg ) ;
2008-12-29 12:04:59 +03:00
if ( ret < 0 )
return ret ;
2009-04-24 19:57:01 +04:00
return reg_write ( client , reg , ret | data ) ;
2008-12-29 12:04:59 +03:00
}
2009-04-24 19:57:01 +04:00
static int reg_clear ( struct i2c_client * client , const u8 reg ,
2008-12-29 12:04:59 +03:00
const u16 data )
{
int ret ;
2009-04-24 19:57:01 +04:00
ret = reg_read ( client , reg ) ;
2008-12-29 12:04:59 +03:00
if ( ret < 0 )
return ret ;
2009-04-24 19:57:01 +04:00
return reg_write ( client , reg , ret & ~ data ) ;
2008-12-29 12:04:59 +03:00
}
2009-04-24 19:57:01 +04:00
static int set_shutter ( struct i2c_client * client , const u32 data )
2008-12-29 12:04:59 +03:00
{
int ret ;
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_SHUTTER_WIDTH_UPPER , data > > 16 ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_SHUTTER_WIDTH , data & 0xffff ) ;
2008-12-29 12:04:59 +03:00
return ret ;
}
2009-04-24 19:57:01 +04:00
static int get_shutter ( struct i2c_client * client , u32 * data )
2008-12-29 12:04:59 +03:00
{
int ret ;
2009-04-24 19:57:01 +04:00
ret = reg_read ( client , MT9T031_SHUTTER_WIDTH_UPPER ) ;
2008-12-29 12:04:59 +03:00
* data = ret < < 16 ;
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_read ( client , MT9T031_SHUTTER_WIDTH ) ;
2008-12-29 12:04:59 +03:00
* data | = ret & 0xffff ;
return ret < 0 ? ret : 0 ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_idle ( struct i2c_client * client )
2008-12-29 12:04:59 +03:00
{
int ret ;
/* Disable chip output, synchronous option update */
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_RESET , 1 ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_RESET , 0 ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_clear ( client , MT9T031_OUTPUT_CONTROL , 2 ) ;
2008-12-29 12:04:59 +03:00
return ret > = 0 ? 0 : - EIO ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_disable ( struct i2c_client * client )
2008-12-29 12:04:59 +03:00
{
/* Disable the chip */
2009-04-24 19:57:01 +04:00
reg_clear ( client , MT9T031_OUTPUT_CONTROL , 2 ) ;
2009-04-03 17:14:02 +04:00
2008-12-29 12:04:59 +03:00
return 0 ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_s_stream ( struct v4l2_subdev * sd , int enable )
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
int ret ;
if ( enable )
/* Switch to master "normal" mode */
ret = reg_set ( client , MT9T031_OUTPUT_CONTROL , 2 ) ;
else
/* Stop sensor readout */
ret = reg_clear ( client , MT9T031_OUTPUT_CONTROL , 2 ) ;
if ( ret < 0 )
2008-12-29 12:04:59 +03:00
return - EIO ;
2009-08-25 18:43:33 +04:00
2008-12-29 12:04:59 +03:00
return 0 ;
}
static int mt9t031_set_bus_param ( struct soc_camera_device * icd ,
unsigned long flags )
{
2009-08-25 18:28:22 +04:00
struct i2c_client * client = to_i2c_client ( to_soc_camera_control ( icd ) ) ;
2009-04-24 19:57:01 +04:00
2008-12-29 12:04:59 +03:00
/* The caller should have queried our parameters, check anyway */
if ( flags & ~ MT9T031_BUS_PARAM )
return - EINVAL ;
if ( flags & SOCAM_PCLK_SAMPLE_FALLING )
2009-04-24 19:57:01 +04:00
reg_clear ( client , MT9T031_PIXEL_CLOCK_CONTROL , 0x8000 ) ;
2009-03-13 15:42:32 +03:00
else
2009-04-24 19:57:01 +04:00
reg_set ( client , MT9T031_PIXEL_CLOCK_CONTROL , 0x8000 ) ;
2008-12-29 12:04:59 +03:00
return 0 ;
}
static unsigned long mt9t031_query_bus_param ( struct soc_camera_device * icd )
{
2009-08-25 18:28:22 +04:00
struct soc_camera_link * icl = to_soc_camera_link ( icd ) ;
2008-12-29 12:04:59 +03:00
return soc_camera_apply_sensor_flags ( icl , MT9T031_BUS_PARAM ) ;
}
2009-12-11 17:53:53 +03:00
enum {
MT9T031_CTRL_VFLIP ,
MT9T031_CTRL_HFLIP ,
MT9T031_CTRL_GAIN ,
MT9T031_CTRL_EXPOSURE ,
MT9T031_CTRL_EXPOSURE_AUTO ,
} ;
static const struct v4l2_queryctrl mt9t031_controls [ ] = {
[ MT9T031_CTRL_VFLIP ] = {
. id = V4L2_CID_VFLIP ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Flip Vertically " ,
. minimum = 0 ,
. maximum = 1 ,
. step = 1 ,
. default_value = 0 ,
} ,
[ MT9T031_CTRL_HFLIP ] = {
. id = V4L2_CID_HFLIP ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Flip Horizontally " ,
. minimum = 0 ,
. maximum = 1 ,
. step = 1 ,
. default_value = 0 ,
} ,
[ MT9T031_CTRL_GAIN ] = {
. id = V4L2_CID_GAIN ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Gain " ,
. minimum = 0 ,
. maximum = 127 ,
. step = 1 ,
. default_value = 64 ,
. flags = V4L2_CTRL_FLAG_SLIDER ,
} ,
[ MT9T031_CTRL_EXPOSURE ] = {
. id = V4L2_CID_EXPOSURE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Exposure " ,
. minimum = 1 ,
. maximum = 255 ,
. step = 1 ,
. default_value = 255 ,
. flags = V4L2_CTRL_FLAG_SLIDER ,
} ,
[ MT9T031_CTRL_EXPOSURE_AUTO ] = {
. id = V4L2_CID_EXPOSURE_AUTO ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Automatic Exposure " ,
. minimum = 0 ,
. maximum = 1 ,
. step = 1 ,
. default_value = 1 ,
}
} ;
static struct soc_camera_ops mt9t031_ops = {
. set_bus_param = mt9t031_set_bus_param ,
. query_bus_param = mt9t031_query_bus_param ,
. controls = mt9t031_controls ,
. num_controls = ARRAY_SIZE ( mt9t031_controls ) ,
} ;
2009-08-25 18:50:46 +04:00
/* target must be _even_ */
static u16 mt9t031_skip ( s32 * source , s32 target , s32 max )
2009-02-23 18:13:23 +03:00
{
2009-08-25 18:50:46 +04:00
unsigned int skip ;
if ( * source < target + target / 2 ) {
* source = target ;
return 1 ;
}
skip = min ( max , * source + target / 2 ) / target ;
if ( skip > 8 )
skip = 8 ;
* source = target * skip ;
return skip ;
2009-02-23 18:13:23 +03:00
}
2009-08-25 18:50:46 +04:00
/* rect is the sensor rectangle, the caller guarantees parameter validity */
2009-12-11 17:53:53 +03:00
static int mt9t031_set_params ( struct i2c_client * client ,
2009-03-13 12:08:20 +03:00
struct v4l2_rect * rect , u16 xskip , u16 yskip )
2008-12-29 12:04:59 +03:00
{
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2008-12-29 12:04:59 +03:00
int ret ;
2009-08-25 18:50:46 +04:00
u16 xbin , ybin ;
2008-12-29 12:04:59 +03:00
const u16 hblank = MT9T031_HORIZONTAL_BLANK ,
vblank = MT9T031_VERTICAL_BLANK ;
xbin = min ( xskip , ( u16 ) 3 ) ;
ybin = min ( yskip , ( u16 ) 3 ) ;
2009-08-25 18:50:46 +04:00
/*
* Could just do roundup ( rect - > left , [ xy ] bin * 2 ) ; but this is cheaper .
* There is always a valid suitably aligned value . The worst case is
* xbin = 3 , width = 2048. Then we will start at 36 , the last read out
* pixel will be 2083 , which is < 2085 - first black pixel .
*
* MT9T031 datasheet imposes window left border alignment , depending on
* the selected xskip . Failing to conform to this requirement produces
* dark horizontal stripes in the image . However , even obeying to this
* requirement doesn ' t eliminate the stripes in all configurations . They
* appear " locally reproducibly, " but can differ between tests under
* different lighting conditions .
*/
2008-12-29 12:04:59 +03:00
switch ( xbin ) {
2009-08-25 18:50:46 +04:00
case 1 :
rect - > left & = ~ 1 ;
2008-12-29 12:04:59 +03:00
break ;
case 2 :
2009-08-25 18:50:46 +04:00
rect - > left & = ~ 3 ;
2008-12-29 12:04:59 +03:00
break ;
case 3 :
2009-08-25 18:50:46 +04:00
rect - > left = rect - > left > roundup ( MT9T031_COLUMN_SKIP , 6 ) ?
( rect - > left / 6 ) * 6 : roundup ( MT9T031_COLUMN_SKIP , 6 ) ;
2008-12-29 12:04:59 +03:00
}
2009-08-25 18:50:46 +04:00
rect - > top & = ~ 1 ;
dev_dbg ( & client - > dev , " skip %u:%u, rect %ux%u@%u:%u \n " ,
xskip , yskip , rect - > width , rect - > height , rect - > left , rect - > top ) ;
2009-02-23 18:13:23 +03:00
/* Disable register update, reconfigure atomically */
2009-04-24 19:57:01 +04:00
ret = reg_set ( client , MT9T031_OUTPUT_CONTROL , 1 ) ;
2009-02-23 18:13:23 +03:00
if ( ret < 0 )
return ret ;
2008-12-29 12:04:59 +03:00
/* Blanking and start values - default... */
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_HORIZONTAL_BLANKING , hblank ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_VERTICAL_BLANKING , vblank ) ;
2008-12-29 12:04:59 +03:00
2009-03-13 12:08:20 +03:00
if ( yskip ! = mt9t031 - > yskip | | xskip ! = mt9t031 - > xskip ) {
2008-12-29 12:04:59 +03:00
/* Binning, skipping */
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_COLUMN_ADDRESS_MODE ,
2008-12-29 12:04:59 +03:00
( ( xbin - 1 ) < < 4 ) | ( xskip - 1 ) ) ;
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_ROW_ADDRESS_MODE ,
2008-12-29 12:04:59 +03:00
( ( ybin - 1 ) < < 4 ) | ( yskip - 1 ) ) ;
}
2009-08-25 18:50:46 +04:00
dev_dbg ( & client - > dev , " new physical left %u, top %u \n " ,
rect - > left , rect - > top ) ;
2008-12-29 12:04:59 +03:00
2009-12-11 17:15:05 +03:00
/*
* The caller provides a supported format , as guaranteed by
* icd - > try_fmt_cap ( ) , soc_camera_s_crop ( ) and soc_camera_cropcap ( )
*/
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-08-25 18:50:46 +04:00
ret = reg_write ( client , MT9T031_COLUMN_START , rect - > left ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-08-25 18:50:46 +04:00
ret = reg_write ( client , MT9T031_ROW_START , rect - > top ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-08-25 18:50:46 +04:00
ret = reg_write ( client , MT9T031_WINDOW_WIDTH , rect - > width - 1 ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_write ( client , MT9T031_WINDOW_HEIGHT ,
2009-12-11 17:14:46 +03:00
rect - > height + mt9t031 - > y_skip_top - 1 ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 & & mt9t031 - > autoexposure ) {
2009-12-11 17:14:46 +03:00
unsigned int total_h = rect - > height + mt9t031 - > y_skip_top + vblank ;
2009-08-25 18:53:23 +04:00
ret = set_shutter ( client , total_h ) ;
2008-12-29 12:04:59 +03:00
if ( ret > = 0 ) {
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank ;
const struct v4l2_queryctrl * qctrl =
2009-12-11 17:53:53 +03:00
& mt9t031_controls [ MT9T031_CTRL_EXPOSURE ] ;
2009-08-25 18:53:23 +04:00
mt9t031 - > exposure = ( shutter_max / 2 + ( total_h - 1 ) *
( qctrl - > maximum - qctrl - > minimum ) ) /
2008-12-29 12:04:59 +03:00
shutter_max + qctrl - > minimum ;
}
}
2009-03-13 12:08:20 +03:00
/* Re-enable register update, commit all changes */
if ( ret > = 0 )
2009-04-24 19:57:01 +04:00
ret = reg_clear ( client , MT9T031_OUTPUT_CONTROL , 1 ) ;
2009-03-13 12:08:20 +03:00
2009-08-25 18:50:46 +04:00
if ( ret > = 0 ) {
mt9t031 - > rect = * rect ;
mt9t031 - > xskip = xskip ;
mt9t031 - > yskip = yskip ;
}
2009-03-13 12:08:20 +03:00
return ret < 0 ? ret : 0 ;
}
2009-08-25 18:46:54 +04:00
static int mt9t031_s_crop ( struct v4l2_subdev * sd , struct v4l2_crop * a )
2009-03-13 12:08:20 +03:00
{
2009-08-25 18:50:46 +04:00
struct v4l2_rect rect = a - > c ;
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2009-03-13 12:08:20 +03:00
2009-08-25 18:50:46 +04:00
rect . width = ALIGN ( rect . width , 2 ) ;
rect . height = ALIGN ( rect . height , 2 ) ;
soc_camera_limit_side ( & rect . left , & rect . width ,
MT9T031_COLUMN_SKIP , MT9T031_MIN_WIDTH , MT9T031_MAX_WIDTH ) ;
soc_camera_limit_side ( & rect . top , & rect . height ,
MT9T031_ROW_SKIP , MT9T031_MIN_HEIGHT , MT9T031_MAX_HEIGHT ) ;
2009-12-11 17:53:53 +03:00
return mt9t031_set_params ( client , & rect , mt9t031 - > xskip , mt9t031 - > yskip ) ;
2009-08-25 18:50:46 +04:00
}
static int mt9t031_g_crop ( struct v4l2_subdev * sd , struct v4l2_crop * a )
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:50:46 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
a - > c = mt9t031 - > rect ;
a - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
2009-08-25 18:46:42 +04:00
2009-08-25 18:50:46 +04:00
return 0 ;
}
static int mt9t031_cropcap ( struct v4l2_subdev * sd , struct v4l2_cropcap * a )
{
a - > bounds . left = MT9T031_COLUMN_SKIP ;
a - > bounds . top = MT9T031_ROW_SKIP ;
a - > bounds . width = MT9T031_MAX_WIDTH ;
a - > bounds . height = MT9T031_MAX_HEIGHT ;
a - > defrect = a - > bounds ;
a - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
a - > pixelaspect . numerator = 1 ;
a - > pixelaspect . denominator = 1 ;
2009-08-25 18:46:42 +04:00
2009-08-25 18:50:46 +04:00
return 0 ;
}
2009-12-11 17:46:49 +03:00
static int mt9t031_g_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
2009-08-25 18:50:46 +04:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:50:46 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2009-12-11 17:46:49 +03:00
mf - > width = mt9t031 - > rect . width / mt9t031 - > xskip ;
mf - > height = mt9t031 - > rect . height / mt9t031 - > yskip ;
mf - > code = V4L2_MBUS_FMT_SBGGR10_1X10 ;
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
mf - > field = V4L2_FIELD_NONE ;
2009-08-25 18:50:46 +04:00
return 0 ;
2009-03-13 12:08:20 +03:00
}
2009-12-11 17:46:49 +03:00
static int mt9t031_s_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
2009-03-13 12:08:20 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2009-03-13 12:08:20 +03:00
u16 xskip , yskip ;
2009-08-25 18:50:46 +04:00
struct v4l2_rect rect = mt9t031 - > rect ;
2009-03-13 12:08:20 +03:00
/*
2009-08-25 18:50:46 +04:00
* try_fmt has put width and height within limits .
* S_FMT : use binning and skipping for scaling
2009-03-13 12:08:20 +03:00
*/
2009-12-11 17:46:49 +03:00
xskip = mt9t031_skip ( & rect . width , mf - > width , MT9T031_MAX_WIDTH ) ;
yskip = mt9t031_skip ( & rect . height , mf - > height , MT9T031_MAX_HEIGHT ) ;
mf - > code = V4L2_MBUS_FMT_SBGGR10_1X10 ;
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
2008-12-29 12:04:59 +03:00
2009-08-25 18:50:46 +04:00
/* mt9t031_set_params() doesn't change width and height */
2009-12-11 17:53:53 +03:00
return mt9t031_set_params ( client , & rect , xskip , yskip ) ;
2008-12-29 12:04:59 +03:00
}
2009-08-25 18:50:46 +04:00
/*
* If a user window larger than sensor window is requested , we ' ll increase the
* sensor window .
*/
2009-12-11 17:46:49 +03:00
static int mt9t031_try_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
2008-12-29 12:04:59 +03:00
{
2009-05-31 04:45:46 +04:00
v4l_bound_align_image (
2009-12-11 17:46:49 +03:00
& mf - > width , MT9T031_MIN_WIDTH , MT9T031_MAX_WIDTH , 1 ,
& mf - > height , MT9T031_MIN_HEIGHT , MT9T031_MAX_HEIGHT , 1 , 0 ) ;
mf - > code = V4L2_MBUS_FMT_SBGGR10_1X10 ;
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
2008-12-29 12:04:59 +03:00
return 0 ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_g_chip_ident ( struct v4l2_subdev * sd ,
struct v4l2_dbg_chip_ident * id )
2008-12-29 12:04:59 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2008-12-29 12:04:59 +03:00
2008-12-30 13:14:19 +03:00
if ( id - > match . type ! = V4L2_CHIP_MATCH_I2C_ADDR )
2008-12-29 12:04:59 +03:00
return - EINVAL ;
2009-08-25 18:28:22 +04:00
if ( id - > match . addr ! = client - > addr )
2008-12-29 12:04:59 +03:00
return - ENODEV ;
id - > ident = mt9t031 - > model ;
id - > revision = 0 ;
return 0 ;
}
# ifdef CONFIG_VIDEO_ADV_DEBUG
2009-08-25 18:43:33 +04:00
static int mt9t031_g_register ( struct v4l2_subdev * sd ,
struct v4l2_dbg_register * reg )
2008-12-29 12:04:59 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-29 12:04:59 +03:00
2008-12-30 13:14:19 +03:00
if ( reg - > match . type ! = V4L2_CHIP_MATCH_I2C_ADDR | | reg - > reg > 0xff )
2008-12-29 12:04:59 +03:00
return - EINVAL ;
2009-04-24 19:57:01 +04:00
if ( reg - > match . addr ! = client - > addr )
2008-12-29 12:04:59 +03:00
return - ENODEV ;
2009-04-24 19:57:01 +04:00
reg - > val = reg_read ( client , reg - > reg ) ;
2008-12-29 12:04:59 +03:00
if ( reg - > val > 0xffff )
return - EIO ;
return 0 ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_s_register ( struct v4l2_subdev * sd ,
struct v4l2_dbg_register * reg )
2008-12-29 12:04:59 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-29 12:04:59 +03:00
2008-12-30 13:14:19 +03:00
if ( reg - > match . type ! = V4L2_CHIP_MATCH_I2C_ADDR | | reg - > reg > 0xff )
2008-12-29 12:04:59 +03:00
return - EINVAL ;
2009-04-24 19:57:01 +04:00
if ( reg - > match . addr ! = client - > addr )
2008-12-29 12:04:59 +03:00
return - ENODEV ;
2009-04-24 19:57:01 +04:00
if ( reg_write ( client , reg - > reg , reg - > val ) < 0 )
2008-12-29 12:04:59 +03:00
return - EIO ;
return 0 ;
}
# endif
2009-08-25 18:43:33 +04:00
static int mt9t031_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2008-12-29 12:04:59 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2008-12-29 12:04:59 +03:00
int data ;
switch ( ctrl - > id ) {
case V4L2_CID_VFLIP :
2009-04-24 19:57:01 +04:00
data = reg_read ( client , MT9T031_READ_MODE_2 ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
ctrl - > value = ! ! ( data & 0x8000 ) ;
break ;
case V4L2_CID_HFLIP :
2009-04-24 19:57:01 +04:00
data = reg_read ( client , MT9T031_READ_MODE_2 ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
ctrl - > value = ! ! ( data & 0x4000 ) ;
break ;
case V4L2_CID_EXPOSURE_AUTO :
ctrl - > value = mt9t031 - > autoexposure ;
break ;
2009-08-25 18:53:23 +04:00
case V4L2_CID_GAIN :
ctrl - > value = mt9t031 - > gain ;
break ;
case V4L2_CID_EXPOSURE :
ctrl - > value = mt9t031 - > exposure ;
break ;
2008-12-29 12:04:59 +03:00
}
return 0 ;
}
2009-08-25 18:43:33 +04:00
static int mt9t031_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2008-12-29 12:04:59 +03:00
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2008-12-29 12:04:59 +03:00
const struct v4l2_queryctrl * qctrl ;
int data ;
switch ( ctrl - > id ) {
case V4L2_CID_VFLIP :
if ( ctrl - > value )
2009-04-24 19:57:01 +04:00
data = reg_set ( client , MT9T031_READ_MODE_2 , 0x8000 ) ;
2008-12-29 12:04:59 +03:00
else
2009-04-24 19:57:01 +04:00
data = reg_clear ( client , MT9T031_READ_MODE_2 , 0x8000 ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
break ;
case V4L2_CID_HFLIP :
if ( ctrl - > value )
2009-04-24 19:57:01 +04:00
data = reg_set ( client , MT9T031_READ_MODE_2 , 0x4000 ) ;
2008-12-29 12:04:59 +03:00
else
2009-04-24 19:57:01 +04:00
data = reg_clear ( client , MT9T031_READ_MODE_2 , 0x4000 ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
break ;
case V4L2_CID_GAIN :
2009-12-11 17:53:53 +03:00
qctrl = & mt9t031_controls [ MT9T031_CTRL_GAIN ] ;
2008-12-29 12:04:59 +03:00
if ( ctrl - > value > qctrl - > maximum | | ctrl - > value < qctrl - > minimum )
return - EINVAL ;
/* See Datasheet Table 7, Gain settings. */
if ( ctrl - > value < = qctrl - > default_value ) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
unsigned long range = qctrl - > default_value - qctrl - > minimum ;
data = ( ( ctrl - > value - qctrl - > minimum ) * 8 + range / 2 ) / range ;
2009-08-25 18:47:00 +04:00
dev_dbg ( & client - > dev , " Setting gain %d \n " , data ) ;
2009-04-24 19:57:01 +04:00
data = reg_write ( client , MT9T031_GLOBAL_GAIN , data ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
} else {
2009-02-23 18:13:23 +03:00
/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
2008-12-29 12:04:59 +03:00
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
unsigned long range = qctrl - > maximum - qctrl - > default_value - 1 ;
2009-02-23 18:13:23 +03:00
/* calculated gain: map 65..127 to 9..1024 step 0.125 */
2008-12-29 12:04:59 +03:00
unsigned long gain = ( ( ctrl - > value - qctrl - > default_value - 1 ) *
2009-02-23 18:13:23 +03:00
1015 + range / 2 ) / range + 9 ;
2008-12-29 12:04:59 +03:00
2009-02-23 18:13:23 +03:00
if ( gain < = 32 ) /* calculated gain 9..32 -> 9..32 */
2008-12-29 12:04:59 +03:00
data = gain ;
2009-02-23 18:13:23 +03:00
else if ( gain < = 64 ) /* calculated gain 33..64 -> 0x51..0x60 */
2008-12-29 12:04:59 +03:00
data = ( ( gain - 32 ) * 16 + 16 ) / 32 + 80 ;
else
2009-02-23 18:13:23 +03:00
/* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
data = ( ( ( gain - 64 + 7 ) * 32 ) & 0xff00 ) | 0x60 ;
2008-12-29 12:04:59 +03:00
2009-08-25 18:47:00 +04:00
dev_dbg ( & client - > dev , " Set gain from 0x%x to 0x%x \n " ,
2009-04-24 19:57:01 +04:00
reg_read ( client , MT9T031_GLOBAL_GAIN ) , data ) ;
data = reg_write ( client , MT9T031_GLOBAL_GAIN , data ) ;
2008-12-29 12:04:59 +03:00
if ( data < 0 )
return - EIO ;
}
/* Success */
2009-08-25 18:53:23 +04:00
mt9t031 - > gain = ctrl - > value ;
2008-12-29 12:04:59 +03:00
break ;
case V4L2_CID_EXPOSURE :
2009-12-11 17:53:53 +03:00
qctrl = & mt9t031_controls [ MT9T031_CTRL_EXPOSURE ] ;
2008-12-29 12:04:59 +03:00
/* mt9t031 has maximum == default */
if ( ctrl - > value > qctrl - > maximum | | ctrl - > value < qctrl - > minimum )
return - EINVAL ;
else {
const unsigned long range = qctrl - > maximum - qctrl - > minimum ;
const u32 shutter = ( ( ctrl - > value - qctrl - > minimum ) * 1048 +
range / 2 ) / range + 1 ;
u32 old ;
2009-04-24 19:57:01 +04:00
get_shutter ( client , & old ) ;
2009-08-25 18:47:00 +04:00
dev_dbg ( & client - > dev , " Set shutter from %u to %u \n " ,
2008-12-29 12:04:59 +03:00
old , shutter ) ;
2009-04-24 19:57:01 +04:00
if ( set_shutter ( client , shutter ) < 0 )
2008-12-29 12:04:59 +03:00
return - EIO ;
2009-08-25 18:53:23 +04:00
mt9t031 - > exposure = ctrl - > value ;
2008-12-29 12:04:59 +03:00
mt9t031 - > autoexposure = 0 ;
}
break ;
case V4L2_CID_EXPOSURE_AUTO :
if ( ctrl - > value ) {
const u16 vblank = MT9T031_VERTICAL_BLANK ;
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank ;
2009-08-25 18:53:23 +04:00
unsigned int total_h = mt9t031 - > rect . height +
2009-12-11 17:14:46 +03:00
mt9t031 - > y_skip_top + vblank ;
2009-08-25 18:53:23 +04:00
if ( set_shutter ( client , total_h ) < 0 )
2008-12-29 12:04:59 +03:00
return - EIO ;
2009-12-11 17:53:53 +03:00
qctrl = & mt9t031_controls [ MT9T031_CTRL_EXPOSURE ] ;
2009-08-25 18:53:23 +04:00
mt9t031 - > exposure = ( shutter_max / 2 + ( total_h - 1 ) *
( qctrl - > maximum - qctrl - > minimum ) ) /
2008-12-29 12:04:59 +03:00
shutter_max + qctrl - > minimum ;
mt9t031 - > autoexposure = 1 ;
} else
mt9t031 - > autoexposure = 0 ;
break ;
2009-12-11 17:53:53 +03:00
default :
return - EINVAL ;
2008-12-29 12:04:59 +03:00
}
return 0 ;
}
2010-02-09 13:59:12 +03:00
/*
* Power Management :
* This function does nothing for now but must be present for pm to work
*/
static int mt9t031_runtime_suspend ( struct device * dev )
{
return 0 ;
}
/*
* Power Management :
* COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged
* they are however changed at reset if the platform hook is present
* thus we rewrite them with the values stored by the driver
*/
static int mt9t031_runtime_resume ( struct device * dev )
{
struct video_device * vdev = to_video_device ( dev ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_device * icd = dev_get_drvdata ( vdev - > parent ) ;
2010-02-09 13:59:12 +03:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2010-02-09 13:59:12 +03:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
int ret ;
u16 xbin , ybin ;
xbin = min ( mt9t031 - > xskip , ( u16 ) 3 ) ;
ybin = min ( mt9t031 - > yskip , ( u16 ) 3 ) ;
ret = reg_write ( client , MT9T031_COLUMN_ADDRESS_MODE ,
( ( xbin - 1 ) < < 4 ) | ( mt9t031 - > xskip - 1 ) ) ;
if ( ret < 0 )
return ret ;
ret = reg_write ( client , MT9T031_ROW_ADDRESS_MODE ,
( ( ybin - 1 ) < < 4 ) | ( mt9t031 - > yskip - 1 ) ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static struct dev_pm_ops mt9t031_dev_pm_ops = {
. runtime_suspend = mt9t031_runtime_suspend ,
. runtime_resume = mt9t031_runtime_resume ,
} ;
static struct device_type mt9t031_dev_type = {
. name = " MT9T031 " ,
. pm = & mt9t031_dev_pm_ops ,
} ;
2009-12-11 17:15:05 +03:00
/*
* Interface active , can use i2c . If it fails , it can indeed mean , that
* this wasn ' t our capture interface , so , we wait for the right one
*/
2009-08-25 18:43:33 +04:00
static int mt9t031_video_probe ( struct i2c_client * client )
2008-12-29 12:04:59 +03:00
{
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2010-02-09 13:59:12 +03:00
struct video_device * vdev = soc_camera_i2c_to_vdev ( client ) ;
2008-12-29 12:04:59 +03:00
s32 data ;
2009-08-25 18:53:23 +04:00
int ret ;
2008-12-29 12:04:59 +03:00
/* Enable the chip */
2009-04-24 19:57:01 +04:00
data = reg_write ( client , MT9T031_CHIP_ENABLE , 1 ) ;
2009-08-25 18:47:00 +04:00
dev_dbg ( & client - > dev , " write: %d \n " , data ) ;
2008-12-29 12:04:59 +03:00
/* Read out the chip version register */
2009-04-24 19:57:01 +04:00
data = reg_read ( client , MT9T031_CHIP_VERSION ) ;
2008-12-29 12:04:59 +03:00
switch ( data ) {
case 0x1621 :
mt9t031 - > model = V4L2_IDENT_MT9T031 ;
break ;
default :
2009-08-25 18:47:00 +04:00
dev_err ( & client - > dev ,
2008-12-29 12:04:59 +03:00
" No MT9T031 chip detected, register read %x \n " , data ) ;
2009-08-25 18:28:22 +04:00
return - ENODEV ;
2008-12-29 12:04:59 +03:00
}
2009-08-25 18:47:00 +04:00
dev_info ( & client - > dev , " Detected a MT9T031 chip ID %x \n " , data ) ;
2008-12-29 12:04:59 +03:00
2009-08-25 18:53:23 +04:00
ret = mt9t031_idle ( client ) ;
if ( ret < 0 )
dev_err ( & client - > dev , " Failed to initialise the camera \n " ) ;
2010-02-09 13:59:12 +03:00
else
vdev - > dev . type = & mt9t031_dev_type ;
2009-08-25 18:53:23 +04:00
2009-08-25 18:53:23 +04:00
/* mt9t031_idle() has reset the chip to default. */
mt9t031 - > exposure = 255 ;
mt9t031 - > gain = 64 ;
2009-08-25 18:53:23 +04:00
return ret ;
2008-12-29 12:04:59 +03:00
}
2009-12-11 17:14:46 +03:00
static int mt9t031_g_skip_top_lines ( struct v4l2_subdev * sd , u32 * lines )
{
2010-07-31 00:24:54 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-12-11 17:14:46 +03:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
* lines = mt9t031 - > y_skip_top ;
return 0 ;
}
2009-08-25 18:43:33 +04:00
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
. g_ctrl = mt9t031_g_ctrl ,
. s_ctrl = mt9t031_s_ctrl ,
. g_chip_ident = mt9t031_g_chip_ident ,
# ifdef CONFIG_VIDEO_ADV_DEBUG
. g_register = mt9t031_g_register ,
. s_register = mt9t031_s_register ,
# endif
} ;
2010-05-09 00:55:00 +04:00
static int mt9t031_enum_fmt ( struct v4l2_subdev * sd , unsigned int index ,
2009-12-11 17:46:49 +03:00
enum v4l2_mbus_pixelcode * code )
{
if ( index )
return - EINVAL ;
* code = V4L2_MBUS_FMT_SBGGR10_1X10 ;
return 0 ;
}
2009-08-25 18:43:33 +04:00
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
. s_stream = mt9t031_s_stream ,
2009-12-11 17:46:49 +03:00
. s_mbus_fmt = mt9t031_s_fmt ,
. g_mbus_fmt = mt9t031_g_fmt ,
. try_mbus_fmt = mt9t031_try_fmt ,
2009-08-25 18:46:54 +04:00
. s_crop = mt9t031_s_crop ,
2009-08-25 18:50:46 +04:00
. g_crop = mt9t031_g_crop ,
. cropcap = mt9t031_cropcap ,
2009-12-11 17:46:49 +03:00
. enum_mbus_fmt = mt9t031_enum_fmt ,
2009-08-25 18:43:33 +04:00
} ;
2009-12-11 17:14:46 +03:00
static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
. g_skip_top_lines = mt9t031_g_skip_top_lines ,
} ;
2009-08-25 18:43:33 +04:00
static struct v4l2_subdev_ops mt9t031_subdev_ops = {
. core = & mt9t031_subdev_core_ops ,
. video = & mt9t031_subdev_video_ops ,
2009-12-11 17:14:46 +03:00
. sensor = & mt9t031_subdev_sensor_ops ,
2009-08-25 18:43:33 +04:00
} ;
2008-12-29 12:04:59 +03:00
static int mt9t031_probe ( struct i2c_client * client ,
const struct i2c_device_id * did )
{
struct mt9t031 * mt9t031 ;
2009-08-25 18:28:22 +04:00
struct soc_camera_device * icd = client - > dev . platform_data ;
2008-12-29 12:04:59 +03:00
struct i2c_adapter * adapter = to_i2c_adapter ( client - > dev . parent ) ;
int ret ;
2009-12-11 17:53:53 +03:00
if ( icd ) {
struct soc_camera_link * icl = to_soc_camera_link ( icd ) ;
if ( ! icl ) {
dev_err ( & client - > dev , " MT9T031 driver needs platform data \n " ) ;
return - EINVAL ;
}
2009-08-25 18:28:22 +04:00
2009-12-11 17:53:53 +03:00
icd - > ops = & mt9t031_ops ;
2008-12-29 12:04:59 +03:00
}
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_WORD_DATA ) ) {
dev_warn ( & adapter - > dev ,
" I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD \n " ) ;
return - EIO ;
}
mt9t031 = kzalloc ( sizeof ( struct mt9t031 ) , GFP_KERNEL ) ;
if ( ! mt9t031 )
return - ENOMEM ;
2009-08-25 18:43:33 +04:00
v4l2_i2c_subdev_init ( & mt9t031 - > subdev , client , & mt9t031_subdev_ops ) ;
2008-12-29 12:04:59 +03:00
2009-12-11 17:14:46 +03:00
mt9t031 - > y_skip_top = 0 ;
2009-08-25 18:50:46 +04:00
mt9t031 - > rect . left = MT9T031_COLUMN_SKIP ;
mt9t031 - > rect . top = MT9T031_ROW_SKIP ;
mt9t031 - > rect . width = MT9T031_MAX_WIDTH ;
mt9t031 - > rect . height = MT9T031_MAX_HEIGHT ;
2009-12-11 17:15:05 +03:00
/*
* Simulated autoexposure . If enabled , we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width
*/
2008-12-29 12:04:59 +03:00
mt9t031 - > autoexposure = 1 ;
mt9t031 - > xskip = 1 ;
mt9t031 - > yskip = 1 ;
2009-08-25 18:43:33 +04:00
mt9t031_idle ( client ) ;
ret = mt9t031_video_probe ( client ) ;
mt9t031_disable ( client ) ;
2009-08-25 18:28:22 +04:00
if ( ret ) {
2009-12-11 17:53:53 +03:00
if ( icd )
icd - > ops = NULL ;
2009-08-25 18:28:22 +04:00
kfree ( mt9t031 ) ;
}
2008-12-29 12:04:59 +03:00
return ret ;
}
static int mt9t031_remove ( struct i2c_client * client )
{
2009-08-25 18:43:33 +04:00
struct mt9t031 * mt9t031 = to_mt9t031 ( client ) ;
2009-08-25 18:28:22 +04:00
struct soc_camera_device * icd = client - > dev . platform_data ;
2008-12-29 12:04:59 +03:00
2009-12-11 17:53:53 +03:00
if ( icd )
icd - > ops = NULL ;
2008-12-29 12:04:59 +03:00
kfree ( mt9t031 ) ;
return 0 ;
}
static const struct i2c_device_id mt9t031_id [ ] = {
{ " mt9t031 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mt9t031_id ) ;
static struct i2c_driver mt9t031_i2c_driver = {
. driver = {
. name = " mt9t031 " ,
} ,
. probe = mt9t031_probe ,
. remove = mt9t031_remove ,
. id_table = mt9t031_id ,
} ;
static int __init mt9t031_mod_init ( void )
{
return i2c_add_driver ( & mt9t031_i2c_driver ) ;
}
static void __exit mt9t031_mod_exit ( void )
{
i2c_del_driver ( & mt9t031_i2c_driver ) ;
}
module_init ( mt9t031_mod_init ) ;
module_exit ( mt9t031_mod_exit ) ;
MODULE_DESCRIPTION ( " Micron MT9T031 Camera driver " ) ;
MODULE_AUTHOR ( " Guennadi Liakhovetski <lg@denx.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;