blob: e890f2ffb126e207601266fd09f38fc1fab20ad8 [file] [log] [blame]
/*
* imx135.c - imx135 sensor driver
*
* Copyright (c) 2013, NVIDIA CORPORATION, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/regulator/consumer.h>
#include <media/imx135.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include "nvc_utilities.h"
struct imx135_reg {
u16 addr;
u8 val;
};
struct imx135_info {
struct miscdevice miscdev_info;
int mode;
struct imx135_power_rail power;
struct imx135_sensordata sensor_data;
struct i2c_client *i2c_client;
struct imx135_platform_data *pdata;
struct clk *mclk;
struct mutex imx135_camera_lock;
struct dentry *debugdir;
atomic_t in_use;
};
#define IMX135_TABLE_WAIT_MS 0
#define IMX135_TABLE_END 1
#define IMX135_MAX_RETRIES 3
#define IMX135_WAIT_MS 3
#define IMX135_4208x3120_HDR
#define MAX_BUFFER_SIZE 32
#define IMX135_FRAME_LENGTH_ADDR_MSB 0x0340
#define IMX135_FRAME_LENGTH_ADDR_LSB 0x0341
#define IMX135_COARSE_TIME_ADDR_MSB 0x0202
#define IMX135_COARSE_TIME_ADDR_LSB 0x0203
#define IMX135_COARSE_TIME_SHORT_ADDR_MSB 0x0230
#define IMX135_COARSE_TIME_SHORT_ADDR_LSB 0x0231
#define IMX135_GAIN_ADDR 0x0205
#define IMX135_GAIN_SHORT_ADDR 0x0233
#ifdef IMX135_4208x3120_HDR
/* HDR */
static struct imx135_reg mode_4208x3120[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3873, 0x03},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x00},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x01},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x11},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0E},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x00},
{0x0391, 0x11},
{0x0392, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x4082, 0x01},
{0x4083, 0x01},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0C},
{0x0341, 0xD0},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x00},
{0x0345, 0x00},
{0x0346, 0x00},
{0x0347, 0x00},
{0x0348, 0x10},
{0x0349, 0x6F},
{0x034A, 0x0C},
{0x034B, 0x2F},
{0x034C, 0x10},
{0x034D, 0x70},
{0x034E, 0x0C},
{0x034F, 0x30},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x10},
{0x0355, 0x70},
{0x0356, 0x0C},
{0x0357, 0x30},
{0x301D, 0x30},
{0x3310, 0x10},
{0x3311, 0x70},
{0x3312, 0x0C},
{0x3313, 0x30},
{0x331C, 0x01},
{0x331D, 0x68},
{0x4084, 0x00},
{0x4085, 0x00},
{0x4086, 0x00},
{0x4087, 0x00},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x87},
{0x0831, 0x3F},
{0x0832, 0x67},
{0x0833, 0x3F},
{0x0834, 0x3F},
{0x0835, 0x4F},
{0x0836, 0xDF},
{0x0837, 0x47},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0C},
{0x0203, 0xCC},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x10},
{0x33B1, 0x70},
{0x33B3, 0x01},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
#else
/* standard */
static struct imx135_reg mode_4208x3120[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x01},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x01},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x11},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0A},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x00},
{0x0391, 0x11},
{0x0392, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x4082, 0x01},
{0x4083, 0x01},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0C},
{0x0341, 0xD0},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x00},
{0x0345, 0x00},
{0x0346, 0x00},
{0x0347, 0x00},
{0x0348, 0x10},
{0x0349, 0x6F},
{0x034A, 0x0C},
{0x034B, 0x2F},
{0x034C, 0x10},
{0x034D, 0x70},
{0x034E, 0x0C},
{0x034F, 0x30},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x10},
{0x0355, 0x70},
{0x0356, 0x0C},
{0x0357, 0x30},
{0x301D, 0x30},
{0x3310, 0x10},
{0x3311, 0x70},
{0x3312, 0x0C},
{0x3313, 0x30},
{0x331C, 0x01},
{0x331D, 0x68},
{0x4084, 0x00},
{0x4085, 0x00},
{0x4086, 0x00},
{0x4087, 0x00},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x87},
{0x0831, 0x3F},
{0x0832, 0x67},
{0x0833, 0x3F},
{0x0834, 0x3F},
{0x0835, 0x4F},
{0x0836, 0xDF},
{0x0837, 0x47},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0C},
{0x0203, 0xCC},
/* Gain Setting */
{0x0205, 0x04},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x04},
{0x33B1, 0x00},
{0x33B3, 0x00},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
#endif
static struct imx135_reg mode_1920x1080[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x01},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x02},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x12},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0A},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x01},
{0x0391, 0x22},
{0x0392, 0x00},
{0x0401, 0x02},
{0x0404, 0x00},
{0x0405, 0x11},
{0x4082, 0x00},
{0x4083, 0x00},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0A},
{0x0341, 0x40},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x00},
{0x0345, 0x40},
{0x0346, 0x01},
{0x0347, 0x9C},
{0x0348, 0x10},
{0x0349, 0x2F},
{0x034A, 0x0A},
{0x034B, 0x93},
{0x034C, 0x07},
{0x034D, 0x80},
{0x034E, 0x04},
{0x034F, 0x38},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x07},
{0x0355, 0xF8},
{0x0356, 0x04},
{0x0357, 0x7C},
{0x301D, 0x30},
{0x3310, 0x07},
{0x3311, 0x80},
{0x3312, 0x04},
{0x3313, 0x38},
{0x331C, 0x00},
{0x331D, 0xD2},
{0x4084, 0x07},
{0x4085, 0x80},
{0x4086, 0x04},
{0x4087, 0x38},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x67},
{0x0831, 0x27},
{0x0832, 0x47},
{0x0833, 0x27},
{0x0834, 0x27},
{0x0835, 0x1F},
{0x0836, 0x87},
{0x0837, 0x2F},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0A},
{0x0203, 0x3C},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x04},
{0x33B1, 0x00},
{0x33B3, 0x00},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
#ifdef IMX135_1280x720_90_FPS
/* 720p 90fps */
static struct imx135_reg mode_1280x720[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x01},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x02},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x12},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0A},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x01},
{0x0391, 0x22},
{0x0392, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x4082, 0x01},
{0x4083, 0x01},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x03},
{0x0341, 0x6A},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x03},
{0x0345, 0x38},
{0x0346, 0x03},
{0x0347, 0x48},
{0x0348, 0x0D},
{0x0349, 0x37},
{0x034A, 0x08},
{0x034B, 0xE7},
{0x034C, 0x05},
{0x034D, 0x00},
{0x034E, 0x02},
{0x034F, 0xD0},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x05},
{0x0355, 0x00},
{0x0356, 0x02},
{0x0357, 0xD0},
{0x301D, 0x30},
{0x3310, 0x05},
{0x3311, 0x00},
{0x3312, 0x02},
{0x3313, 0xD0},
{0x331C, 0x00},
{0x331D, 0x10},
{0x4084, 0x00},
{0x4085, 0x00},
{0x4086, 0x00},
{0x4087, 0x00},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x67},
{0x0831, 0x27},
{0x0832, 0x47},
{0x0833, 0x27},
{0x0834, 0x27},
{0x0835, 0x1F},
{0x0836, 0x87},
{0x0837, 0x2F},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x03},
{0x0203, 0x66},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x04},
{0x33B1, 0x00},
{0x33B3, 0x00},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
#else
/* 720p 30fps */
static struct imx135_reg mode_1280x720[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x01},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x02},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x12},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0A},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x01},
{0x0391, 0x22},
{0x0392, 0x00},
{0x0401, 0x02},
{0x0404, 0x00},
{0x0405, 0x1A},
{0x4082, 0x00},
{0x4083, 0x00},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0A},
{0x0341, 0x40},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x00},
{0x0345, 0x18},
{0x0346, 0x01},
{0x0347, 0x88},
{0x0348, 0x10},
{0x0349, 0x57},
{0x034A, 0x0A},
{0x034B, 0xAB},
{0x034C, 0x05},
{0x034D, 0x00},
{0x034E, 0x02},
{0x034F, 0xD0},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x08},
{0x0355, 0x20},
{0x0356, 0x04},
{0x0357, 0x92},
{0x301D, 0x30},
{0x3310, 0x05},
{0x3311, 0x00},
{0x3312, 0x02},
{0x3313, 0xD0},
{0x331C, 0x02},
{0x331D, 0x18},
{0x4084, 0x05},
{0x4085, 0x00},
{0x4086, 0x02},
{0x4087, 0xD0},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x67},
{0x0831, 0x27},
{0x0832, 0x47},
{0x0833, 0x27},
{0x0834, 0x27},
{0x0835, 0x1F},
{0x0836, 0x87},
{0x0837, 0x2F},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0A},
{0x0203, 0x3C},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x04},
{0x33B1, 0x00},
{0x33B3, 0x00},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
#endif
static struct imx135_reg mode_2616x1472[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3873, 0x03},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x00},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x01},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x11},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0E},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x00},
{0x0391, 0x11},
{0x0392, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x4082, 0x01},
{0x4083, 0x01},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0A},
{0x0341, 0x40},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x03},
{0x0345, 0x1C},
{0x0346, 0x03},
{0x0347, 0x38},
{0x0348, 0x0D},
{0x0349, 0x53},
{0x034A, 0x08},
{0x034B, 0xF7},
{0x034C, 0x0A},
{0x034D, 0x38},
{0x034E, 0x05},
{0x034F, 0xC0},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x0A},
{0x0355, 0x38},
{0x0356, 0x05},
{0x0357, 0xC0},
{0x301D, 0x30},
{0x3310, 0x0A},
{0x3311, 0x38},
{0x3312, 0x05},
{0x3313, 0xC0},
{0x331C, 0x08},
{0x331D, 0xD4},
{0x4084, 0x00},
{0x4085, 0x00},
{0x4086, 0x00},
{0x4087, 0x00},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x87},
{0x0831, 0x3F},
{0x0832, 0x67},
{0x0833, 0x3F},
{0x0834, 0x3F},
{0x0835, 0x4F},
{0x0836, 0xDF},
{0x0837, 0x47},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0A},
{0x0203, 0x3C},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x0A},
{0x33B1, 0x38},
{0x33B3, 0x01},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
static struct imx135_reg mode_3896x2192[] = {
/* software reset */
{0x0103, 0x01},
/* global settings */
{0x0101, 0x00},
{0x0105, 0x01},
{0x0110, 0x00},
{0x0220, 0x01},
{0x3302, 0x11},
{0x3833, 0x20},
{0x3873, 0x03},
{0x3893, 0x00},
{0x3906, 0x08},
{0x3907, 0x01},
{0x391B, 0x00},
{0x3C09, 0x01},
{0x600A, 0x00},
{0x3008, 0xB0},
{0x320A, 0x01},
{0x320D, 0x10},
{0x3216, 0x2E},
{0x322C, 0x02},
{0x3409, 0x0C},
{0x340C, 0x2D},
{0x3411, 0x39},
{0x3414, 0x1E},
{0x3427, 0x04},
{0x3480, 0x1E},
{0x3484, 0x1E},
{0x3488, 0x1E},
{0x348C, 0x1E},
{0x3490, 0x1E},
{0x3494, 0x1E},
{0x3511, 0x8F},
{0x364F, 0x2D},
/* Clock Setting */
{0x011E, 0x18},
{0x011F, 0x00},
{0x0301, 0x05},
{0x0303, 0x01},
{0x0305, 0x0C},
{0x0309, 0x05},
{0x030B, 0x01},
{0x030C, 0x01},
{0x030D, 0xC2},
{0x030E, 0x01},
{0x3A06, 0x11},
/* Mode Settings */
{0x0108, 0x03},
{0x0112, 0x0E},
{0x0113, 0x0A},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0390, 0x00},
{0x0391, 0x11},
{0x0392, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x4082, 0x01},
{0x4083, 0x01},
{0x7006, 0x04},
/* Optinal/Function settings */
{0x0700, 0x00},
{0x3A63, 0x00},
{0x4100, 0xF8},
{0x4203, 0xFF},
{0x4344, 0x00},
{0x441C, 0x01},
/* Size Setting */
{0x0340, 0x0A},
{0x0341, 0x40},
{0x0342, 0x11},
{0x0343, 0xDC},
{0x0344, 0x00},
{0x0345, 0x9C},
{0x0346, 0x01},
{0x0347, 0xD0},
{0x0348, 0x0F},
{0x0349, 0xD3},
{0x034A, 0x0A},
{0x034B, 0x5F},
{0x034C, 0x0F},
{0x034D, 0x38},
{0x034E, 0x08},
{0x034F, 0x90},
{0x0350, 0x00},
{0x0351, 0x00},
{0x0352, 0x00},
{0x0353, 0x00},
{0x0354, 0x0F},
{0x0355, 0x38},
{0x0356, 0x08},
{0x0357, 0x90},
{0x301D, 0x30},
{0x3310, 0x0F},
{0x3311, 0x38},
{0x3312, 0x08},
{0x3313, 0x90},
{0x331C, 0x0F},
{0x331D, 0x32},
{0x4084, 0x00},
{0x4085, 0x00},
{0x4086, 0x00},
{0x4087, 0x00},
{0x4400, 0x00},
/* Global Timing Setting */
{0x0830, 0x87},
{0x0831, 0x3F},
{0x0832, 0x67},
{0x0833, 0x3F},
{0x0834, 0x3F},
{0x0835, 0x4F},
{0x0836, 0xDF},
{0x0837, 0x47},
{0x0839, 0x1F},
{0x083A, 0x17},
{0x083B, 0x02},
/* Integration Time Setting */
{0x0202, 0x0A},
{0x0203, 0x3C},
/* Gain Setting */
{0x0205, 0x00},
{0x020E, 0x01},
{0x020F, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
/* HDR Setting */
{0x0230, 0x00},
{0x0231, 0x00},
{0x0233, 0x00},
{0x0234, 0x00},
{0x0235, 0x40},
{0x0238, 0x01},
{0x0239, 0x04},
{0x023B, 0x00},
{0x023C, 0x01},
{0x33B0, 0x0F},
{0x33B1, 0x38},
{0x33B3, 0x01},
{0x33B4, 0x01},
{0x3800, 0x00},
{0x3A43, 0x01},
/* stream on */
{0x0100, 0x01},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
static struct imx135_reg mode_quality_hdr[] = {
/* defect correction */
{0x380A, 0x00},
{0x380B, 0x00},
{0x4103, 0x00},
/* color artifact */
{0x4243, 0x9A},
{0x4330, 0x01},
{0x4331, 0x90},
{0x4332, 0x02},
{0x4333, 0x58},
{0x4334, 0x03},
{0x4335, 0x20},
{0x4336, 0x03},
{0x4337, 0x84},
{0x433C, 0x01},
{0x4340, 0x02},
{0x4341, 0x58},
{0x4342, 0x03},
{0x4343, 0x52},
/* moire reduction */
{0x4364, 0x0B},
{0x4368, 0x00},
{0x4369, 0x0F},
{0x436A, 0x03},
{0x436B, 0xA8},
{0x436C, 0x00},
{0x436D, 0x00},
{0x436E, 0x00},
{0x436F, 0x06},
/* CNR parameter */
{0x4281, 0x21},
{0x4282, 0x18},
{0x4283, 0x04},
{0x4284, 0x08},
{0x4287, 0x7F},
{0x4288, 0x08},
{0x428B, 0x7F},
{0x428C, 0x08},
{0x428F, 0x7F},
{0x4297, 0x00},
{0x4298, 0x7E},
{0x4299, 0x7E},
{0x429A, 0x7E},
{0x42A4, 0xFB},
{0x42A5, 0x7E},
{0x42A6, 0xDF},
{0x42A7, 0xB7},
{0x42AF, 0x03},
/* ARNR Parameter Settings */
{0x4207, 0x03},
{0x4216, 0x08},
{0x4217, 0x08},
/* DLC parameter */
{0x4218, 0x00},
{0x421B, 0x20},
{0x421F, 0x04},
{0x4222, 0x02},
{0x4223, 0x22},
{0x422E, 0x54},
{0x422F, 0xFB},
{0x4230, 0xFF},
{0x4231, 0xFE},
{0x4232, 0xFF},
{0x4235, 0x58},
{0x4236, 0xF7},
{0x4237, 0xFD},
{0x4239, 0x4E},
{0x423A, 0xFC},
{0x423B, 0xFD},
/* HDR Setting */
{0x4300, 0x00},
{0x4316, 0x12},
{0x4317, 0x22},
{0x4318, 0x00},
{0x4319, 0x00},
{0x431A, 0x00},
{0x4324, 0x03},
{0x4325, 0x20},
{0x4326, 0x03},
{0x4327, 0x84},
{0x4328, 0x03},
{0x4329, 0x20},
{0x432A, 0x03},
{0x432B, 0x20},
{0x432C, 0x01},
{0x432D, 0x01},
{0x4338, 0x02},
{0x4339, 0x00},
{0x433A, 0x00},
{0x433B, 0x02},
{0x435A, 0x03},
{0x435B, 0x84},
{0x435E, 0x01},
{0x435F, 0xFF},
{0x4360, 0x01},
{0x4361, 0xF4},
{0x4362, 0x03},
{0x4363, 0x84},
{0x437B, 0x01},
{0x4401, 0x3F},
{0x4402, 0xFF},
{0x4404, 0x13},
{0x4405, 0x26},
{0x4406, 0x07},
{0x4408, 0x20},
{0x4409, 0xE5},
{0x440A, 0xFB},
{0x440C, 0xF6},
{0x440D, 0xEA},
{0x440E, 0x20},
{0x4410, 0x00},
{0x4411, 0x00},
{0x4412, 0x3F},
{0x4413, 0xFF},
{0x4414, 0x1F},
{0x4415, 0xFF},
{0x4416, 0x20},
{0x4417, 0x00},
{0x4418, 0x1F},
{0x4419, 0xFF},
{0x441A, 0x20},
{0x441B, 0x00},
{0x441D, 0x40},
{0x441E, 0x1E},
{0x441F, 0x38},
{0x4420, 0x01},
{0x4444, 0x00},
{0x4445, 0x00},
{0x4446, 0x1D},
{0x4447, 0xF9},
{0x4452, 0x00},
{0x4453, 0xA0},
{0x4454, 0x08},
{0x4455, 0x00},
{0x4456, 0x0F},
{0x4457, 0xFF},
{0x4458, 0x18},
{0x4459, 0x18},
{0x445A, 0x3F},
{0x445B, 0x3A},
{0x445C, 0x00},
{0x445D, 0x28},
{0x445E, 0x01},
{0x445F, 0x90},
{0x4460, 0x00},
{0x4461, 0x60},
{0x4462, 0x00},
{0x4463, 0x00},
{0x4464, 0x00},
{0x4465, 0x00},
{0x446C, 0x01},
{0x446D, 0x00},
{0x446E, 0x00},
/* LSC setting */
{0x452A, 0x02},
/* White balance */
{0x0712, 0x01},
{0x0713, 0x00},
{0x0714, 0x01},
{0x0715, 0x00},
{0x0716, 0x01},
{0x0717, 0x00},
{0x0718, 0x01},
{0x0719, 0x00},
/* Shading */
{0x4500, 0x1F},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
static struct imx135_reg mode_quality[] = {
/* defect correction */
{0x380A, 0x00},
{0x380B, 0x00},
{0x4103, 0x00},
/* color artifact */
{0x4243, 0x9A},
{0x4330, 0x01},
{0x4331, 0x90},
{0x4332, 0x02},
{0x4333, 0x58},
{0x4334, 0x03},
{0x4335, 0x20},
{0x4336, 0x03},
{0x4337, 0x84},
{0x433C, 0x01},
{0x4340, 0x02},
{0x4341, 0x58},
{0x4342, 0x03},
{0x4343, 0x52},
/* moire reduction */
{0x4364, 0x0B},
{0x4368, 0x00},
{0x4369, 0x0F},
{0x436A, 0x03},
{0x436B, 0xA8},
{0x436C, 0x00},
{0x436D, 0x00},
{0x436E, 0x00},
{0x436F, 0x06},
/* CNR parameter */
{0x4281, 0x21},
{0x4282, 0x18},
{0x4283, 0x04},
{0x4284, 0x08},
{0x4287, 0x7F},
{0x4288, 0x08},
{0x428B, 0x7F},
{0x428C, 0x08},
{0x428F, 0x7F},
{0x4297, 0x00},
{0x4298, 0x7E},
{0x4299, 0x7E},
{0x429A, 0x7E},
{0x42A4, 0xFB},
{0x42A5, 0x7E},
{0x42A6, 0xDF},
{0x42A7, 0xB7},
{0x42AF, 0x03},
/* ARNR Parameter Settings */
{0x4207, 0x03},
{0x4216, 0x08},
{0x4217, 0x08},
/* DLC parameter */
{0x4218, 0x00},
{0x421B, 0x20},
{0x421F, 0x04},
{0x4222, 0x02},
{0x4223, 0x22},
{0x422E, 0x54},
{0x422F, 0xFB},
{0x4230, 0xFF},
{0x4231, 0xFE},
{0x4232, 0xFF},
{0x4235, 0x58},
{0x4236, 0xF7},
{0x4237, 0xFD},
{0x4239, 0x4E},
{0x423A, 0xFC},
{0x423B, 0xFD},
/* HDR Setting */
{0x4300, 0x00},
{0x4316, 0x12},
{0x4317, 0x22},
{0x4318, 0x00},
{0x4319, 0x00},
{0x431A, 0x00},
{0x4324, 0x03},
{0x4325, 0x20},
{0x4326, 0x03},
{0x4327, 0x84},
{0x4328, 0x03},
{0x4329, 0x20},
{0x432A, 0x03},
{0x432B, 0x20},
{0x432C, 0x01},
{0x432D, 0x01},
{0x4338, 0x02},
{0x4339, 0x00},
{0x433A, 0x00},
{0x433B, 0x02},
{0x435A, 0x03},
{0x435B, 0x84},
{0x435E, 0x01},
{0x435F, 0xFF},
{0x4360, 0x01},
{0x4361, 0xF4},
{0x4362, 0x03},
{0x4363, 0x84},
{0x437B, 0x01},
{0x4401, 0x3F},
{0x4402, 0xFF},
{0x4404, 0x13},
{0x4405, 0x26},
{0x4406, 0x07},
{0x4408, 0x20},
{0x4409, 0xE5},
{0x440A, 0xFB},
{0x440C, 0xF6},
{0x440D, 0xEA},
{0x440E, 0x20},
{0x4410, 0x00},
{0x4411, 0x00},
{0x4412, 0x3F},
{0x4413, 0xFF},
{0x4414, 0x1F},
{0x4415, 0xFF},
{0x4416, 0x20},
{0x4417, 0x00},
{0x4418, 0x1F},
{0x4419, 0xFF},
{0x441A, 0x20},
{0x441B, 0x00},
{0x441D, 0x40},
{0x441E, 0x1E},
{0x441F, 0x38},
{0x4420, 0x01},
{0x4444, 0x00},
{0x4445, 0x00},
{0x4446, 0x1D},
{0x4447, 0xF9},
{0x4452, 0x00},
{0x4453, 0xA0},
{0x4454, 0x08},
{0x4455, 0x00},
{0x4456, 0x0F},
{0x4457, 0xFF},
{0x4458, 0x18},
{0x4459, 0x18},
{0x445A, 0x3F},
{0x445B, 0x3A},
{0x445C, 0x00},
{0x445D, 0x28},
{0x445E, 0x01},
{0x445F, 0x90},
{0x4460, 0x00},
{0x4461, 0x60},
{0x4462, 0x00},
{0x4463, 0x00},
{0x4464, 0x00},
{0x4465, 0x00},
{0x446C, 0x01},
{0x446D, 0x00},
{0x446E, 0x00},
/* LSC setting */
{0x452A, 0x02},
/* White balance */
{0x0712, 0x01},
{0x0713, 0x00},
{0x0714, 0x01},
{0x0715, 0x00},
{0x0716, 0x01},
{0x0717, 0x00},
{0x0718, 0x01},
{0x0719, 0x00},
/* Shading */
{0x4500, 0x1F},
{IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
{IMX135_TABLE_END, 0x00}
};
enum {
IMX135_MODE_4208X3120,
IMX135_MODE_1920X1080,
IMX135_MODE_1280X720,
IMX135_MODE_2616X1472,
IMX135_MODE_3896X2192,
IMX135_MODE_QUALITY_HDR,
IMX135_MODE_QUALITY,
};
static struct imx135_reg *mode_table[] = {
[IMX135_MODE_4208X3120] = mode_4208x3120,
[IMX135_MODE_1920X1080] = mode_1920x1080,
[IMX135_MODE_1280X720] = mode_1280x720,
[IMX135_MODE_2616X1472] = mode_2616x1472,
[IMX135_MODE_3896X2192] = mode_3896x2192,
[IMX135_MODE_QUALITY_HDR] = mode_quality_hdr,
[IMX135_MODE_QUALITY] = mode_quality,
};
static struct imx135_reg flash_strobe_mod[] = {
{0x0800, 0x01}, /* flash strobe output enble on ERS mode */
{0x0801, 0x01}, /* reference dividor fron EXT CLK */
{0x0804, 0x04}, /* shutter sync mode */
{0x0806, 0x00}, /* ref point hi */
{0x0807, 0x00}, /* ref point lo */
{0x0808, 0x00}, /* latency hi from ref point */
{0x0809, 0x00}, /* latency lo from ref point */
{0x080A, 0x09}, /* high period of XHS for ERS hi */
{0x080B, 0x60}, /* high period of XHS for ERS lo */
{0x080C, 0x00}, /* low period of XHS for ERS hi */
{0x080D, 0x00}, /* low period of XHS for ERS lo */
{0x3100, 0x01}, /* XHS output control, 1 - FSTROBE enabled */
{0x3104, 0x00}, /* XHS driving capability, 0 - 2mA */
{0x3108, 0x00}, /* for 'sync to XVS' mode */
{0x3109, 0x00}, /* AE bracketing mode, 0 - normal */
{0x080E, 0x01}, /* num of ERS flash pulse */
{IMX135_TABLE_END, 0x00}
};
static inline void
msleep_range(unsigned int delay_base)
{
usleep_range(delay_base*1000, delay_base*1000+500);
}
static inline void
imx135_get_frame_length_regs(struct imx135_reg *regs, u32 frame_length)
{
regs->addr = IMX135_FRAME_LENGTH_ADDR_MSB;
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = IMX135_FRAME_LENGTH_ADDR_LSB;
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void
imx135_get_coarse_time_regs(struct imx135_reg *regs, u32 coarse_time)
{
regs->addr = IMX135_COARSE_TIME_ADDR_MSB;
regs->val = (coarse_time >> 8) & 0xff;
(regs + 1)->addr = IMX135_COARSE_TIME_ADDR_LSB;
(regs + 1)->val = (coarse_time) & 0xff;
}
static inline void
imx135_get_coarse_time_short_regs(struct imx135_reg *regs, u32 coarse_time)
{
regs->addr = IMX135_COARSE_TIME_SHORT_ADDR_MSB;
regs->val = (coarse_time >> 8) & 0xff;
(regs + 1)->addr = IMX135_COARSE_TIME_SHORT_ADDR_LSB;
(regs + 1)->val = (coarse_time) & 0xff;
}
static inline void
imx135_get_gain_reg(struct imx135_reg *regs, u16 gain)
{
regs->addr = IMX135_GAIN_ADDR;
regs->val = gain;
}
static inline void
imx135_get_gain_short_reg(struct imx135_reg *regs, u16 gain)
{
regs->addr = IMX135_GAIN_SHORT_ADDR;
regs->val = gain;
}
static int
imx135_read_reg(struct i2c_client *client, u16 addr, u8 *val)
{
int err;
struct i2c_msg msg[2];
unsigned char data[3];
if (!client->adapter)
return -ENODEV;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = data;
/* high byte goes out first */
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = data + 2;
err = i2c_transfer(client->adapter, msg, 2);
if (err != 2)
return -EINVAL;
*val = data[2];
return 0;
}
static int
imx135_write_reg(struct i2c_client *client, u16 addr, u8 val)
{
int err;
struct i2c_msg msg;
unsigned char data[3];
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;
err = i2c_transfer(client->adapter, &msg, 1);
if (err == 1)
return 0;
pr_err("%s:i2c write failed, %x = %x\n",
__func__, addr, val);
return err;
}
static int
imx135_write_table(struct i2c_client *client,
const struct imx135_reg table[],
const struct imx135_reg override_list[],
int num_override_regs)
{
int err;
const struct imx135_reg *next;
int i;
u16 val;
for (next = table; next->addr != IMX135_TABLE_END; next++) {
if (next->addr == IMX135_TABLE_WAIT_MS) {
msleep_range(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 = imx135_write_reg(client, next->addr, val);
if (err) {
pr_err("%s:imx135_write_table:%d", __func__, err);
return err;
}
}
return 0;
}
static int imx135_set_flash_output(struct imx135_info *info)
{
struct imx135_flash_control *fctl;
if (!info->pdata)
return 0;
fctl = &info->pdata->flash_cap;
dev_dbg(&info->i2c_client->dev, "%s: %x\n", __func__, fctl->enable);
dev_dbg(&info->i2c_client->dev, "edg: %x, st: %x, rpt: %x, dly: %x\n",
fctl->edge_trig_en, fctl->start_edge,
fctl->repeat, fctl->delay_frm);
return imx135_write_table(info->i2c_client, flash_strobe_mod, NULL, 0);
}
static int imx135_get_flash_cap(struct imx135_info *info)
{
struct imx135_flash_control *fctl;
dev_dbg(&info->i2c_client->dev, "%s: %p\n", __func__, info->pdata);
if (info->pdata) {
fctl = &info->pdata->flash_cap;
dev_dbg(&info->i2c_client->dev,
"edg: %x, st: %x, rpt: %x, dl: %x\n",
fctl->edge_trig_en,
fctl->start_edge,
fctl->repeat,
fctl->delay_frm);
if (fctl->enable)
return 0;
}
return -ENODEV;
}
static inline int imx135_set_flash_control(
struct imx135_info *info, struct imx135_flash_control *fc)
{
dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
return imx135_write_reg(info->i2c_client, 0x0802, 0x01);
}
static int
imx135_set_mode(struct imx135_info *info, struct imx135_mode *mode)
{
int sensor_mode;
u8 quality_hdr;
int err;
struct imx135_reg reg_list[8];
pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u, hdr %d\n",
__func__, mode->xres, mode->yres, mode->frame_length,
mode->coarse_time, mode->gain, mode->hdr_en);
if (mode->xres == 4208 && mode->yres == 3120) {
sensor_mode = IMX135_MODE_4208X3120;
quality_hdr = 1;
} else if (mode->xres == 1920 && mode->yres == 1080) {
sensor_mode = IMX135_MODE_1920X1080;
quality_hdr = 0;
} else if (mode->xres == 1280 && mode->yres == 720) {
sensor_mode = IMX135_MODE_1280X720;
quality_hdr = 0;
} else if (mode->xres == 2616 && mode->yres == 1472) {
sensor_mode = IMX135_MODE_2616X1472;
quality_hdr = 1;
} else if (mode->xres == 3896 && mode->yres == 2192) {
sensor_mode = IMX135_MODE_3896X2192;
quality_hdr = 1;
} else {
pr_err("%s: invalid resolution supplied to set mode %d %d\n",
__func__, mode->xres, mode->yres);
return -EINVAL;
}
/* get a list of override regs for the asking frame length, */
/* coarse integration time, and gain. */
imx135_get_frame_length_regs(reg_list, mode->frame_length);
imx135_get_coarse_time_regs(reg_list + 2, mode->coarse_time);
imx135_get_gain_reg(reg_list + 4, mode->gain);
/* if HDR is enabled */
if (mode->hdr_en == 1) {
imx135_get_gain_short_reg(reg_list + 5, mode->gain);
imx135_get_coarse_time_short_regs(
reg_list + 6, mode->coarse_time_short);
}
err = imx135_write_table(info->i2c_client,
mode_table[sensor_mode],
reg_list, mode->hdr_en ? 8 : 5);
if (err)
return err;
if (quality_hdr)
err = imx135_write_table(info->i2c_client,
mode_table[IMX135_MODE_QUALITY_HDR],
reg_list, 0);
else
err = imx135_write_table(info->i2c_client,
mode_table[IMX135_MODE_QUALITY],
reg_list, 0);
if (err)
return err;
imx135_set_flash_output(info);
info->mode = sensor_mode;
pr_info("[IMX135]: stream on.\n");
return 0;
}
static int
imx135_get_status(struct imx135_info *info, u8 *dev_status)
{
*dev_status = 0;
return 0;
}
static int
imx135_set_frame_length(struct imx135_info *info, u32 frame_length,
bool group_hold)
{
struct imx135_reg reg_list[2];
int i = 0;
int ret;
imx135_get_frame_length_regs(reg_list, frame_length);
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x0104, 0x01);
if (ret)
return ret;
}
for (i = 0; i < 2; i++) {
ret = imx135_write_reg(info->i2c_client, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x0104, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx135_set_coarse_time(struct imx135_info *info, u32 coarse_time,
bool group_hold)
{
int ret;
struct imx135_reg reg_list[2];
int i = 0;
imx135_get_coarse_time_regs(reg_list, coarse_time);
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x01);
if (ret)
return ret;
}
for (i = 0; i < 2; i++) {
ret = imx135_write_reg(info->i2c_client, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx135_set_gain(struct imx135_info *info, u16 gain, bool group_hold)
{
int ret;
struct imx135_reg reg_list;
imx135_get_gain_reg(&reg_list, gain);
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x1);
if (ret)
return ret;
}
ret = imx135_write_reg(info->i2c_client, reg_list.addr, reg_list.val);
/* writing second gain register for HDR */
ret = imx135_write_reg(info->i2c_client, 0x233, reg_list.val);
if (ret)
return ret;
if (group_hold) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx135_set_hdr_coarse_time(struct imx135_info *info, struct imx135_hdr *values)
{
struct imx135_reg reg_list[2];
struct imx135_reg reg_list_short[2];
int ret, i = 0;
/* get long and short coarse time registers */
imx135_get_coarse_time_regs(reg_list, values->coarse_time_long);
imx135_get_coarse_time_short_regs(reg_list_short,
values->coarse_time_short);
/* set to direct mode */
ret = imx135_write_reg(info->i2c_client, 0x238, 0x1);
if (ret)
return ret;
/* set group hold */
ret = imx135_write_reg(info->i2c_client, 0x104, 0x1);
if (ret)
return ret;
/* writing long exposure */
for (i = 0; i < 2; i++) {
ret = imx135_write_reg(info->i2c_client, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
/* writing short exposure */
for (i = 0; i < 2; i++) {
ret = imx135_write_reg(info->i2c_client, reg_list_short[i].addr,
reg_list_short[i].val);
if (ret)
return ret;
}
ret = imx135_write_reg(info->i2c_client, 0x104, 0x0);
if (ret)
return ret;
return 0;
}
static int
imx135_set_group_hold(struct imx135_info *info, struct imx135_ae *ae)
{
int ret;
int count = 0;
bool group_hold_enabled = false;
struct imx135_hdr values;
values.coarse_time_long = ae->coarse_time;
values.coarse_time_short = ae->coarse_time_short;
if (ae->gain_enable)
count++;
if (ae->coarse_time_enable)
count++;
if (ae->frame_length_enable)
count++;
if (count >= 2)
group_hold_enabled = true;
if (group_hold_enabled) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x1);
if (ret)
return ret;
}
if (ae->gain_enable)
imx135_set_gain(info, ae->gain, false);
if (ae->coarse_time_enable)
imx135_set_hdr_coarse_time(info, &values);
if (ae->frame_length_enable)
imx135_set_frame_length(info, ae->frame_length, false);
if (group_hold_enabled) {
ret = imx135_write_reg(info->i2c_client, 0x104, 0x0);
if (ret)
return ret;
}
return 0;
}
static int imx135_get_sensor_id(struct imx135_info *info)
{
int ret = 0;
int i;
u8 bak = 0;
pr_info("%s\n", __func__);
if (info->sensor_data.fuse_id_size)
return 0;
/* Note 1: If the sensor does not have power at this point
Need to supply the power, e.g. by calling power on function */
ret |= imx135_write_reg(info->i2c_client, 0x3B02, 0x00);
ret |= imx135_write_reg(info->i2c_client, 0x3B00, 0x01);
for (i = 0; i < 9 ; i++) {
ret |= imx135_read_reg(info->i2c_client, 0x3B24 + i, &bak);
info->sensor_data.fuse_id[i] = bak;
}
if (!ret)
info->sensor_data.fuse_id_size = i;
/* Note 2: Need to clean up any action carried out in Note 1 */
return ret;
}
static void imx135_mclk_disable(struct imx135_info *info)
{
dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__);
clk_disable_unprepare(info->mclk);
}
static int imx135_mclk_enable(struct imx135_info *info)
{
int err;
unsigned long mclk_init_rate = 24000000;
dev_dbg(&info->i2c_client->dev, "%s: enable MCLK with %lu Hz\n",
__func__, mclk_init_rate);
err = clk_set_rate(info->mclk, mclk_init_rate);
if (!err)
err = clk_prepare_enable(info->mclk);
return err;
}
static long
imx135_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct imx135_info *info = file->private_data;
switch (cmd) {
case IMX135_IOCTL_SET_POWER:
if (!info->pdata)
break;
if (arg && info->pdata->power_on) {
err = imx135_mclk_enable(info);
if (!err)
err = info->pdata->power_on(&info->power);
if (err < 0)
imx135_mclk_disable(info);
}
if (!arg && info->pdata->power_off) {
info->pdata->power_off(&info->power);
imx135_mclk_disable(info);
}
break;
case IMX135_IOCTL_SET_MODE:
{
struct imx135_mode mode;
if (copy_from_user(&mode, (const void __user *)arg,
sizeof(struct imx135_mode))) {
pr_err("%s:Failed to get mode from user.\n", __func__);
return -EFAULT;
}
return imx135_set_mode(info, &mode);
}
case IMX135_IOCTL_SET_FRAME_LENGTH:
return imx135_set_frame_length(info, (u32)arg, true);
case IMX135_IOCTL_SET_COARSE_TIME:
return imx135_set_coarse_time(info, (u32)arg, true);
case IMX135_IOCTL_SET_GAIN:
return imx135_set_gain(info, (u16)arg, true);
case IMX135_IOCTL_GET_STATUS:
{
u8 status;
err = imx135_get_status(info, &status);
if (err)
return err;
if (copy_to_user((void __user *)arg, &status, 1)) {
pr_err("%s:Failed to copy status to user\n", __func__);
return -EFAULT;
}
return 0;
}
case IMX135_IOCTL_GET_SENSORDATA:
{
err = imx135_get_sensor_id(info);
if (err) {
pr_err("%s:Failed to get fuse id info.\n", __func__);
return err;
}
if (copy_to_user((void __user *)arg, &info->sensor_data,
sizeof(struct imx135_sensordata))) {
pr_info("%s:Failed to copy fuse id to user space\n",
__func__);
return -EFAULT;
}
return 0;
}
case IMX135_IOCTL_SET_GROUP_HOLD:
{
struct imx135_ae ae;
if (copy_from_user(&ae, (const void __user *)arg,
sizeof(struct imx135_ae))) {
pr_info("%s:fail group hold\n", __func__);
return -EFAULT;
}
return imx135_set_group_hold(info, &ae);
}
case IMX135_IOCTL_SET_HDR_COARSE_TIME:
{
struct imx135_hdr values;
dev_dbg(&info->i2c_client->dev,
"IMX135_IOCTL_SET_HDR_COARSE_TIME\n");
if (copy_from_user(&values,
(const void __user *)arg,
sizeof(struct imx135_hdr))) {
err = -EFAULT;
break;
}
err = imx135_set_hdr_coarse_time(info, &values);
break;
}
case IMX135_IOCTL_SET_FLASH_MODE:
{
struct imx135_flash_control values;
dev_dbg(&info->i2c_client->dev,
"IMX135_IOCTL_SET_FLASH_MODE\n");
if (copy_from_user(&values,
(const void __user *)arg,
sizeof(struct imx135_flash_control))) {
err = -EFAULT;
break;
}
err = imx135_set_flash_control(info, &values);
break;
}
case IMX135_IOCTL_GET_FLASH_CAP:
err = imx135_get_flash_cap(info);
break;
default:
pr_err("%s:unknown cmd.\n", __func__);
err = -EINVAL;
}
return err;
}
static int imx135_debugfs_show(struct seq_file *s, void *unused)
{
struct imx135_info *dev = s->private;
dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__);
mutex_lock(&dev->imx135_camera_lock);
mutex_unlock(&dev->imx135_camera_lock);
return 0;
}
static ssize_t imx135_debugfs_write(
struct file *file,
char const __user *buf,
size_t count,
loff_t *offset)
{
struct imx135_info *dev =
((struct seq_file *)file->private_data)->private;
struct i2c_client *i2c_client = dev->i2c_client;
int ret = 0;
char buffer[MAX_BUFFER_SIZE];
u32 address;
u32 data;
u8 readback;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (copy_from_user(&buffer, buf, sizeof(buffer)))
goto debugfs_write_fail;
if (sscanf(buf, "0x%x 0x%x", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "0X%x 0X%x", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "%d %d", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "0x%x 0x%x", &address, &data) == 1)
goto read;
if (sscanf(buf, "0X%x 0X%x", &address, &data) == 1)
goto read;
if (sscanf(buf, "%d %d", &address, &data) == 1)
goto read;
dev_err(&i2c_client->dev, "SYNTAX ERROR: %s\n", buf);
return -EFAULT;
set_attr:
dev_info(&i2c_client->dev,
"new address = %x, data = %x\n", address, data);
ret |= imx135_write_reg(i2c_client, address, data);
read:
ret |= imx135_read_reg(i2c_client, address, &readback);
dev_dbg(&i2c_client->dev,
"wrote to address 0x%x with value 0x%x\n",
address, readback);
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 imx135_debugfs_open(struct inode *inode, struct file *file)
{
struct imx135_info *dev = inode->i_private;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
return single_open(file, imx135_debugfs_show, inode->i_private);
}
static const struct file_operations imx135_debugfs_fops = {
.open = imx135_debugfs_open,
.read = seq_read,
.write = imx135_debugfs_write,
.llseek = seq_lseek,
.release = single_release,
};
static void imx135_remove_debugfs(struct imx135_info *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
debugfs_remove_recursive(dev->debugdir);
dev->debugdir = NULL;
}
static void imx135_create_debugfs(struct imx135_info *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->miscdev_info.this_device->kobj.name,
NULL);
if (!dev->debugdir)
goto remove_debugfs;
ret = debugfs_create_file("d",
S_IWUSR | S_IRUGO,
dev->debugdir, dev,
&imx135_debugfs_fops);
if (!ret)
goto remove_debugfs;
return;
remove_debugfs:
dev_err(&i2c_client->dev, "couldn't create debugfs\n");
imx135_remove_debugfs(dev);
}
static int imx135_get_extra_regulators(struct imx135_power_rail *pw)
{
if (!pw->ext_reg1) {
pw->ext_reg1 = regulator_get(NULL, "imx135_reg1");
if (WARN_ON(IS_ERR(pw->ext_reg1))) {
pr_err("%s: can't get regulator imx135_reg1: %ld\n",
__func__, PTR_ERR(pw->ext_reg1));
pw->ext_reg1 = NULL;
return -ENODEV;
}
}
if (!pw->ext_reg2) {
pw->ext_reg2 = regulator_get(NULL, "imx135_reg2");
if (WARN_ON(IS_ERR(pw->ext_reg2))) {
pr_err("%s: can't get regulator imx135_reg2: %ld\n",
__func__, PTR_ERR(pw->ext_reg2));
pw->ext_reg2 = NULL;
return -ENODEV;
}
}
return 0;
}
static int imx135_power_on(struct imx135_power_rail *pw)
{
int err;
struct imx135_info *info = container_of(pw, struct imx135_info, power);
if (unlikely(WARN_ON(!pw || !pw->iovdd || !pw->avdd)))
return -EFAULT;
if (info->pdata->ext_reg) {
if (imx135_get_extra_regulators(pw))
goto imx135_poweron_fail;
err = regulator_enable(pw->ext_reg1);
if (unlikely(err))
goto imx135_ext_reg1_fail;
err = regulator_enable(pw->ext_reg2);
if (unlikely(err))
goto imx135_ext_reg2_fail;
}
gpio_set_value(info->pdata->reset_gpio, 0);
gpio_set_value(info->pdata->af_gpio, 1);
gpio_set_value(info->pdata->cam1_gpio, 0);
usleep_range(10, 20);
err = regulator_enable(pw->avdd);
if (err)
goto imx135_avdd_fail;
err = regulator_enable(pw->iovdd);
if (err)
goto imx135_iovdd_fail;
usleep_range(1, 2);
gpio_set_value(info->pdata->reset_gpio, 1);
gpio_set_value(info->pdata->cam1_gpio, 1);
usleep_range(300, 310);
return 1;
imx135_iovdd_fail:
regulator_disable(pw->avdd);
imx135_avdd_fail:
if (pw->ext_reg2)
regulator_disable(pw->ext_reg2);
imx135_ext_reg2_fail:
if (pw->ext_reg1)
regulator_disable(pw->ext_reg1);
gpio_set_value(info->pdata->af_gpio, 0);
imx135_ext_reg1_fail:
imx135_poweron_fail:
pr_err("%s failed.\n", __func__);
return -ENODEV;
}
static int imx135_power_off(struct imx135_power_rail *pw)
{
struct imx135_info *info = container_of(pw, struct imx135_info, power);
if (unlikely(WARN_ON(!pw || !pw->iovdd || !pw->avdd)))
return -EFAULT;
usleep_range(1, 2);
gpio_set_value(info->pdata->cam1_gpio, 0);
usleep_range(1, 2);
regulator_disable(pw->iovdd);
regulator_disable(pw->avdd);
if (info->pdata->ext_reg) {
regulator_disable(pw->ext_reg1);
regulator_disable(pw->ext_reg2);
}
return 0;
}
static int
imx135_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct imx135_info *info;
info = container_of(miscdev, struct imx135_info, miscdev_info);
/* check if the device is in use */
if (atomic_xchg(&info->in_use, 1)) {
pr_info("%s:BUSY!\n", __func__);
return -EBUSY;
}
file->private_data = info;
return 0;
}
static int
imx135_release(struct inode *inode, struct file *file)
{
struct imx135_info *info = file->private_data;
file->private_data = NULL;
/* warn if device is already released */
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static int imx135_power_put(struct imx135_power_rail *pw)
{
if (unlikely(!pw))
return -EFAULT;
if (likely(pw->avdd))
regulator_put(pw->avdd);
if (likely(pw->iovdd))
regulator_put(pw->iovdd);
if (likely(pw->dvdd))
regulator_put(pw->dvdd);
if (likely(pw->ext_reg1))
regulator_put(pw->ext_reg1);
if (likely(pw->ext_reg2))
regulator_put(pw->ext_reg2);
pw->avdd = NULL;
pw->iovdd = NULL;
pw->dvdd = NULL;
pw->ext_reg1 = NULL;
pw->ext_reg2 = NULL;
return 0;
}
static int imx135_regulator_get(struct imx135_info *info,
struct regulator **vreg, char vreg_name[])
{
struct regulator *reg = NULL;
int err = 0;
reg = regulator_get(&info->i2c_client->dev, vreg_name);
if (unlikely(IS_ERR_OR_NULL(reg))) {
dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n",
__func__, vreg_name, (int)reg);
err = PTR_ERR(reg);
reg = NULL;
} else
dev_dbg(&info->i2c_client->dev, "%s: %s\n",
__func__, vreg_name);
*vreg = reg;
return err;
}
static int imx135_power_get(struct imx135_info *info)
{
struct imx135_power_rail *pw = &info->power;
int err = 0;
err |= imx135_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */
err |= imx135_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */
err |= imx135_regulator_get(info, &pw->iovdd, "vif"); /* IO 1.8v */
return err;
}
static const struct file_operations imx135_fileops = {
.owner = THIS_MODULE,
.open = imx135_open,
.unlocked_ioctl = imx135_ioctl,
.release = imx135_release,
};
static struct miscdevice imx135_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "imx135",
.fops = &imx135_fileops,
};
static struct of_device_id imx135_of_match[] = {
{ .compatible = "nvidia,imx135", },
{ },
};
MODULE_DEVICE_TABLE(of, imx135_of_match);
static struct imx135_platform_data *imx135_parse_dt(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node;
struct imx135_platform_data *board_info_pdata;
const struct of_device_id *match;
match = of_match_device(imx135_of_match, &client->dev);
if (!match) {
dev_err(&client->dev, "Failed to find matching dt id\n");
return NULL;
}
board_info_pdata = devm_kzalloc(&client->dev, sizeof(*board_info_pdata),
GFP_KERNEL);
if (!board_info_pdata) {
dev_err(&client->dev, "Failed to allocate pdata\n");
return NULL;
}
board_info_pdata->cam1_gpio = of_get_named_gpio(np, "cam1-gpios", 0);
board_info_pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
board_info_pdata->af_gpio = of_get_named_gpio(np, "af-gpios", 0);
board_info_pdata->ext_reg = of_property_read_bool(np, "nvidia,ext_reg");
board_info_pdata->power_on = imx135_power_on;
board_info_pdata->power_off = imx135_power_off;
return board_info_pdata;
}
static int
imx135_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct imx135_info *info;
int err;
const char *mclk_name;
pr_info("[IMX135]: probing sensor.\n");
info = devm_kzalloc(&client->dev,
sizeof(struct imx135_info), GFP_KERNEL);
if (!info) {
pr_err("%s:Unable to allocate memory!\n", __func__);
return -ENOMEM;
}
if (client->dev.of_node)
info->pdata = imx135_parse_dt(client);
else
info->pdata = client->dev.platform_data;
if (!info->pdata) {
pr_err("[IMX135]:%s:Unable to get platform data\n", __func__);
return -EFAULT;
}
info->i2c_client = client;
atomic_set(&info->in_use, 0);
info->mode = -1;
mclk_name = info->pdata->mclk_name ?
info->pdata->mclk_name : "default_mclk";
info->mclk = devm_clk_get(&client->dev, mclk_name);
if (IS_ERR(info->mclk)) {
dev_err(&client->dev, "%s: unable to get clock %s\n",
__func__, mclk_name);
return PTR_ERR(info->mclk);
}
imx135_power_get(info);
memcpy(&info->miscdev_info,
&imx135_device,
sizeof(struct miscdevice));
err = misc_register(&info->miscdev_info);
if (err) {
pr_err("%s:Unable to register misc device!\n", __func__);
goto imx135_probe_fail;
}
i2c_set_clientdata(client, info);
/* create debugfs interface */
imx135_create_debugfs(info);
return 0;
imx135_probe_fail:
imx135_power_put(&info->power);
return err;
}
static int
imx135_remove(struct i2c_client *client)
{
struct imx135_info *info;
info = i2c_get_clientdata(client);
misc_deregister(&imx135_device);
imx135_power_put(&info->power);
imx135_remove_debugfs(info);
return 0;
}
static const struct i2c_device_id imx135_id[] = {
{ "imx135", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, imx135_id);
static struct i2c_driver imx135_i2c_driver = {
.driver = {
.name = "imx135",
.owner = THIS_MODULE,
},
.probe = imx135_probe,
.remove = imx135_remove,
.id_table = imx135_id,
};
static int __init imx135_init(void)
{
pr_info("[IMX135] sensor driver loading\n");
return i2c_add_driver(&imx135_i2c_driver);
}
static void __exit imx135_exit(void)
{
i2c_del_driver(&imx135_i2c_driver);
}
module_init(imx135_init);
module_exit(imx135_exit);