/*
* Copyright (c) 2013-2014, 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZEOF_I2C_TRANSBUF 128
#define STATUS_BIT_MASK 0x02
#define STATUS_REG 0x0081
struct mt9m114_reg {
u16 cmd; /* command */
u16 addr;
u16 val;
};
static struct mt9m114_reg mode_1280x960_30fps[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x301A, 0x0234},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x1000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC97E, 0x01},
{MT9M114_SENSOR_WORD_WRITE, 0xC980, 0x0120},
{MT9M114_SENSOR_WORD_WRITE, 0xC982, 0x0700},
{MT9M114_SENSOR_WORD_WRITE, 0xC800, 0x0004},
{MT9M114_SENSOR_WORD_WRITE, 0xC802, 0x0004},
{MT9M114_SENSOR_WORD_WRITE, 0xC804, 0x03CB},
{MT9M114_SENSOR_WORD_WRITE, 0xC806, 0x050B},
{MT9M114_SENSOR_WORD_WRITE, 0xC808, 0x02DC},
{MT9M114_SENSOR_WORD_WRITE, 0xC80A, 0x6C00},
{MT9M114_SENSOR_WORD_WRITE, 0xC80C, 0x0001},
{MT9M114_SENSOR_WORD_WRITE, 0xC80E, 0x00DB},
{MT9M114_SENSOR_WORD_WRITE, 0xC810, 0x05B3},
{MT9M114_SENSOR_WORD_WRITE, 0xC812, 0x03EE},
{MT9M114_SENSOR_WORD_WRITE, 0xC814, 0x0636},
{MT9M114_SENSOR_WORD_WRITE, 0xC816, 0x0060},
{MT9M114_SENSOR_WORD_WRITE, 0xC818, 0x03C3},
{MT9M114_SENSOR_WORD_WRITE, 0xC826, 0x0020},
{MT9M114_SENSOR_WORD_WRITE, 0xC834, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC854, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC856, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC858, 0x0500},
{MT9M114_SENSOR_WORD_WRITE, 0xC85A, 0x03C0},
{MT9M114_SENSOR_BYTE_WRITE, 0xC85C, 0x03},
{MT9M114_SENSOR_WORD_WRITE, 0xC868, 0x0500},
{MT9M114_SENSOR_WORD_WRITE, 0xC86A, 0x03C0},
{MT9M114_SENSOR_WORD_WRITE, 0xC88C, 0x1E02},
{MT9M114_SENSOR_WORD_WRITE, 0xC88E, 0x0780},
{MT9M114_SENSOR_WORD_WRITE, 0xC914, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC916, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC918, 0x04FF},
{MT9M114_SENSOR_WORD_WRITE, 0xC91A, 0x03BF},
{MT9M114_SENSOR_WORD_WRITE, 0xC91C, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC91E, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC920, 0x00FF},
{MT9M114_SENSOR_WORD_WRITE, 0xC922, 0x00BF},
{MT9M114_SENSOR_BYTE_WRITE, 0xE801, 0x00},
{MT9M114_SENSOR_WORD_WRITE, 0x316A, 0x8270},
{MT9M114_SENSOR_WORD_WRITE, 0x316C, 0x8270},
{MT9M114_SENSOR_WORD_WRITE, 0x3ED0, 0x2305},
{MT9M114_SENSOR_WORD_WRITE, 0x3ED2, 0x77CF},
{MT9M114_SENSOR_WORD_WRITE, 0x316E, 0x8202},
{MT9M114_SENSOR_WORD_WRITE, 0x3180, 0x87FF},
{MT9M114_SENSOR_WORD_WRITE, 0x30D4, 0x6080},
{MT9M114_SENSOR_WORD_WRITE, 0xA802, 0x0008},
{MT9M114_SENSOR_WORD_WRITE, 0x3E14, 0xFF39},
{MT9M114_SENSOR_WORD_WRITE, 0x301A, 0x8234},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x495E},
{MT9M114_SENSOR_WORD_WRITE, 0xC95E, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0x3640, 0x02B0},
{MT9M114_SENSOR_WORD_WRITE, 0x3642, 0x9A6C},
{MT9M114_SENSOR_WORD_WRITE, 0x3644, 0x07F1},
{MT9M114_SENSOR_WORD_WRITE, 0x3646, 0x286B},
{MT9M114_SENSOR_WORD_WRITE, 0x3648, 0xA8AE},
{MT9M114_SENSOR_WORD_WRITE, 0x364A, 0x0210},
{MT9M114_SENSOR_WORD_WRITE, 0x364C, 0xDE6C},
{MT9M114_SENSOR_WORD_WRITE, 0x364E, 0x2D51},
{MT9M114_SENSOR_WORD_WRITE, 0x3650, 0x7DAC},
{MT9M114_SENSOR_WORD_WRITE, 0x3652, 0xBF2D},
{MT9M114_SENSOR_WORD_WRITE, 0x3654, 0x0310},
{MT9M114_SENSOR_WORD_WRITE, 0x3656, 0x42AB},
{MT9M114_SENSOR_WORD_WRITE, 0x3658, 0x5B10},
{MT9M114_SENSOR_WORD_WRITE, 0x365A, 0x166B},
{MT9M114_SENSOR_WORD_WRITE, 0x365C, 0x28EA},
{MT9M114_SENSOR_WORD_WRITE, 0x365E, 0x0190},
{MT9M114_SENSOR_WORD_WRITE, 0x3660, 0xDF2C},
{MT9M114_SENSOR_WORD_WRITE, 0x3662, 0x0B91},
{MT9M114_SENSOR_WORD_WRITE, 0x3664, 0x2C8B},
{MT9M114_SENSOR_WORD_WRITE, 0x3666, 0xECEE},
{MT9M114_SENSOR_WORD_WRITE, 0x3680, 0x9E69},
{MT9M114_SENSOR_WORD_WRITE, 0x3682, 0x3FAA},
{MT9M114_SENSOR_WORD_WRITE, 0x3684, 0x426B},
{MT9M114_SENSOR_WORD_WRITE, 0x3686, 0xEDAB},
{MT9M114_SENSOR_WORD_WRITE, 0x3688, 0x0BCF},
{MT9M114_SENSOR_WORD_WRITE, 0x368A, 0x4529},
{MT9M114_SENSOR_WORD_WRITE, 0x368C, 0x5F4B},
{MT9M114_SENSOR_WORD_WRITE, 0x368E, 0xD84D},
{MT9M114_SENSOR_WORD_WRITE, 0x3690, 0x16A9},
{MT9M114_SENSOR_WORD_WRITE, 0x3692, 0x4B0F},
{MT9M114_SENSOR_WORD_WRITE, 0x3694, 0x3E4C},
{MT9M114_SENSOR_WORD_WRITE, 0x3696, 0x910B},
{MT9M114_SENSOR_WORD_WRITE, 0x3698, 0xD58F},
{MT9M114_SENSOR_WORD_WRITE, 0x369A, 0x492C},
{MT9M114_SENSOR_WORD_WRITE, 0x369C, 0x6BB0},
{MT9M114_SENSOR_WORD_WRITE, 0x369E, 0x0FCB},
{MT9M114_SENSOR_WORD_WRITE, 0x36A0, 0x164C},
{MT9M114_SENSOR_WORD_WRITE, 0x36A2, 0xCA6E},
{MT9M114_SENSOR_WORD_WRITE, 0x36A4, 0x920C},
{MT9M114_SENSOR_WORD_WRITE, 0x36A6, 0x684F},
{MT9M114_SENSOR_WORD_WRITE, 0x36C0, 0x1751},
{MT9M114_SENSOR_WORD_WRITE, 0x36C2, 0x156E},
{MT9M114_SENSOR_WORD_WRITE, 0x36C4, 0xE671},
{MT9M114_SENSOR_WORD_WRITE, 0x36C6, 0xECEF},
{MT9M114_SENSOR_WORD_WRITE, 0x36C8, 0x5B93},
{MT9M114_SENSOR_WORD_WRITE, 0x36CA, 0x3E91},
{MT9M114_SENSOR_WORD_WRITE, 0x36CC, 0x10CE},
{MT9M114_SENSOR_WORD_WRITE, 0x36CE, 0xB171},
{MT9M114_SENSOR_WORD_WRITE, 0x36D0, 0xCB2D},
{MT9M114_SENSOR_WORD_WRITE, 0x36D2, 0x21B3},
{MT9M114_SENSOR_WORD_WRITE, 0x36D4, 0x0131},
{MT9M114_SENSOR_WORD_WRITE, 0x36D6, 0xEB4B},
{MT9M114_SENSOR_WORD_WRITE, 0x36D8, 0x8192},
{MT9M114_SENSOR_WORD_WRITE, 0x36DA, 0x2F4F},
{MT9M114_SENSOR_WORD_WRITE, 0x36DC, 0x7393},
{MT9M114_SENSOR_WORD_WRITE, 0x36DE, 0x18B1},
{MT9M114_SENSOR_WORD_WRITE, 0x36E0, 0x1CEE},
{MT9M114_SENSOR_WORD_WRITE, 0x36E2, 0xF111},
{MT9M114_SENSOR_WORD_WRITE, 0x36E4, 0x84F0},
{MT9M114_SENSOR_WORD_WRITE, 0x36E6, 0x5B33},
{MT9M114_SENSOR_WORD_WRITE, 0x3700, 0xD12C},
{MT9M114_SENSOR_WORD_WRITE, 0x3702, 0x320D},
{MT9M114_SENSOR_WORD_WRITE, 0x3704, 0x74D0},
{MT9M114_SENSOR_WORD_WRITE, 0x3706, 0x94D0},
{MT9M114_SENSOR_WORD_WRITE, 0x3708, 0xBE11},
{MT9M114_SENSOR_WORD_WRITE, 0x370A, 0xF66D},
{MT9M114_SENSOR_WORD_WRITE, 0x370C, 0x0C49},
{MT9M114_SENSOR_WORD_WRITE, 0x370E, 0x3551},
{MT9M114_SENSOR_WORD_WRITE, 0x3710, 0xAD0F},
{MT9M114_SENSOR_WORD_WRITE, 0x3712, 0xCBD2},
{MT9M114_SENSOR_WORD_WRITE, 0x3714, 0xEB8D},
{MT9M114_SENSOR_WORD_WRITE, 0x3716, 0x660E},
{MT9M114_SENSOR_WORD_WRITE, 0x3718, 0x1772},
{MT9M114_SENSOR_WORD_WRITE, 0x371A, 0xEC70},
{MT9M114_SENSOR_WORD_WRITE, 0x371C, 0xA5B3},
{MT9M114_SENSOR_WORD_WRITE, 0x371E, 0x912C},
{MT9M114_SENSOR_WORD_WRITE, 0x3720, 0x114C},
{MT9M114_SENSOR_WORD_WRITE, 0x3722, 0x5590},
{MT9M114_SENSOR_WORD_WRITE, 0x3724, 0xF84F},
{MT9M114_SENSOR_WORD_WRITE, 0x3726, 0xE670},
{MT9M114_SENSOR_WORD_WRITE, 0x3740, 0xAE90},
{MT9M114_SENSOR_WORD_WRITE, 0x3742, 0x8C8E},
{MT9M114_SENSOR_WORD_WRITE, 0x3744, 0x4073},
{MT9M114_SENSOR_WORD_WRITE, 0x3746, 0xB970},
{MT9M114_SENSOR_WORD_WRITE, 0x3748, 0x8174},
{MT9M114_SENSOR_WORD_WRITE, 0x374A, 0xA310},
{MT9M114_SENSOR_WORD_WRITE, 0x374C, 0x888F},
{MT9M114_SENSOR_WORD_WRITE, 0x374E, 0x1193},
{MT9M114_SENSOR_WORD_WRITE, 0x3750, 0x9431},
{MT9M114_SENSOR_WORD_WRITE, 0x3752, 0xCE73},
{MT9M114_SENSOR_WORD_WRITE, 0x3754, 0xAAD0},
{MT9M114_SENSOR_WORD_WRITE, 0x3756, 0x430E},
{MT9M114_SENSOR_WORD_WRITE, 0x3758, 0x10F4},
{MT9M114_SENSOR_WORD_WRITE, 0x375A, 0xF271},
{MT9M114_SENSOR_WORD_WRITE, 0x375C, 0x8835},
{MT9M114_SENSOR_WORD_WRITE, 0x375E, 0xD530},
{MT9M114_SENSOR_WORD_WRITE, 0x3760, 0xA3EE},
{MT9M114_SENSOR_WORD_WRITE, 0x3762, 0x48F3},
{MT9M114_SENSOR_WORD_WRITE, 0x3764, 0xE60F},
{MT9M114_SENSOR_WORD_WRITE, 0x3766, 0x8CD4},
{MT9M114_SENSOR_WORD_WRITE, 0x3784, 0x0258},
{MT9M114_SENSOR_WORD_WRITE, 0x3782, 0x0200},
{MT9M114_SENSOR_WORD_WRITE, 0x37C0, 0xF984},
{MT9M114_SENSOR_WORD_WRITE, 0x37C2, 0xDC68},
{MT9M114_SENSOR_WORD_WRITE, 0x37C4, 0xDB0A},
{MT9M114_SENSOR_WORD_WRITE, 0x37C6, 0x61C6},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC960, 0x0AF0},
{MT9M114_SENSOR_WORD_WRITE, 0xC962, 0x7841},
{MT9M114_SENSOR_WORD_WRITE, 0xC964, 0x59C0},
{MT9M114_SENSOR_WORD_WRITE, 0xC966, 0x7734},
{MT9M114_SENSOR_WORD_WRITE, 0xC968, 0x7328},
{MT9M114_SENSOR_WORD_WRITE, 0xC96A, 0x0FA0},
{MT9M114_SENSOR_WORD_WRITE, 0xC96C, 0x7FF8},
{MT9M114_SENSOR_WORD_WRITE, 0xC96E, 0x7F92},
{MT9M114_SENSOR_WORD_WRITE, 0xC970, 0x801C},
{MT9M114_SENSOR_WORD_WRITE, 0xC972, 0x7E4A},
{MT9M114_SENSOR_WORD_WRITE, 0xC974, 0x1964},
{MT9M114_SENSOR_WORD_WRITE, 0xC976, 0x7AD2},
{MT9M114_SENSOR_WORD_WRITE, 0xC978, 0x6F48},
{MT9M114_SENSOR_WORD_WRITE, 0xC97A, 0x7BE9},
{MT9M114_SENSOR_WORD_WRITE, 0xC97C, 0x7967},
{MT9M114_SENSOR_WORD_WRITE, 0xC95E, 0x0003},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC95E, 0x0003},
{MT9M114_SENSOR_WORD_WRITE, 0xC892, 0x01F7},
{MT9M114_SENSOR_WORD_WRITE, 0xC894, 0xFF89},
{MT9M114_SENSOR_WORD_WRITE, 0xC896, 0xFF80},
{MT9M114_SENSOR_WORD_WRITE, 0xC898, 0xFF84},
{MT9M114_SENSOR_WORD_WRITE, 0xC89A, 0x012C},
{MT9M114_SENSOR_WORD_WRITE, 0xC89C, 0x0050},
{MT9M114_SENSOR_WORD_WRITE, 0xC89E, 0xFF89},
{MT9M114_SENSOR_WORD_WRITE, 0xC8A0, 0xFEB0},
{MT9M114_SENSOR_WORD_WRITE, 0xC8A2, 0x02C7},
{MT9M114_SENSOR_WORD_WRITE, 0xC8C8, 0x006D},
{MT9M114_SENSOR_WORD_WRITE, 0xC8CA, 0x011B},
{MT9M114_SENSOR_WORD_WRITE, 0xC8A4, 0x021C},
{MT9M114_SENSOR_WORD_WRITE, 0xC8A6, 0xFF34},
{MT9M114_SENSOR_WORD_WRITE, 0xC8A8, 0xFFB0},
{MT9M114_SENSOR_WORD_WRITE, 0xC8AA, 0xFF87},
{MT9M114_SENSOR_WORD_WRITE, 0xC8AC, 0x0140},
{MT9M114_SENSOR_WORD_WRITE, 0xC8AE, 0x0039},
{MT9M114_SENSOR_WORD_WRITE, 0xC8B0, 0xFFC6},
{MT9M114_SENSOR_WORD_WRITE, 0xC8B2, 0xFF1D},
{MT9M114_SENSOR_WORD_WRITE, 0xC8B4, 0x021D},
{MT9M114_SENSOR_WORD_WRITE, 0xC8CC, 0x0094},
{MT9M114_SENSOR_WORD_WRITE, 0xC8CE, 0x00FF},
{MT9M114_SENSOR_WORD_WRITE, 0xC8B6, 0x01FF},
{MT9M114_SENSOR_WORD_WRITE, 0xC8B8, 0xFF55},
{MT9M114_SENSOR_WORD_WRITE, 0xC8BA, 0xFFAB},
{MT9M114_SENSOR_WORD_WRITE, 0xC8BC, 0xFF9F},
{MT9M114_SENSOR_WORD_WRITE, 0xC8BE, 0x0181},
{MT9M114_SENSOR_WORD_WRITE, 0xC8C0, 0xFFE0},
{MT9M114_SENSOR_WORD_WRITE, 0xC8C2, 0xFFF3},
{MT9M114_SENSOR_WORD_WRITE, 0xC8C4, 0xFF77},
{MT9M114_SENSOR_WORD_WRITE, 0xC8C6, 0x0196},
{MT9M114_SENSOR_WORD_WRITE, 0xC8D0, 0x00AD},
{MT9M114_SENSOR_WORD_WRITE, 0xC8D2, 0x008D},
{MT9M114_SENSOR_WORD_WRITE, 0xC8D4, 0x09C4},
{MT9M114_SENSOR_WORD_WRITE, 0xC8D6, 0x0D67},
{MT9M114_SENSOR_WORD_WRITE, 0xC8D8, 0x1964},
{MT9M114_SENSOR_WORD_WRITE, 0xC8EC, 0x09C4},
{MT9M114_SENSOR_WORD_WRITE, 0xC8EE, 0x1964},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F2, 0x0003},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F3, 0x0002},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F4, 0xF800},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F6, 0xA800},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F8, 0x7C00},
{MT9M114_SENSOR_WORD_WRITE, 0xC8FA, 0x3580},
{MT9M114_SENSOR_WORD_WRITE, 0xC8FC, 0x0DC0},
{MT9M114_SENSOR_WORD_WRITE, 0xC8FE, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC900, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC902, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC904, 0x0031},
{MT9M114_SENSOR_WORD_WRITE, 0xC906, 0x002A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC90C, 0xB0},
{MT9M114_SENSOR_BYTE_WRITE, 0xC90D, 0x98},
{MT9M114_SENSOR_BYTE_WRITE, 0xC90E, 0x80},
{MT9M114_SENSOR_WORD_WRITE, 0xC926, 0x0020},
{MT9M114_SENSOR_WORD_WRITE, 0xC928, 0x009A},
{MT9M114_SENSOR_WORD_WRITE, 0xC946, 0x0070},
{MT9M114_SENSOR_WORD_WRITE, 0xC948, 0x00F3},
{MT9M114_SENSOR_WORD_WRITE, 0xC952, 0x0020},
{MT9M114_SENSOR_WORD_WRITE, 0xC954, 0x009A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92A, 0x80},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92B, 0x4B},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92C, 0x00},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92D, 0xFF},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92E, 0x3C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC92F, 0x02},
{MT9M114_SENSOR_BYTE_WRITE, 0xC930, 0x06},
{MT9M114_SENSOR_BYTE_WRITE, 0xC931, 0x64},
{MT9M114_SENSOR_BYTE_WRITE, 0xC932, 0x01},
{MT9M114_SENSOR_BYTE_WRITE, 0xC933, 0x0C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC934, 0x3C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC935, 0x3C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC936, 0x3C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC937, 0x0F},
{MT9M114_SENSOR_BYTE_WRITE, 0xC938, 0x64},
{MT9M114_SENSOR_BYTE_WRITE, 0xC939, 0x64},
{MT9M114_SENSOR_BYTE_WRITE, 0xC93A, 0x64},
{MT9M114_SENSOR_BYTE_WRITE, 0xC93B, 0x32},
{MT9M114_SENSOR_WORD_WRITE, 0xC93C, 0x0020},
{MT9M114_SENSOR_WORD_WRITE, 0xC93E, 0x009A},
{MT9M114_SENSOR_WORD_WRITE, 0xC940, 0x00DC},
{MT9M114_SENSOR_BYTE_WRITE, 0xC942, 0x38},
{MT9M114_SENSOR_BYTE_WRITE, 0xC943, 0x30},
{MT9M114_SENSOR_BYTE_WRITE, 0xC944, 0x50},
{MT9M114_SENSOR_BYTE_WRITE, 0xC945, 0x19},
{MT9M114_SENSOR_WORD_WRITE, 0xC94A, 0x0230},
{MT9M114_SENSOR_WORD_WRITE, 0xC94C, 0x0010},
{MT9M114_SENSOR_WORD_WRITE, 0xC94E, 0x01CD},
{MT9M114_SENSOR_BYTE_WRITE, 0xC950, 0x05},
{MT9M114_SENSOR_BYTE_WRITE, 0xC951, 0x40},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x1B},
{MT9M114_SENSOR_BYTE_WRITE, 0xC878, 0x0E},
{MT9M114_SENSOR_WORD_WRITE, 0xC890, 0x0080},
{MT9M114_SENSOR_WORD_WRITE, 0xC886, 0x0100},
{MT9M114_SENSOR_WORD_WRITE, 0xC87C, 0x005A},
{MT9M114_SENSOR_BYTE_WRITE, 0xB42A, 0x05},
{MT9M114_SENSOR_BYTE_WRITE, 0xA80A, 0x20},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_WORD_WRITE, 0xC984, 0x8041},
{MT9M114_SENSOR_WORD_WRITE, 0x001E, 0x0777},
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xDC00},
{MT9M114_SENSOR_WORD_WRITE, 0xC988, 0x0F00},
{MT9M114_SENSOR_WORD_WRITE, 0xC98A, 0x0B07},
{MT9M114_SENSOR_WORD_WRITE, 0xC98C, 0x0D01},
{MT9M114_SENSOR_WORD_WRITE, 0xC98E, 0x071D},
{MT9M114_SENSOR_WORD_WRITE, 0xC990, 0x0006},
{MT9M114_SENSOR_WORD_WRITE, 0xC992, 0x0A0C},
{MT9M114_SENSOR_WORD_WRITE, 0xA802, 0x0008},
{MT9M114_SENSOR_BYTE_WRITE, 0xC908, 0x01},
{MT9M114_SENSOR_BYTE_WRITE, 0xC879, 0x01},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x02},
{MT9M114_SENSOR_BYTE_WRITE, 0xA80A, 0x18},
{MT9M114_SENSOR_BYTE_WRITE, 0xA80B, 0x18},
{MT9M114_SENSOR_BYTE_WRITE, 0xAC16, 0x18},
{MT9M114_SENSOR_BYTE_WRITE, 0xC878, 0x0E},
{MT9M114_SENSOR_BYTE_WRITE, 0xDC00, 0x28},
{MT9M114_SENSOR_WAIT_MS, 0, 50},
{MT9M114_SENSOR_WORD_WRITE, 0x0080, 0x8002},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_Whitebalance_Auto[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x02},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_Whitebalance_Cloudy[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x00},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F0, 0x1964},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_Whitebalance_Daylight[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x00},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F0, 0x1964},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_Whitebalance_Incandescent[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x00},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F0, 0x0A8C},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_Whitebalance_Fluorescent[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0x0000},
{MT9M114_SENSOR_BYTE_WRITE, 0xC909, 0x00},
{MT9M114_SENSOR_WORD_WRITE, 0xC8F0, 0x0E74},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_EV_zero[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xC87A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87A, 0x3C},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x1E},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_EV_plus_1[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xC87A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87A, 0x42},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x21},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_EV_plus_2[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xC87A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87A, 0x48},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x24},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_EV_minus_1[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xC87A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87A, 0x36},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x1B},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
static struct mt9m114_reg mt9m114_EV_minus_2[] = {
{MT9M114_SENSOR_WORD_WRITE, 0x098E, 0xC87A},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87A, 0x32},
{MT9M114_SENSOR_BYTE_WRITE, 0xC87B, 0x19},
{MT9M114_SENSOR_TABLE_END, 0x0000}
};
struct mt9m114_info {
struct miscdevice miscdev_info;
struct mt9m114_power_rail power;
struct mt9m114_sensordata sensor_data;
struct i2c_client *i2c_client;
struct mt9m114_platform_data *pdata;
struct regmap *regmap;
atomic_t in_use;
const struct mt9m114_reg *mode;
u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF];
struct clk *mclk;
struct sysedp_consumer *sysedpc;
};
struct mt9m114_mode_desc {
u16 xres;
u16 yres;
const struct mt9m114_reg *mode_tbl;
struct mt9m114_modeinfo mode_info;
};
static struct mt9m114_mode_desc mode_table[] = {
{
.xres = 1280,
.yres = 960,
.mode_tbl = mode_1280x960_30fps,
},
{ },
};
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
};
static long mt9m114_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
static inline void mt9m114_msleep(u32 t)
{
usleep_range(t*1000, t*1000 + 500);
}
static int mt9m114_read_reg(struct mt9m114_info *info,
unsigned int addr, unsigned int *val)
{
dev_dbg(&info->i2c_client->dev, "0x%x = %p\n", addr, val);
return regmap_read(info->regmap, addr, val);
}
static int mt9m114_write_reg8(struct mt9m114_info *info, u16 addr, u8 val)
{
dev_dbg(&info->i2c_client->dev, "0x%x = 0x%x\n", addr, val);
return regmap_write(info->regmap, addr, val);
}
static int mt9m114_write_reg16(struct mt9m114_info *info, u16 addr, u16 val)
{
unsigned char data[2];
data[0] = (u8) (val >> 8);
data[1] = (u8) (val & 0xff);
dev_dbg(&info->i2c_client->dev, "0x%x = 0x%x\n", addr, val);
return regmap_raw_write(info->regmap, addr, data, sizeof(data));
}
static int mt9m114_write_table(
struct mt9m114_info *info,
const struct mt9m114_reg table[])
{
int err = 0;
const struct mt9m114_reg *next;
u16 val;
dev_dbg(&info->i2c_client->dev, "yuv %s\n", __func__);
for (next = table; next->cmd != MT9M114_SENSOR_TABLE_END; next++) {
if (next->cmd == MT9M114_SENSOR_WAIT_MS) {
msleep(next->val);
continue;
}
val = next->val;
if (next->cmd == MT9M114_SENSOR_BYTE_WRITE)
err = mt9m114_write_reg8(info, next->addr, val);
else if (next->cmd == MT9M114_SENSOR_WORD_WRITE)
err = mt9m114_write_reg16(info, next->addr, val);
if (err)
return err;
}
return 0;
}
static void mt9m114_mclk_disable(struct mt9m114_info *info)
{
dev_info(&info->i2c_client->dev, "%s: disable MCLK\n", __func__);
clk_disable_unprepare(info->mclk);
}
static int mt9m114_mclk_enable(struct mt9m114_info *info)
{
int err;
unsigned long mclk_init_rate = 24000000;
dev_info(&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 int mt9m114_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct mt9m114_info *info = dev_get_drvdata(miscdev->parent);
dev_dbg(&info->i2c_client->dev, "mt9m114: open.\n");
info = container_of(miscdev, struct mt9m114_info, miscdev_info);
/* check if the device is in use */
if (atomic_xchg(&info->in_use, 1)) {
dev_info(&info->i2c_client->dev, "%s:BUSY!\n", __func__);
return -EBUSY;
}
file->private_data = info;
if (info->pdata && info->pdata->power_on) {
mt9m114_mclk_enable(info);
info->pdata->power_on(&info->power);
} else {
dev_err(&info->i2c_client->dev,
"%s:no valid power_on function.\n", __func__);
return -EFAULT;
}
return 0;
}
int mt9m114_release(struct inode *inode, struct file *file)
{
struct mt9m114_info *info = file->private_data;
if (info->pdata && info->pdata->power_off) {
info->pdata->power_off(&info->power);
mt9m114_mclk_disable(info);
sysedp_set_state(info->sysedpc, 0);
}
file->private_data = NULL;
/* warn if device is already released */
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static int mt9m114_regulator_get(struct mt9m114_info *info,
struct regulator **vreg, char vreg_name[])
{
struct regulator *reg = NULL;
int err = 0;
reg = devm_regulator_get(&info->i2c_client->dev, vreg_name);
if (unlikely(IS_ERR(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 mt9m114_power_get(struct mt9m114_info *info)
{
struct mt9m114_power_rail *pw = &info->power;
dev_dbg(&info->i2c_client->dev, "mt9m114: %s\n", __func__);
/* note: mt9m114 uses i2c address 0x90, which is different from
* most of other sensors that using 0x64 or 0x20.
*
* This needs us to define a new vif2 as 2-0048
* for platform board file that uses mt9m114
* otherwise below could not get the regulator
*
* This rails of "vif2" and "vana" can be modified as needed
* for a new platform.
*
* mt9m114: need to get 1.8v first
*/
mt9m114_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */
mt9m114_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.8v */
return 0;
}
static const struct file_operations mt9m114_fileops = {
.owner = THIS_MODULE,
.open = mt9m114_open,
.unlocked_ioctl = mt9m114_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mt9m114_ioctl,
#endif
.release = mt9m114_release,
};
static struct miscdevice mt9m114_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mt9m114",
.fops = &mt9m114_fileops,
};
static struct mt9m114_modeinfo def_modeinfo = {
.xres = 1280,
.yres = 960,
};
static struct mt9m114_mode_desc *mt9m114_get_mode(
struct mt9m114_info *info, struct mt9m114_mode *mode)
{
struct mt9m114_mode_desc *mt = mode_table;
while (mt->xres) {
if ((mt->xres == mode->xres) &&
(mt->yres == mode->yres))
break;
mt++;
}
if (!mt->xres)
mt = NULL;
return mt;
}
static int mt9m114_mode_info_init(struct mt9m114_info *info)
{
struct mt9m114_mode_desc *md = mode_table;
const struct mt9m114_reg *mt;
struct mt9m114_modeinfo *mi;
dev_dbg(&info->i2c_client->dev, "%s", __func__);
while (md->xres) {
mi = &md->mode_info;
mt = md->mode_tbl;
memcpy(mi, &def_modeinfo, sizeof(*mi));
dev_dbg(&info->i2c_client->dev, "mode %d x %d ",
md->xres, md->yres);
mi->xres = md->xres;
mi->yres = md->yres;
md++;
}
return 0;
}
static int mt9m114_set_mode(struct mt9m114_info *info,
struct mt9m114_mode *mode)
{
struct mt9m114_mode_desc *sensor_mode;
int err = 0;
unsigned int val = 0;
int count = 10;
dev_info(&info->i2c_client->dev,
"%s: xres %u yres %u\n", __func__, mode->xres, mode->yres);
sensor_mode = mt9m114_get_mode(info, mode);
if (sensor_mode == NULL) {
dev_err(&info->i2c_client->dev,
"%s: invalid params supplied to set mode %d %d\n",
__func__, mode->xres, mode->yres);
return -EINVAL;
}
sysedp_set_state(info->sysedpc, 1);
/* polling the status to check if the sensor is ready. */
err = mt9m114_read_reg(info, STATUS_REG, &val);
if (err)
return err;
while ((val & STATUS_BIT_MASK) != 0 && count > 0) {
mt9m114_msleep(5);
err = mt9m114_read_reg(info, STATUS_REG, &val);
if (err)
return err;
count--;
}
if (count == 0) {
dev_err(&info->i2c_client->dev,
"%s: status register polling timed out\n",
__func__);
return -EFAULT;
}
err = mt9m114_write_table(
info, sensor_mode->mode_tbl);
if (err)
return err;
info->mode = sensor_mode->mode_tbl;
return 0;
}
static int mt9m114_min_frame_rate_reg(
struct mt9m114_info *info,
u16 min_frame_rate)
{
int err = 0;
err += mt9m114_write_reg16(info, 0xC88E, min_frame_rate);
err += mt9m114_write_reg16(info, 0x0080, 0x8002);
return err;
}
static long mt9m114_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct mt9m114_info *info = file->private_data;
switch (_IOC_NR(cmd)) {
case _IOC_NR(MT9M114_SENSOR_IOCTL_SET_MODE):
{
struct mt9m114_mode mode;
dev_dbg(&info->i2c_client->dev, "MT9M114_IOCTL_SET_MODE\n");
if (copy_from_user(&mode, (const void __user *)arg,
sizeof(struct mt9m114_mode))) {
err = -EFAULT;
break;
}
err = mt9m114_set_mode(info, &mode);
break;
}
case _IOC_NR(MT9M114_SENSOR_IOCTL_SET_WHITE_BALANCE):
{
u8 whitebalance;
if (copy_from_user(&whitebalance, (const void __user *)arg,
sizeof(whitebalance))) {
return -EFAULT;
}
switch (whitebalance) {
case MT9M114_YUV_Whitebalance_Auto:
err = mt9m114_write_table(info,
mt9m114_Whitebalance_Auto);
break;
case MT9M114_YUV_Whitebalance_Daylight:
err = mt9m114_write_table(info,
mt9m114_Whitebalance_Daylight);
break;
case MT9M114_YUV_Whitebalance_CloudyDaylight:
err = mt9m114_write_table(info,
mt9m114_Whitebalance_Cloudy);
break;
case MT9M114_YUV_Whitebalance_Incandescent:
err = mt9m114_write_table(info,
mt9m114_Whitebalance_Incandescent);
break;
case MT9M114_YUV_Whitebalance_Fluorescent:
err = mt9m114_write_table(info,
mt9m114_Whitebalance_Fluorescent);
break;
default:
break;
}
if (err)
return err;
return 0;
}
case _IOC_NR(MT9M114_SENSOR_IOCTL_SET_EV):
{
short ev;
if (copy_from_user(&ev,
(const void __user *)arg,
sizeof(short)))
return -EFAULT;
if (ev == -2)
err = mt9m114_write_table(info, mt9m114_EV_minus_2);
else if (ev == -1)
err = mt9m114_write_table(info, mt9m114_EV_minus_1);
else if (ev == 0)
err = mt9m114_write_table(info, mt9m114_EV_zero);
else if (ev == 1)
err = mt9m114_write_table(info, mt9m114_EV_plus_1);
else if (ev == 2)
err = mt9m114_write_table(info, mt9m114_EV_plus_2);
else
err = -1;
if (err)
return err;
return 0;
}
case _IOC_NR(MT9M114_SENSOR_IOCTL_SET_MIN_FPS):
{
u16 min_frame_rate;
if (copy_from_user(&min_frame_rate,
(const void __user *)arg,
sizeof(u16)))
return -EFAULT;
err = mt9m114_min_frame_rate_reg(info, min_frame_rate);
if (err)
return err;
return 0;
}
default:
dev_dbg(&info->i2c_client->dev, "INVALID IOCTL\n");
err = -EINVAL;
}
if (err)
dev_err(&info->i2c_client->dev,
"%s - %x: ERR = %d\n", __func__, cmd, err);
return err;
}
static int mt9m114_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct mt9m114_info *info;
const char *mclk_name;
dev_dbg(&client->dev, "mt9m114: probing sensor.\n");
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
dev_err(&client->dev, "%s: kzalloc error\n", __func__);
return -ENOMEM;
}
info->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(&client->dev,
"regmap init failed: %ld\n", PTR_ERR(info->regmap));
return -ENODEV;
}
info->pdata = client->dev.platform_data;
info->i2c_client = client;
atomic_set(&info->in_use, 0);
info->mode = NULL;
i2c_set_clientdata(client, info);
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);
}
mt9m114_power_get(info);
info->sysedpc = sysedp_create_consumer("mt9m114", "mt9m114");
mt9m114_mode_info_init(info);
memcpy(&info->miscdev_info,
&mt9m114_device,
sizeof(struct miscdevice));
err = misc_register(&info->miscdev_info);
if (err) {
dev_err(&info->i2c_client->dev, "mt9m114: Unable to register misc device!\n");
kfree(info);
return err;
}
return 0;
}
static int mt9m114_remove(struct i2c_client *client)
{
struct mt9m114_info *info;
info = i2c_get_clientdata(client);
misc_deregister(&mt9m114_device);
sysedp_free_consumer(info->sysedpc);
kfree(info);
return 0;
}
static const struct i2c_device_id mt9m114_id[] = {
{ "mt9m114", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, mt9m114_id);
static struct i2c_driver mt9m114_i2c_driver = {
.driver = {
.name = "mt9m114",
.owner = THIS_MODULE,
},
.probe = mt9m114_probe,
.remove = mt9m114_remove,
.id_table = mt9m114_id,
};
static int __init mt9m114_init(void)
{
pr_info("mt9m114 sensor driver loading\n");
return i2c_add_driver(&mt9m114_i2c_driver);
}
static void __exit mt9m114_exit(void)
{
i2c_del_driver(&mt9m114_i2c_driver);
}
module_init(mt9m114_init);
module_exit(mt9m114_exit);
MODULE_LICENSE("GPL v2");