/*
* ar0832_main.c - Aptina AR0832 8M Bayer type sensor driver
*
* Copyright (c) 2011, NVIDIA, All Rights Reserved.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <mach/hardware.h>
#include <linux/gpio.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/atomic.h>
#include <linux/regulator/consumer.h>
#include <media/ar0832_main.h>
#define POS_LOW 0
#define POS_HIGH 1000
#define SETTLETIME_MS 100
struct ar0832_sensor_info {
int mode;
struct ar0832_stereo_region region;
};
struct ar0832_focuser_info {
struct ar0832_focuser_config config;
int focuser_init_flag;
u16 last_position;
};
struct ar0832_power_rail {
struct regulator *sen_1v8_reg;
struct regulator *sen_2v8_reg;
};
struct ar0832_dev {
struct ar0832_sensor_info *sensor_info;
struct ar0832_focuser_info *focuser_info;
struct ar0832_platform_data *pdata;
struct i2c_client *i2c_client;
struct mutex ar0832_camera_lock;
struct miscdevice misc_dev;
struct ar0832_power_rail power_rail;
int brd_power_cnt;
atomic_t in_use;
char dname[20];
int is_stereo;
u16 sensor_id_data;
struct dentry *debugdir;
};
#define UpperByte16to8(x) ((u8)((x & 0xFF00) >> 8))
#define LowerByte16to8(x) ((u8)(x & 0x00FF))
#define ar0832_TABLE_WAIT_MS 0
#define ar0832_TABLE_END 1
#define ar0832_MAX_RETRIES 3
/* AR0832 Register */
#define AR0832_SENSORID_REG 0x0002
#define AR0832_RESET_REG 0x301A
#define AR0832_ID_REG 0x31FC
#define AR0832_GLOBAL_GAIN_REG 0x305E
#define AR0832_TEST_PATTERN_REG 0x0600
#define AR0832_GROUP_HOLD_REG 0x0104
#define AR0832_TEST_RED_REG 0x0602
#define AR0832_TEST_GREENR_REG 0x0604
#define AR0832_TEST_BLUE_REG 0x0606
#define AR0832_TEST_GREENB_REG 0x0608
/* AR0832_RESET_REG */
#define AR0832_RESET_REG_GROUPED_PARAMETER_HOLD (1 << 15)
#define AR0832_RESET_REG_GAIN_INSERT (1 << 14)
#define AR0832_RESET_REG_SMIA_SERIALIZER_DIS (1 << 12)
#define AR0832_RESET_REG_RESTART_BAD (1 << 10)
#define AR0832_RESET_REG_MASK_BAD (1 << 9)
#define AR0832_RESET_REG_GPI_EN (1 << 8)
#define AR0832_RESET_REG_PARALLEL_EN (1 << 7)
#define AR0832_RESET_REG_DRIVE_PINS (1 << 6)
#define AR0832_RESET_REG_STDBY_EOF (1 << 4)
#define AR0832_RESET_REG_LOCK_REG (1 << 3)
#define AR0832_RESET_REG_STREAM (1 << 2)
#define AR0832_RESET_REG_RESTART (1 << 1)
#define AR0832_RESET_REG_RESET (1 << 0)
static struct ar0832_reg mode_start[] = {
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_3264X2448_8140[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* MT9E013 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E77}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D20}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x0004}, /* X_ADDR_START */
{0x0348, 0x0CCB}, /* X_ADDR_END */
{0x0346, 0x0004}, /* Y_ADDR_START */
{0x034A, 0x099B}, /* Y_ADDR_END */
{0x034C, 0x0CC8}, /* X_OUTPUT_SIZE */
{0x034E, 0x0998}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x0342, 0x133C}, /* LINE_LENGTH_PCK */
{0x0340, 0x0A27}, /* FRAME_LENGTH_LINES */
{0x0202, 0x0A27}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x09DC}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_3264X2448_8141[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* AR0832 Recommended Settings */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x0004}, /* X_ADDR_START */
{0x0348, 0x0CCB}, /* X_ADDR_END */
{0x0346, 0x0004}, /* Y_ADDR_START */
{0x034A, 0x099B}, /* Y_ADDR_END */
{0x034C, 0x0CC8}, /* X_OUTPUT_SIZE */
{0x034E, 0x0998}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x0342, 0x133C}, /* LINE_LENGTH_PCK */
{0x0340, 0x0A27}, /* FRAME_LENGTH_LINES */
{0x0202, 0x0A27}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x09DC}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_2880X1620_8140[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* MT9E013 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E77}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D20}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x00C8}, /* X_ADDR_START */
{0x0348, 0x0C07}, /* X_ADDR_END */
{0x0346, 0x01A6}, /* Y_ADDR_START */
{0x034A, 0x07F9}, /* Y_ADDR_END */
{0x034C, 0x0B40}, /* X_OUTPUT_SIZE */
{0x034E, 0x0654}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0342, 0x11B8}, /* LINE_LENGTH_PCK */
{0x0340, 0x06E3}, /* FRAME_LENGTH_LINES */
{0x0202, 0x06E3}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0BD8}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_2880X1620_8141[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* AR0832 Recommended Settings */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x00C8}, /* X_ADDR_START */
{0x0348, 0x0C07}, /* X_ADDR_END */
{0x0346, 0x01A6}, /* Y_ADDR_START */
{0x034A, 0x07F9}, /* Y_ADDR_END */
{0x034C, 0x0B40}, /* X_OUTPUT_SIZE */
{0x034E, 0x0654}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0342, 0x11B8}, /* LINE_LENGTH_PCK */
{0x0340, 0x06E3}, /* FRAME_LENGTH_LINES */
{0x0202, 0x06E3}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0BD8}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_1920X1080_8140[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* MT9E013 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E77}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D20}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x028C}, /* X_ADDR_START */
{0x0348, 0x0A0B}, /* X_ADDR_END */
{0x0346, 0x006E}, /* Y_ADDR_START */
{0x034A, 0x04A5}, /* Y_ADDR_END */
{0x034C, 0x0780}, /* X_OUTPUT_SIZE */
{0x034E, 0x0438}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0342, 0x103B}, /* LINE_LENGTH_PCK */
{0x0340, 0x05C4}, /* FRAME_LENGTH_LINES */
{0x0202, 0x05C4}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0702}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_1920X1080_8141[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* AR0832 Recommended Settings */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001},
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x028C}, /* X_ADDR_START */
{0x0348, 0x0A0B}, /* X_ADDR_END */
{0x0346, 0x006E}, /* Y_ADDR_START */
{0x034A, 0x04A5}, /* Y_ADDR_END */
{0x034C, 0x0780}, /* X_OUTPUT_SIZE */
{0x034E, 0x0438}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0342, 0x103B}, /* LINE_LENGTH_PCK */
{0x0340, 0x05C4}, /* FRAME_LENGTH_LINES */
{0x0202, 0x05C4}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0702}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_1632X1224_8140[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
/* SC-CHANGE: to-do 8 bit write */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* MT9E013 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E77}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D20}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x0008}, /* X_ADDR_START */
{0x0348, 0x0CC9}, /* X_ADDR_END */
{0x0346, 0x0008}, /* Y_ADDR_START */
{0x034A, 0x0999}, /* Y_ADDR_END */
{0x034C, 0x0660}, /* X_OUTPUT_SIZE */
{0x034E, 0x04C8}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC4C3}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0400, 0x0002}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x0342, 0x101A}, /* LINE_LENGTH_PCK */
{0x0340, 0x0610}, /* FRAME_LENGTH_LINES */
{0x0202, 0x0557}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0988}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0130}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
/* todo 8-bit write */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_1632X1224_8141[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
/* SC-CHANGE: to-do 8 bit write */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* AR0832 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E77}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D20}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
{0x3174, 0x8000},
/* mode end */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0040}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x0008}, /* X_ADDR_START */
{0x0348, 0x0CC9}, /* X_ADDR_END */
{0x0346, 0x0008}, /* Y_ADDR_START */
{0x034A, 0x0999}, /* Y_ADDR_END */
{0x034C, 0x0660}, /* X_OUTPUT_SIZE */
{0x034E, 0x04C8}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC4C3}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0400, 0x0002}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x0342, 0x101A}, /* LINE_LENGTH_PCK */
{0x0340, 0x0610}, /* FRAME_LENGTH_LINES */
{0x0202, 0x0557}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x0988}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0130}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
/* todo 8-bit write */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_800X600_8140[] = {
/* mode start */
{0x301A, 0x0058},
{0x301A, 0x0050},
{0x0104, 0x0100},
{0x3064, 0x7800},
{0x31AE, 0x0202},
{0x31B8, 0x0E3F},
{0x31BE, 0xC003},
{0x3070, 0x0000},
{ar0832_TABLE_WAIT_MS, 0x0005},
/* MT9E013 Recommended Settings */
{0x3044, 0x0590},
{0x306E, 0xFC80},
{0x30B2, 0xC000},
{0x30D6, 0x0800},
{0x316C, 0xB42F},
{0x316E, 0x869A},
{0x3170, 0x210E},
{0x317A, 0x010E},
{0x31E0, 0x1FB9},
{0x31E6, 0x07FC},
{0x37C0, 0x0000},
{0x37C2, 0x0000},
{0x37C4, 0x0000},
{0x37C6, 0x0000},
{0x3E00, 0x0011},
{0x3E02, 0x8801},
{0x3E04, 0x2801},
{0x3E06, 0x8449},
{0x3E08, 0x6841},
{0x3E0A, 0x400C},
{0x3E0C, 0x1001},
{0x3E0E, 0x2603},
{0x3E10, 0x4B41},
{0x3E12, 0x4B24},
{0x3E14, 0xA3CF},
{0x3E16, 0x8802},
{0x3E18, 0x8401},
{0x3E1A, 0x8601},
{0x3E1C, 0x8401},
{0x3E1E, 0x840A},
{0x3E20, 0xFF00},
{0x3E22, 0x8401},
{0x3E24, 0x00FF},
{0x3E26, 0x0088},
{0x3E28, 0x2E8A},
{0x3E30, 0x0000},
{0x3E32, 0x8801},
{0x3E34, 0x4029},
{0x3E36, 0x00FF},
{0x3E38, 0x8469},
{0x3E3A, 0x00FF},
{0x3E3C, 0x2801},
{0x3E3E, 0x3E2A},
{0x3E40, 0x1C01},
{0x3E42, 0xFF84},
{0x3E44, 0x8401},
{0x3E46, 0x0C01},
{0x3E48, 0x8401},
{0x3E4A, 0x00FF},
{0x3E4C, 0x8402},
{0x3E4E, 0x8984},
{0x3E50, 0x6628},
{0x3E52, 0x8340},
{0x3E54, 0x00FF},
{0x3E56, 0x4A42},
{0x3E58, 0x2703},
{0x3E5A, 0x6752},
{0x3E5C, 0x3F2A},
{0x3E5E, 0x846A},
{0x3E60, 0x4C01},
{0x3E62, 0x8401},
{0x3E66, 0x3901},
{0x3E90, 0x2C01},
{0x3E98, 0x2B02},
{0x3E92, 0x2A04},
{0x3E94, 0x2509},
{0x3E96, 0x0000},
{0x3E9A, 0x2905},
{0x3E9C, 0x00FF},
{0x3ECC, 0x00EB},
{0x3ED0, 0x1E24},
{0x3ED4, 0xAFC4},
{0x3ED6, 0x909B},
{0x3EE0, 0x2424},
{0x3EE2, 0x9797},
{0x3EE4, 0xC100},
{0x3EE6, 0x0540},
/* mode end */
{0x3174, 0x8000},
/* [RAW10] */
{0x0112, 0x0A0A},
/* PLL Configuration Ext=24MHz */
{0x0300, 0x0004},
{0x0302, 0x0001},
{0x0304, 0x0002},
{0x0306, 0x0042},
{0x0308, 0x000A},
{0x030A, 0x0001},
{ar0832_TABLE_WAIT_MS, 0x0001},
/* Output size */
{0x0344, 0x04D8},
{0x0348, 0x07F7},
{0x0346, 0x03A4},
{0x034A, 0x05FB},
{0x034C, 0x0320},
{0x034E, 0x0258},
{0x3040, 0xC041},
{0x306E, 0xFC80},
{0x3178, 0x0000},
{0x3ED0, 0x1E24},
/* Scale Configuration */
{0x0400, 0x0000},
{0x0404, 0x0010},
/* Timing Configuration */
{0x0342, 0x08A8},
{0x0340, 0x02E7},
{0x0202, 0x02E7},
{0x3014, 0x03F6},
{0x3010, 0x0078},
{0x301A, 0x8250},
{0x301A, 0x8650},
{0x301A, 0x8658},
/* STATE= Minimum Gain, 1500 */
{0x305E, 0x13AF},
{0x0104, 0x0000},
{0x301A, 0x065C},
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_800X600_8141[] = {
/* mode start */
{0x301A, 0x0058}, /* RESET_REGISTER */
{0x301A, 0x0050}, /* RESET_REGISTER */
/* SC-CHANGE: to-do 8 bit write */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x3064, 0x7800}, /* RESERVED_MFR_3064 */
{0x31AE, 0x0202}, /* SERIAL_FORMAT */
/* AR0832 Recommended Settings */
{0x31B0, 0x0083}, /* FRAME_PREAMBLE */
{0x31B2, 0x004D}, /* LINE_PREAMBLE */
{0x31B4, 0x0E88}, /* MIPI_TIMING_0 */
{0x31B6, 0x0D24}, /* MIPI_TIMING_1 */
{0x31B8, 0x020E}, /* MIPI_TIMING_2 */
{0x31BA, 0x0710}, /* MIPI_TIMING_3 */
{0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */
{ar0832_TABLE_WAIT_MS, 0x0005},
{0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */
{0x3044, 0x0590}, /* RESERVED_MFR_3044 */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x30B2, 0xC000}, /* RESERVED_MFR_30B2 */
{0x30D6, 0x0800}, /* RESERVED_MFR_30D6 */
{0x316C, 0xB42F}, /* RESERVED_MFR_316C */
{0x316E, 0x869A}, /* RESERVED_MFR_316E */
{0x3170, 0x210E}, /* RESERVED_MFR_3170 */
{0x317A, 0x010E}, /* RESERVED_MFR_317A */
{0x31E0, 0x1FB9}, /* RESERVED_MFR_31E0 */
{0x31E6, 0x07FC}, /* RESERVED_MFR_31E6 */
{0x37C0, 0x0000}, /* P_GR_Q5 */
{0x37C2, 0x0000}, /* P_RD_Q5 */
{0x37C4, 0x0000}, /* P_BL_Q5 */
{0x37C6, 0x0000}, /* P_GB_Q5 */
{0x3E00, 0x0011}, /* RESERVED_MFR_3E00 */
{0x3E02, 0x8801}, /* RESERVED_MFR_3E02 */
{0x3E04, 0x2801}, /* RESERVED_MFR_3E04 */
{0x3E06, 0x8449}, /* RESERVED_MFR_3E06 */
{0x3E08, 0x6841}, /* RESERVED_MFR_3E08 */
{0x3E0A, 0x400C}, /* RESERVED_MFR_3E0A */
{0x3E0C, 0x1001}, /* RESERVED_MFR_3E0C */
{0x3E0E, 0x2603}, /* RESERVED_MFR_3E0E */
{0x3E10, 0x4B41}, /* RESERVED_MFR_3E10 */
{0x3E12, 0x4B24}, /* RESERVED_MFR_3E12 */
{0x3E14, 0xA3CF}, /* RESERVED_MFR_3E14 */
{0x3E16, 0x8802}, /* RESERVED_MFR_3E16 */
{0x3E18, 0x84FF}, /* RESERVED_MFR_3E18 */
{0x3E1A, 0x8601}, /* RESERVED_MFR_3E1A */
{0x3E1C, 0x8401}, /* RESERVED_MFR_3E1C */
{0x3E1E, 0x840A}, /* RESERVED_MFR_3E1E */
{0x3E20, 0xFF00}, /* RESERVED_MFR_3E20 */
{0x3E22, 0x8401}, /* RESERVED_MFR_3E22 */
{0x3E24, 0x00FF}, /* RESERVED_MFR_3E24 */
{0x3E26, 0x0088}, /* RESERVED_MFR_3E26 */
{0x3E28, 0x2E8A}, /* RESERVED_MFR_3E28 */
{0x3E30, 0x0000}, /* RESERVED_MFR_3E30 */
{0x3E32, 0x8801}, /* RESERVED_MFR_3E32 */
{0x3E34, 0x4029}, /* RESERVED_MFR_3E34 */
{0x3E36, 0x00FF}, /* RESERVED_MFR_3E36 */
{0x3E38, 0x8469}, /* RESERVED_MFR_3E38 */
{0x3E3A, 0x00FF}, /* RESERVED_MFR_3E3A */
{0x3E3C, 0x2801}, /* RESERVED_MFR_3E3C */
{0x3E3E, 0x3E2A}, /* RESERVED_MFR_3E3E */
{0x3E40, 0x1C01}, /* RESERVED_MFR_3E40 */
{0x3E42, 0xFF84}, /* RESERVED_MFR_3E42 */
{0x3E44, 0x8401}, /* RESERVED_MFR_3E44 */
{0x3E46, 0x0C01}, /* RESERVED_MFR_3E46 */
{0x3E48, 0x8401}, /* RESERVED_MFR_3E48 */
{0x3E4A, 0x00FF}, /* RESERVED_MFR_3E4A */
{0x3E4C, 0x8402}, /* RESERVED_MFR_3E4C */
{0x3E4E, 0x8984}, /* RESERVED_MFR_3E4E */
{0x3E50, 0x6628}, /* RESERVED_MFR_3E50 */
{0x3E52, 0x8340}, /* RESERVED_MFR_3E52 */
{0x3E54, 0x00FF}, /* RESERVED_MFR_3E54 */
{0x3E56, 0x4A42}, /* RESERVED_MFR_3E56 */
{0x3E58, 0x2703}, /* RESERVED_MFR_3E58 */
{0x3E5A, 0x6752}, /* RESERVED_MFR_3E5A */
{0x3E5C, 0x3F2A}, /* RESERVED_MFR_3E5C */
{0x3E5E, 0x846A}, /* RESERVED_MFR_3E5E */
{0x3E60, 0x4C01}, /* RESERVED_MFR_3E60 */
{0x3E62, 0x8401}, /* RESERVED_MFR_3E62 */
{0x3E66, 0x3901}, /* RESERVED_MFR_3E66 */
{0x3E90, 0x2C01}, /* RESERVED_MFR_3E90 */
{0x3E98, 0x2B02}, /* RESERVED_MFR_3E98 */
{0x3E92, 0x2A04}, /* RESERVED_MFR_3E92 */
{0x3E94, 0x2509}, /* RESERVED_MFR_3E94 */
{0x3E96, 0x0000}, /* RESERVED_MFR_3E96 */
{0x3E9A, 0x2905}, /* RESERVED_MFR_3E9A */
{0x3E9C, 0x00FF}, /* RESERVED_MFR_3E9C */
{0x3ECC, 0x00EB}, /* RESERVED_MFR_3ECC */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x3ED4, 0xAFC4}, /* RESERVED_MFR_3ED4 */
{0x3ED6, 0x909B}, /* RESERVED_MFR_3ED6 */
{0x3EE0, 0x2424}, /* RESERVED_MFR_3EE0 */
{0x3EE2, 0x9797}, /* RESERVED_MFR_3EE2 */
{0x3EE4, 0xC100}, /* RESERVED_MFR_3EE4 */
{0x3EE6, 0x0540}, /* RESERVED_MFR_3EE6 */
/* mode end */
{0x3174, 0x8000}, /* RESERVED_MFR_3174 */
{0x0300, 0x0004}, /* VT_PIX_CLK_DIV */
{0x0302, 0x0001}, /* VT_SYS_CLK_DIV */
{0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */
{0x0306, 0x0042}, /* PLL_MULTIPLIER */
{0x0308, 0x000A}, /* OP_PIX_CLK_DIV */
{0x030A, 0x0001}, /* OP_SYS_CLK_DIV */
{ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */
{0x3064, 0x7400}, /* RESERVED_MFR_3064 */
{0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */
{0x0344, 0x04D8}, /* X_ADDR_START */
{0x0348, 0x07F7}, /* X_ADDR_END */
{0x0346, 0x03A4}, /* Y_ADDR_START */
{0x034A, 0x05FB}, /* Y_ADDR_END */
{0x034C, 0x0320}, /* X_OUTPUT_SIZE */
{0x034E, 0x0260}, /* Y_OUTPUT_SIZE */
{0x3040, 0xC041}, /* READ_MODE */
{0x306E, 0xFC80}, /* DATAPATH_SELECT */
{0x3178, 0x0000}, /* RESERVED_MFR_3178 */
{0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */
{0x0400, 0x0000}, /* SCALING_MODE */
{0x0404, 0x0010}, /* SCALE_M */
{0x0342, 0x08A8}, /* LINE_LENGTH_PCK */
{0x0340, 0x02E7}, /* FRAME_LENGTH_LINES */
{0x0202, 0x02E7}, /* COARSE_INTEGRATION_TIME */
{0x3014, 0x03F6}, /* FINE_INTEGRATION_TIME */
{0x3010, 0x0078}, /* FINE_CORRECTION */
{0x301A, 0x8250}, /* RESET_REGISTER */
{0x301A, 0x8650}, /* RESET_REGISTER */
{0x301A, 0x8658}, /* RESET_REGISTER */
/* gain */
{0x305e, 0x10AA}, /* gain */
/* todo 8-bit write */
{0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */
{0x301A, 0x065C}, /* RESET_REGISTER */
{ar0832_TABLE_END, 0x0000}
};
static struct ar0832_reg mode_end[] = {
{ar0832_TABLE_END, 0x0000}
};
enum {
ar0832_MODE_3264X2448,
ar0832_MODE_2880X1620,
ar0832_MODE_1920X1080,
ar0832_MODE_1632X1224,
ar0832_MODE_800X600
};
static struct ar0832_reg *mode_table_8140[] = {
[ar0832_MODE_3264X2448] = mode_3264X2448_8140,
[ar0832_MODE_2880X1620] = mode_2880X1620_8140,
[ar0832_MODE_1920X1080] = mode_1920X1080_8140,
[ar0832_MODE_1632X1224] = mode_1632X1224_8140,
[ar0832_MODE_800X600] = mode_800X600_8140,
};
static struct ar0832_reg *mode_table_8141[] = {
[ar0832_MODE_3264X2448] = mode_3264X2448_8141,
[ar0832_MODE_2880X1620] = mode_2880X1620_8141,
[ar0832_MODE_1920X1080] = mode_1920X1080_8141,
[ar0832_MODE_1632X1224] = mode_1632X1224_8141,
[ar0832_MODE_800X600] = mode_800X600_8141,
};
static inline void ar0832_msleep(u32 t)
{
/*
why usleep_range() instead of msleep() ?
Read Documentation/timers/timers-howto.txt
*/
usleep_range(t*1000, t*1000 + 500);
}
/* 16 bit reg to program frame length */
static inline void ar0832_get_frame_length_regs(struct ar0832_reg *regs,
u32 frame_length)
{
regs->addr = 0x0340;
regs->val = (frame_length) & 0xFFFF;
}
static inline void ar0832_get_coarse_time_regs(struct ar0832_reg *regs,
u32 coarse_time)
{
regs->addr = 0x0202;
regs->val = (coarse_time) & 0xFFFF;
}
static inline void ar0832_get_focuser_vcm_control_regs(struct ar0832_reg *regs,
u16 value)
{
regs->addr = 0x30F0;
regs->val = (value) & 0xFFFF;
}
static inline void ar0832_get_focuser_vcm_step_time_regs
(struct ar0832_reg *regs, u16 value)
{
regs->addr = 0x30F4;
regs->val = (value) & 0xFFFF;
}
static inline void ar0832_get_focuser_data_regs(struct ar0832_reg *regs,
u16 value)
{
regs->addr = 0x30F2;
regs->val = (value) & 0xFFFF;
}
static inline void ar0832_get_gain_regs(struct ar0832_reg *regs, u16 gain)
{
/* global_gain register*/
regs->addr = AR0832_GLOBAL_GAIN_REG;
regs->val = gain;
}
static int ar0832_write_reg8(struct i2c_client *client, u16 addr, u8 val)
{
int err;
struct i2c_msg msg;
unsigned char data[3];
int retry = 0;
if (!client->adapter)
return -ENODEV;
data[0] = (u8) (addr >> 8);;
data[1] = (u8) (addr & 0xff);
data[2] = (u8) (val & 0xff);
msg.addr = client->addr;
msg.flags = 0;
msg.len = 3;
msg.buf = data;
dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val);
do {
err = i2c_transfer(client->adapter, &msg, 1);
if (err > 0)
return 0;
retry++;
dev_err(&client->dev,
"%s: i2c transfer failed, retrying %x %x\n",
__func__, addr, val);
ar0832_msleep(3);
} while (retry < ar0832_MAX_RETRIES);
return err;
}
static int ar0832_write_reg16(struct i2c_client *client, u16 addr, u16 val)
{
int count;
struct i2c_msg msg;
unsigned char data[4];
int retry = 0;
if (!client->adapter)
return -ENODEV;
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
data[2] = (u8) (val >> 8);
data[3] = (u8) (val & 0xff);
msg.addr = client->addr;
msg.flags = 0;
msg.len = 4;
msg.buf = data;
dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val);
do {
count = i2c_transfer(client->adapter, &msg, 1);
if (count == 1)
return 0;
retry++;
dev_err(&client->dev,
"%s: i2c transfer failed, retrying %x %x\n",
__func__, addr, val);
ar0832_msleep(3);
} while (retry <= ar0832_MAX_RETRIES);
return -EIO;
}
static int ar0832_read_reg16(struct i2c_client *client, u16 addr, u16 *val)
{
struct i2c_msg msg[2];
u8 data[4];
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = data;
data[0] = (addr >> 8);
data[1] = (addr & 0xff);
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = data + 2;
if (i2c_transfer(client->adapter, msg, 2) == 2) {
*val = ((data[2] << 8) | data[3]);
dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, *val);
return 0;
} else {
*val = 0;
dev_err(&client->dev,
"%s: i2c read failed.\n", __func__);
return -1;
}
}
static int ar0832_write_reg_helper(struct ar0832_dev *dev,
u16 addr,
u16 val)
{
int ret;
if (addr == 0x104)
ret = ar0832_write_reg8(dev->i2c_client, addr,
(val >> 8 & 0xff));
else
ret = ar0832_write_reg16(dev->i2c_client, addr, val);
return ret;
}
static int ar0832_write_table(struct ar0832_dev *dev,
const struct ar0832_reg table[],
const struct ar0832_reg override_list[],
int num_override_regs)
{
int err;
const struct ar0832_reg *next;
u16 val;
int i;
for (next = table; next->addr != ar0832_TABLE_END; next++) {
if (next->addr == ar0832_TABLE_WAIT_MS) {
ar0832_msleep(next->val);
continue;
}
val = next->val;
/* When an override list is passed in, replace the reg */
/* value to write if the reg is in the list */
if (override_list) {
for (i = 0; i < num_override_regs; i++) {
if (next->addr == override_list[i].addr) {
val = override_list[i].val;
break;
}
}
}
err = ar0832_write_reg_helper(dev, next->addr, val);
if (err)
return err;
}
return 0;
}
static int ar0832_set_frame_length(struct ar0832_dev *dev,
u32 frame_length)
{
struct ar0832_reg reg_list;
struct i2c_client *i2c_client = dev->i2c_client;
int ret;
dev_dbg(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, frame_length);
ar0832_get_frame_length_regs(®_list, frame_length);
ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1);
if (ret)
return ret;
ret = ar0832_write_reg16(i2c_client, reg_list.addr,
reg_list.val);
if (ret)
return ret;
ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0);
if (ret)
return ret;
return 0;
}
static int ar0832_set_coarse_time(struct ar0832_dev *dev,
u32 coarse_time)
{
int ret;
struct ar0832_reg reg_list;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "[%s] (0x%08x)\n", __func__, coarse_time);
ar0832_get_coarse_time_regs(®_list, coarse_time);
ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1);
if (ret)
return ret;
ret = ar0832_write_reg16(i2c_client, reg_list.addr,
reg_list.val);
if (ret)
return ret;
ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0);
if (ret)
return ret;
return 0;
}
static int ar0832_set_gain(struct ar0832_dev *dev, u16 gain)
{
int ret = 0;
struct ar0832_reg reg_list_gain;
ret = ar0832_write_reg8(dev->i2c_client, AR0832_GROUP_HOLD_REG, 0x1);
/* Gain Registers Start */
ar0832_get_gain_regs(®_list_gain, gain);
ret |= ar0832_write_reg16(dev->i2c_client,
reg_list_gain.addr,
reg_list_gain.val);
if (ret)
return ret;
/* Gain register End */
ret |= ar0832_write_reg8(dev->i2c_client, AR0832_GROUP_HOLD_REG, 0x0);
return ret;
}
static int ar0832_set_mode(struct ar0832_dev *dev,
struct ar0832_mode *mode)
{
int sensor_mode;
int err;
struct i2c_client *i2c_client = dev->i2c_client;
struct ar0832_reg reg_ovr[3];
struct ar0832_reg *mode_seq;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (mode->xres == 3264 && mode->yres == 2448)
sensor_mode = ar0832_MODE_3264X2448;
else if (mode->xres == 2880 && mode->yres == 1620)
sensor_mode = ar0832_MODE_2880X1620;
else if (mode->xres == 1920 && mode->yres == 1080)
sensor_mode = ar0832_MODE_1920X1080;
else if (mode->xres == 1632 && mode->yres == 1224)
sensor_mode = ar0832_MODE_1632X1224;
else if (mode->xres == 800 && mode->yres == 600)
sensor_mode = ar0832_MODE_800X600;
else {
dev_err(&i2c_client->dev,
"%s: invalid resolution supplied to set mode %d %d\n",
__func__ , mode->xres, mode->yres);
return -EINVAL;
}
if (dev->sensor_id_data == AR0832_SENSOR_ID_8141)
mode_seq = mode_table_8141[sensor_mode];
else
mode_seq = mode_table_8140[sensor_mode];
/* get a list of override regs for the asking frame length, */
/* coarse integration time, and gain.*/
err = ar0832_write_table(dev, mode_start, NULL, 0);
if (err)
return err;
/* When we change the resolution */
ar0832_get_frame_length_regs(®_ovr[0], mode->frame_length);
ar0832_get_coarse_time_regs(®_ovr[1], mode->coarse_time);
ar0832_get_gain_regs(®_ovr[2], mode->gain);
err = ar0832_write_table(dev, mode_seq, reg_ovr, ARRAY_SIZE(reg_ovr));
if (err)
return err;
err = ar0832_write_table(dev, mode_end, NULL, 0);
if (err)
return err;
dev->sensor_info->mode = sensor_mode;
dev_dbg(&i2c_client->dev, "%s: --\n", __func__);
return 0;
}
static int ar0832_get_status(struct ar0832_dev *dev, u8 *status)
{
int err = 0;
struct i2c_client *i2c_client = dev->i2c_client;
*status = 0;
/* FixMe */
/*
err = ar0832_read_reg(dev->i2c_client, 0x001, status);
*/
dev_dbg(&i2c_client->dev, "%s: %u %d\n", __func__, *status, err);
return err;
}
static int ar0832_set_alternate_addr(struct i2c_client *client)
{
int ret = 0;
u8 new_addr = client->addr;
u16 val;
/* Default slave address of ar0832 is 0x36 */
client->addr = 0x36;
ret = ar0832_read_reg16(client, AR0832_RESET_REG, &val);
val &= ~AR0832_RESET_REG_LOCK_REG;
ret |= ar0832_write_reg16(client, AR0832_RESET_REG, val);
ret |= ar0832_write_reg16(client, AR0832_ID_REG, new_addr << 1);
if (!ret) {
client->addr = new_addr;
dev_dbg(&client->dev,
"new slave address is set to 0x%x\n", new_addr);
}
ret |= ar0832_read_reg16(client, AR0832_RESET_REG, &val);
val |= AR0832_RESET_REG_LOCK_REG;
ret |= ar0832_write_reg16(client, AR0832_RESET_REG, val);
return ret;
}
static int ar0832_power_on(struct ar0832_dev *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
int ret = 0;
dev_dbg(&i2c_client->dev, "%s: ++ %d %d\n",
__func__, dev->is_stereo,
dev->brd_power_cnt);
/* Board specific power-on sequence */
mutex_lock(&dev->ar0832_camera_lock);
if (dev->brd_power_cnt == 0) {
/* Plug 1.8V and 2.8V power to sensor */
if (dev->power_rail.sen_1v8_reg) {
ret = regulator_enable(dev->power_rail.sen_1v8_reg);
if (ret) {
dev_err(&i2c_client->dev,
"%s: failed to enable vdd\n",
__func__);
goto fail_regulator_1v8_reg;
}
}
if (dev->power_rail.sen_2v8_reg) {
ret = regulator_enable(dev->power_rail.sen_2v8_reg);
if (ret) {
dev_err(&i2c_client->dev,
"%s: failed to enable vaa\n",
__func__);
goto fail_regulator_2v8_reg;
}
}
dev->pdata->power_on(dev->is_stereo);
}
dev->brd_power_cnt++;
mutex_unlock(&dev->ar0832_camera_lock);
/* Change slave address */
if (i2c_client->addr)
ret = ar0832_set_alternate_addr(i2c_client);
return 0;
fail_regulator_2v8_reg:
regulator_put(dev->power_rail.sen_2v8_reg);
dev->power_rail.sen_2v8_reg = NULL;
regulator_disable(dev->power_rail.sen_1v8_reg);
fail_regulator_1v8_reg:
regulator_put(dev->power_rail.sen_1v8_reg);
dev->power_rail.sen_1v8_reg = NULL;
return ret;
}
static void ar0832_power_off(struct ar0832_dev *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++ %d\n", __func__, dev->brd_power_cnt);
/* Board specific power-down sequence */
mutex_lock(&dev->ar0832_camera_lock);
if (dev->brd_power_cnt <= 0)
goto ar0832_pwdn_exit;
if (dev->brd_power_cnt-- == 1) {
/* Unplug 1.8V and 2.8V power from sensor */
if (dev->power_rail.sen_2v8_reg)
regulator_disable(dev->power_rail.sen_2v8_reg);
if (dev->power_rail.sen_1v8_reg)
regulator_disable(dev->power_rail.sen_1v8_reg);
dev->pdata->power_off(dev->is_stereo);
}
ar0832_pwdn_exit:
mutex_unlock(&dev->ar0832_camera_lock);
}
static int ar0832_focuser_set_config(struct ar0832_dev *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
struct ar0832_reg reg_vcm_ctrl, reg_vcm_step_time;
int ret = 0;
u8 vcm_slew = 1;
/* bit15(0x80) means that VCM driver enable bit. */
/* bit3(0x08) means that keep VCM(AF position) */
/* while sensor is in soft standby mode during mode transitions. */
u16 vcm_control_data = (0x80 << 8 | (0x08 | (vcm_slew & 0x07)));
u16 vcm_step_time = 1024;
ar0832_get_focuser_vcm_control_regs(®_vcm_ctrl, vcm_control_data);
ret = ar0832_write_reg16(dev->i2c_client, reg_vcm_ctrl.addr,
reg_vcm_ctrl.val);
dev_dbg(&i2c_client->dev, "%s Reg 0x%X Value 0x%X\n", __func__,
reg_vcm_ctrl.addr, reg_vcm_ctrl.val);
if (ret) {
dev_dbg(&i2c_client->dev, "%s Error writing to register 0x%X\n",
__func__, reg_vcm_ctrl.addr);
return ret;
}
ar0832_get_focuser_vcm_step_time_regs(®_vcm_step_time,
vcm_step_time);
ret = ar0832_write_reg16(dev->i2c_client, reg_vcm_step_time.addr,
reg_vcm_step_time.val);
dev_dbg(&i2c_client->dev, "%s Reg step_time 0x%X Value 0x%X\n",
__func__, reg_vcm_step_time.addr,
reg_vcm_step_time.val);
return ret;
}
static int ar0832_focuser_set_position(struct ar0832_dev *dev,
u32 position)
{
int ret = 0;
struct ar0832_reg reg_data;
ar0832_get_focuser_data_regs(®_data, position);
ret = ar0832_write_reg16(dev->i2c_client, reg_data.addr,
reg_data.val);
dev->focuser_info->last_position = position;
return ret;
}
/*
* This function is not currently called as we have the hardcoded
* step time in ar0832_focuser_set_config function. If we need to
* compute the actual step time based on a number of clocks, we need
* to use this function. The formula for computing the clock-based
* step time is obtained from Aptina and is not part of external
* documentation and hence this code needs to be saved.
*/
static u16 ar0832_get_focuser_vcm_step_time(struct ar0832_dev *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
int ret;
u16 pll_multiplier = 0;
u16 pre_pll_clk_div = 0;
u16 vt_sys_clk_div = 0;
u16 vt_pix_clk_div = 0;
u16 vt_pix_clk_freq_mhz = 0;
ret = ar0832_read_reg16(dev->i2c_client, 0x306, &pll_multiplier);
if (ret) {
dev_err(&i2c_client->dev, "%s pll_multiplier read failed\n",
__func__);
}
ret = ar0832_read_reg16(dev->i2c_client, 0x304, &pre_pll_clk_div);
if (ret) {
dev_err(&i2c_client->dev, "%s pre_pll_clk_div read failed\n",
__func__);
}
ret = ar0832_read_reg16(dev->i2c_client, 0x302, &vt_sys_clk_div);
if (ret) {
dev_err(&i2c_client->dev, "%s vt_sys_clk_div read failed\n",
__func__);
}
ret = ar0832_read_reg16(dev->i2c_client, 0x300, &vt_pix_clk_div);
if (ret) {
dev_err(&i2c_client->dev, "%s vt_pix_clk_div read failed\n",
__func__);
}
vt_pix_clk_freq_mhz =
(24 * pll_multiplier) / (pre_pll_clk_div * vt_sys_clk_div *
vt_pix_clk_div);
dev_dbg(&i2c_client->dev, "%s pll_multiplier 0x%X pre_pll_clk_div 0x%X "
"vt_sys_clk_div 0x%X vt_pix_clk_div 0x%X vt_pix_clk_freq_mhz 0x%X\n",
__func__, pll_multiplier,
pre_pll_clk_div, vt_sys_clk_div,
vt_pix_clk_div, vt_pix_clk_freq_mhz);
return vt_pix_clk_freq_mhz;
}
static inline
int ar0832_get_sensorid(struct ar0832_dev *dev, u16 *sensor_id)
{
int ret;
struct i2c_client *i2c_client = dev->i2c_client;
ret = ar0832_power_on(dev);
if (ret)
return ret;
ret = ar0832_read_reg16(i2c_client, AR0832_SENSORID_REG, sensor_id);
dev_dbg(&i2c_client->dev,
"%s: sensor_id - %04x\n", __func__, *sensor_id);
ar0832_power_off(dev);
return ret;
}
static long ar0832_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err;
struct ar0832_dev *dev = file->private_data;
struct i2c_client *i2c_client = dev->i2c_client;
struct ar0832_mode mode;
u16 pos;
switch (cmd) {
case AR0832_IOCTL_SET_POWER_ON:
dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_POWER_ON\n");
if (copy_from_user(&mode,
(const void __user *)arg,
sizeof(struct ar0832_mode))) {
dev_err(&i2c_client->dev,
"%s: AR0832_IOCTL_SET_POWER_ON failed\n",
__func__);
return -EFAULT;
}
dev->is_stereo = mode.stereo;
return ar0832_power_on(dev);
case AR0832_IOCTL_SET_MODE:
{
dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_MODE\n");
if (copy_from_user(&mode,
(const void __user *)arg,
sizeof(struct ar0832_mode))) {
dev_err(&i2c_client->dev,
"%s: AR0832_IOCTL_SET_MODE failed\n",
__func__);
return -EFAULT;
}
mutex_lock(&dev->ar0832_camera_lock);
err = ar0832_set_mode(dev, &mode);
/*
* We need to re-initialize the Focuser registers during mode
* switch due to the known issue of focuser retracting
*/
ar0832_focuser_set_config(dev);
dev->focuser_info->focuser_init_flag = true;
/*
* If the last focuser position is not at infinity when we
* did the mode switch, we need to go there. Before that,
* we need to come back to 0.
*/
if (dev->focuser_info->last_position > 0) {
pos = dev->focuser_info->last_position;
dev_dbg(&i2c_client->dev, "%s: AR0832_IOCTL_SET_MODE: "
" Move to 0, restore the backedup focuser position of %d\n",
__func__, pos);
ar0832_focuser_set_position(dev, 0);
ar0832_msleep(10);
ar0832_focuser_set_position(dev, pos);
ar0832_msleep(10);
}
mutex_unlock(&dev->ar0832_camera_lock);
return err;
}
case AR0832_IOCTL_SET_FRAME_LENGTH:
mutex_lock(&dev->ar0832_camera_lock);
err = ar0832_set_frame_length(dev, (u32)arg);
mutex_unlock(&dev->ar0832_camera_lock);
return err;
case AR0832_IOCTL_SET_COARSE_TIME:
mutex_lock(&dev->ar0832_camera_lock);
err = ar0832_set_coarse_time(dev, (u32)arg);
mutex_unlock(&dev->ar0832_camera_lock);
return err;
case AR0832_IOCTL_SET_GAIN:
mutex_lock(&dev->ar0832_camera_lock);
err = ar0832_set_gain(dev, (u16)arg);
mutex_unlock(&dev->ar0832_camera_lock);
return err;
case AR0832_IOCTL_GET_STATUS:
{
u8 status;
dev_dbg(&i2c_client->dev, "AR0832_IOCTL_GET_STATUS\n");
err = ar0832_get_status(dev, &status);
if (err)
return err;
if (copy_to_user((void __user *)arg, &status,
2)) {
dev_err(&i2c_client->dev,
"%s: AR0832_IOCTL_GET_STATUS failed\n",
__func__);
return -EFAULT;
}
return 0;
}
case AR0832_IOCTL_SET_SENSOR_REGION:
{
struct ar0832_stereo_region region;
dev_dbg(&i2c_client->dev, "AR0832_IOCTL_SET_SENSOR_REGION\n");
/* Right now, it doesn't do anything */
return 0;
}
case AR0832_FOCUSER_IOCTL_GET_CONFIG:
dev_dbg(&i2c_client->dev,
"%s AR0832_FOCUSER_IOCTL_GET_CONFIG\n", __func__);
if (copy_to_user((void __user *) arg,
&dev->focuser_info->config,
sizeof(dev->focuser_info->config))) {
dev_err(&i2c_client->dev,
"%s: AR0832_FOCUSER_IOCTL_GET_CONFIG failed\n",
__func__);
return -EFAULT;
}
return 0;
case AR0832_FOCUSER_IOCTL_SET_POSITION:
dev_dbg(&i2c_client->dev,
"%s AR0832_FOCUSER_IOCTL_SET_POSITION\n", __func__);
mutex_lock(&dev->ar0832_camera_lock);
if (dev->focuser_info->focuser_init_flag == false) {
ar0832_focuser_set_config(dev);
dev->focuser_info->focuser_init_flag = true;
}
err = ar0832_focuser_set_position(dev, (u32)arg);
mutex_unlock(&dev->ar0832_camera_lock);
return err;
case AR0832_IOCTL_GET_SENSOR_ID:
dev_dbg(&i2c_client->dev,
"%s AR0832_IOCTL_GET_SENSOR_ID\n", __func__);
if (!dev->sensor_id_data) {
err = ar0832_get_sensorid(dev, &dev->sensor_id_data);
if (err) {
dev_err(&i2c_client->dev,
"%s: failed to get sensor id\n",
__func__);
return -EFAULT;
}
}
if (copy_to_user((void __user *) arg,
&dev->sensor_id_data,
sizeof(dev->sensor_id_data))) {
dev_err(&i2c_client->dev,
"%s: AR0832_IOCTL_GET_SENSOR_ID failed\n",
__func__);
return -EFAULT;
}
return 0;
default:
dev_err(&i2c_client->dev, "(error) %s NONE IOCTL\n",
__func__);
return -EINVAL;
}
return 0;
}
static int ar0832_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct ar0832_dev *dev = dev_get_drvdata(miscdev->parent);
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (atomic_xchg(&dev->in_use, 1))
return -EBUSY;
dev->focuser_info->focuser_init_flag = false;
file->private_data = dev;
return 0;
}
static int ar0832_release(struct inode *inode, struct file *file)
{
struct ar0832_dev *dev = file->private_data;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
ar0832_power_off(dev);
file->private_data = NULL;
dev->focuser_info->focuser_init_flag = false;
WARN_ON(!atomic_xchg(&dev->in_use, 0));
return 0;
}
static const struct file_operations ar0832_fileops = {
.owner = THIS_MODULE,
.open = ar0832_open,
.unlocked_ioctl = ar0832_ioctl,
.release = ar0832_release,
};
static int ar0832_debugfs_show(struct seq_file *s, void *unused)
{
struct ar0832_dev *dev = s->private;
struct i2c_client *i2c_client = dev->i2c_client;
int ret;
u16 test_pattern_reg;
dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__);
if (!dev->brd_power_cnt) {
dev_info(&i2c_client->dev,
"%s: camera is off\n", __func__);
return 0;
}
mutex_lock(&dev->ar0832_camera_lock);
ret = ar0832_read_reg16(i2c_client,
AR0832_TEST_PATTERN_REG, &test_pattern_reg);
mutex_unlock(&dev->ar0832_camera_lock);
if (ret) {
dev_err(&i2c_client->dev,
"%s: test pattern write failed\n", __func__);
return -EFAULT;
}
seq_printf(s, "%d\n", test_pattern_reg);
return 0;
}
static ssize_t ar0832_debugfs_write(
struct file *file,
char const __user *buf,
size_t count,
loff_t *offset)
{
struct ar0832_dev *dev = ((struct seq_file *)file->private_data)->private;
struct i2c_client *i2c_client = dev->i2c_client;
int ret = 0;
char buffer[10];
u16 input, val, red = 0, green = 0, blue = 0;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (!dev->brd_power_cnt) {
dev_info(&i2c_client->dev,
"%s: camera is off\n", __func__);
return count;
}
if (copy_from_user(&buffer, buf, sizeof(buffer)))
goto debugfs_write_fail;
input = (u16)simple_strtoul(buffer, NULL, 10);
mutex_lock(&dev->ar0832_camera_lock);
ret = ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x1);
switch (input) {
case 1: /* color bar */
val = 2;
break;
case 2: /* Red */
val = 1;
red = 0x300; /* 10 bit value */
green = 0;
blue = 0;
break;
case 3: /* Green */
val = 1;
red = 0;
green = 0x300; /* 10 bit value */
blue = 0;
break;
case 4: /* Blue */
val = 1;
red = 0;
green = 0;
blue = 0x300; /* 10 bit value */
break;
default:
val = 0;
break;
}
if (input == 2 || input == 3 || input == 4) {
ret |= ar0832_write_reg_helper(dev,
AR0832_TEST_RED_REG, red);
ret |= ar0832_write_reg_helper(dev,
AR0832_TEST_GREENR_REG, green);
ret |= ar0832_write_reg_helper(dev,
AR0832_TEST_GREENR_REG, green);
ret |= ar0832_write_reg_helper(dev,
AR0832_TEST_BLUE_REG, blue);
}
ret |= ar0832_write_reg_helper(dev, AR0832_TEST_PATTERN_REG, val);
ret |= ar0832_write_reg8(i2c_client, AR0832_GROUP_HOLD_REG, 0x0);
mutex_unlock(&dev->ar0832_camera_lock);
if (ret)
goto debugfs_write_fail;
return count;
debugfs_write_fail:
dev_err(&i2c_client->dev,
"%s: test pattern write failed\n", __func__);
return -EFAULT;
}
static int ar0832_debugfs_open(struct inode *inode, struct file *file)
{
struct ar0832_dev *dev = inode->i_private;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
return single_open(file, ar0832_debugfs_show, inode->i_private);
}
static const struct file_operations ar0832_debugfs_fops = {
.open = ar0832_debugfs_open,
.read = seq_read,
.write = ar0832_debugfs_write,
.llseek = seq_lseek,
.release = single_release,
};
static void __devexit ar0832_remove_debugfs(struct ar0832_dev *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (dev->debugdir)
debugfs_remove_recursive(dev->debugdir);
dev->debugdir = NULL;
}
static void ar0832_create_debugfs(struct ar0832_dev *dev)
{
struct dentry *ret;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s\n", __func__);
dev->debugdir = debugfs_create_dir(dev->dname, NULL);
if (!dev->debugdir)
goto remove_debugfs;
ret = debugfs_create_file("test_pattern",
S_IWUGO | S_IRUGO,
dev->debugdir, dev,
&ar0832_debugfs_fops);
if (!ret)
goto remove_debugfs;
return;
remove_debugfs:
dev_err(&i2c_client->dev, "couldn't create debugfs\n");
ar0832_remove_debugfs(dev);
}
static int ar0832_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct ar0832_dev *dev = NULL;
int ret;
dev_info(&client->dev, "ar0832: probing sensor.(id:%s)\n",
id->name);
dev = kzalloc(sizeof(struct ar0832_dev), GFP_KERNEL);
if (!dev)
goto probe_fail_release;
dev->sensor_info = kzalloc(sizeof(struct ar0832_sensor_info),
GFP_KERNEL);
if (!dev->sensor_info)
goto probe_fail_release;
dev->focuser_info = kzalloc(sizeof(struct ar0832_focuser_info),
GFP_KERNEL);
if (!dev->focuser_info)
goto probe_fail_release;
/* sensor */
dev->pdata = client->dev.platform_data;
dev->i2c_client = client;
/* focuser */
dev->focuser_info->config.settle_time = SETTLETIME_MS;
dev->focuser_info->config.pos_low = POS_LOW;
dev->focuser_info->config.pos_high = POS_HIGH;
snprintf(dev->dname, sizeof(dev->dname), "%s-%s",
id->name, dev->pdata->id);
dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
dev->misc_dev.name = dev->dname;
dev->misc_dev.fops = &ar0832_fileops;
dev->misc_dev.mode = S_IRWXUGO;
dev->misc_dev.parent = &client->dev;
err = misc_register(&dev->misc_dev);
if (err) {
dev_err(&client->dev, "Unable to register misc device!\n");
ret = -ENOMEM;
goto probe_fail_free;
}
i2c_set_clientdata(client, dev);
mutex_init(&dev->ar0832_camera_lock);
dev->power_rail.sen_1v8_reg = regulator_get(&client->dev, "vdd");
if (IS_ERR_OR_NULL(dev->power_rail.sen_1v8_reg)) {
dev_err(&client->dev, "%s: failed to get vdd\n",
__func__);
ret = PTR_ERR(dev->power_rail.sen_1v8_reg);
goto probe_fail_free;
}
dev->power_rail.sen_2v8_reg = regulator_get(&client->dev, "vaa");
if (IS_ERR_OR_NULL(dev->power_rail.sen_2v8_reg)) {
dev_err(&client->dev, "%s: failed to get vaa\n",
__func__);
ret = PTR_ERR(dev->power_rail.sen_2v8_reg);
regulator_put(dev->power_rail.sen_1v8_reg);
dev->power_rail.sen_1v8_reg = NULL;
goto probe_fail_free;
}
/* create debugfs interface */
ar0832_create_debugfs(dev);
return 0;
probe_fail_release:
dev_err(&client->dev, "%s: unable to allocate memory!\n", __func__);
ret = -ENOMEM;
probe_fail_free:
if (dev) {
kfree(dev->focuser_info);
kfree(dev->sensor_info);
}
kfree(dev);
return ret;
}
static int ar0832_remove(struct i2c_client *client)
{
struct ar0832_dev *dev = i2c_get_clientdata(client);
if (dev->power_rail.sen_1v8_reg)
regulator_put(dev->power_rail.sen_1v8_reg);
if (dev->power_rail.sen_2v8_reg)
regulator_put(dev->power_rail.sen_2v8_reg);
misc_deregister(&dev->misc_dev);
if (dev) {
kfree(dev->sensor_info);
kfree(dev->focuser_info);
}
ar0832_remove_debugfs(dev);
kfree(dev);
return 0;
}
static const struct i2c_device_id ar0832_id[] = {
{ "ar0832", 0 },
{ }
};
static struct i2c_driver ar0832_i2c_driver = {
.probe = ar0832_probe,
.remove = ar0832_remove,
.id_table = ar0832_id,
.driver = {
.name = "ar0832",
.owner = THIS_MODULE,
},
};
static int __init ar0832_init(void)
{
pr_info("%s: ++\n", __func__);
return i2c_add_driver(&ar0832_i2c_driver);
}
static void __exit ar0832_exit(void)
{
i2c_del_driver(&ar0832_i2c_driver);
}
module_init(ar0832_init);
module_exit(ar0832_exit);