/*
* 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.
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
/*
* mt9t031 i2c address 0x5d
* The platform has to define i2c_board_info and link to it from
* struct soc_camera_link
*/
/* 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
#define MT9T031_MIN_WIDTH 18
#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 {
struct v4l2_subdev subdev;
struct v4l2_rect rect; /* Sensor window */
int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
u16 xskip;
u16 yskip;
unsigned int gain;
unsigned short y_skip_top; /* Lines to skip at the top */
unsigned int exposure;
unsigned char autoexposure;
};
static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
}
static int reg_read(struct i2c_client *client, const u8 reg)
{
s32 data = i2c_smbus_read_word_data(client, reg);
return data < 0 ? data : swab16(data);
}
static int reg_write(struct i2c_client *client, const u8 reg,
const u16 data)
{
return i2c_smbus_write_word_data(client, reg, swab16(data));
}
static int reg_set(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret | data);
}
static int reg_clear(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret & ~data);
}
static int set_shutter(struct i2c_client *client, const u32 data)
{
int ret;
ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
if (ret >= 0)
ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);
return ret;
}
static int get_shutter(struct i2c_client *client, u32 *data)
{
int ret;
ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
*data = ret << 16;
if (ret >= 0)
ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
*data |= ret & 0xffff;
return ret < 0 ? ret : 0;
}
static int mt9t031_idle(struct i2c_client *client)
{
int ret;
/* Disable chip output, synchronous option update */
ret = reg_write(client, MT9T031_RESET, 1);
if (ret >= 0)
ret = reg_write(client, MT9T031_RESET, 0);
if (ret >= 0)
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
return ret >= 0 ? 0 : -EIO;
}
static int mt9t031_disable(struct i2c_client *client)
{
/* Disable the chip */
reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
return 0;
}
static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = sd->priv;
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)
return -EIO;
return 0;
}
static int mt9t031_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
struct i2c_client