/*
* ar0261.c - ar0261 sensor driver
*
* 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
struct ar0261_reg {
u16 addr;
u16 val;
};
struct ar0261_info {
struct miscdevice miscdev_info;
struct ar0261_power_rail power;
struct ar0261_sensordata sensor_data;
struct i2c_client *i2c_client;
struct ar0261_platform_data *pdata;
struct clk *mclk;
struct regmap *regmap;
atomic_t in_use;
int mode;
};
#define AR0261_TABLE_WAIT_MS 0
#define AR0261_TABLE_END 1
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
};
static struct ar0261_reg mode_1920x1080[] = {
{0x301A, 0x0019},
{AR0261_TABLE_WAIT_MS, 10},
{0x301A, 0x0218},
{0x31B0, 0x0062},
{0x31B2, 0x0046},
{0x31B4, 0x3248},
{0x31B6, 0x22A6},
{0x31B8, 0x1832},
{0x31BA, 0x1052},
{0x31BC, 0x0408},
{0x31AE, 0x0201},
{AR0261_TABLE_WAIT_MS, 1},
{0x3044, 0x0590},
{0x3EE6, 0x60AD},
{0x3EDC, 0xDBFA},
{0x301A, 0x0218},
{AR0261_TABLE_WAIT_MS, 10},
{0x3D00, 0x0481},
{0x3D02, 0xFFFF},
{0x3D04, 0xFFFF},
{0x3D06, 0xFFFF},
{0x3D08, 0x6600},
{0x3D0A, 0x0311},
{0x3D0C, 0x8C67},
{0x3D0E, 0x0808},
{0x3D10, 0x4380},
{0x3D12, 0x4343},
{0x3D14, 0x8043},
{0x3D16, 0x4330},
{0x3D18, 0x0543},
{0x3D1A, 0x4381},
{0x3D1C, 0x4C85},
{0x3D1E, 0x2022},
{0x3D20, 0x8020},
{0x3D22, 0xA093},
{0x3D24, 0x5A8A},
{0x3D26, 0x4C81},
{0x3D28, 0x5981},
{0x3D2A, 0x1E00},
{0x3D2C, 0x5F83},
{0x3D2E, 0x5C80},
{0x3D30, 0x5C81},
{0x3D32, 0x5F58},
{0x3D34, 0x6880},
{0x3D36, 0x1060},
{0x3D38, 0x8541},
{0x3D3A, 0xB350},
{0x3D3C, 0x5F10},
{0x3D3E, 0x6050},
{0x3D40, 0x5780},
{0x3D42, 0x6880},
{0x3D44, 0x2220},
{0x3D46, 0x805D},
{0x3D48, 0x8140},
{0x3D4A, 0x864B},
{0x3D4C, 0x8524},
{0x3D4E, 0x08A0},
{0x3D50, 0x55B8},
{0x3D52, 0x429C},
{0x3D54, 0x4281},
{0x3D56, 0x4081},
{0x3D58, 0x2808},
{0x3D5A, 0x2810},
{0x3D5C, 0x5727},
{0x3D5E, 0x1069},
{0x3D60, 0x4B52},
{0x3D62, 0x8265},
{0x3D64, 0x8A65},
{0x3D66, 0xA95E},
{0x3D68, 0x5080},
{0x3D6A, 0x5250},
{0x3D6C, 0x6080},
{0x3D6E, 0x6922},
{0x3D70, 0x2080},
{0x3D72, 0x5D80},
{0x3D74, 0x4080},
{0x3D76, 0x5681},
{0x3D78, 0x5781},
{0x3D7A, 0x4B86},
{0x3D7C, 0x2408},
{0x3D7E, 0x9345},
{0x3D80, 0x8144},
{0x3D82, 0x4481},
{0x3D84, 0x4586},
{0x3D86, 0x4E80},
{0x3D88, 0x4FCD},
{0x3D8A, 0x4685},
{0x3D8C, 0x0006},
{0x3D8E, 0x8143},
{0x3D90, 0x4380},
{0x3D92, 0x4343},
{0x3D94, 0x8043},
{0x3D96, 0x4380},
{0x3D98, 0x4343},
{0x3D9A, 0x8043},
{0x3D9C, 0x4380},
{0x3D9E, 0x4343},
{0x3DA0, 0x8648},
{0x3DA2, 0x4880},
{0x3DA4, 0x6B6B},
{0x3DA6, 0x814C},
{0x3DA8, 0x864D},
{0x3DAA, 0xA442},
{0x3DAC, 0x8641},
{0x3DAE, 0x804D},
{0x3DB0, 0x864C},
{0x3DB2, 0x8A45},
{0x3DB4, 0x8144},
{0x3DB6, 0x4481},
{0x3DB8, 0x4583},
{0x3DBA, 0x46B7},
{0x3DBC, 0x7386},
{0x3DBE, 0x4685},
{0x3DC0, 0x0006},
{0x3DC2, 0x8143},
{0x3DC4, 0x4380},
{0x3DC6, 0x4343},
{0x3DC8, 0x8043},
{0x3DCA, 0x4380},
{0x3DCC, 0x4343},
{0x3DCE, 0x8043},
{0x3DD0, 0x4380},
{0x3DD2, 0x4343},
{0x3DD4, 0x8648},
{0x3DD6, 0x4880},
{0x3DD8, 0x6A6A},
{0x3DDA, 0x814C},
{0x3DDC, 0x864D},
{0x3DDE, 0xA442},
{0x3DE0, 0x8641},
{0x3DE2, 0x804D},
{0x3DE4, 0x864C},
{0x3DE6, 0x8A45},
{0x3DE8, 0x8144},
{0x3DEA, 0x4481},
{0x3DEC, 0x4583},
{0x3DEE, 0x4686},
{0x3DF0, 0x73FF},
{0x3DF2, 0xD358},
{0x3DF4, 0x835B},
{0x3DF6, 0x825A},
{0x3DF8, 0x8153},
{0x3DFA, 0x5467},
{0x3DFC, 0x6363},
{0x3DFE, 0x2640},
{0x3E00, 0x6470},
{0x3E02, 0xFFFF},
{0x3E04, 0xFFFF},
{0x3E06, 0xFFED},
{0x3E08, 0x4580},
{0x3E0A, 0x4384},
{0x3E0C, 0x4380},
{0x3E0E, 0x0280},
{0x3E10, 0x8402},
{0x3E12, 0x8080},
{0x3E14, 0x6A84},
{0x3E16, 0x6A80},
{0x3E18, 0x4484},
{0x3E1A, 0x4480},
{0x3E1C, 0x4578},
{0x3E1E, 0x8270},
{0x3E20, 0x0000},
{0x3E22, 0x0000},
{0x3E24, 0x0000},
{0x3E26, 0x0000},
{0x3E28, 0x0000},
{0x3E2A, 0x0000},
{0x3E2C, 0x0000},
{0x3E2E, 0x0000},
{0x3E30, 0x0000},
{0x3E32, 0x0000},
{0x3E34, 0x0000},
{0x3E36, 0x0000},
{0x3E38, 0x0000},
{0x3E3A, 0x0000},
{0x3E3C, 0x0000},
{0x3E3E, 0x0000},
{0x3E40, 0x0000},
{0x3E42, 0x0000},
{0x3E44, 0x0000},
{0x3E46, 0x0000},
{0x3E48, 0x0000},
{0x3E4A, 0x0000},
{0x3E4C, 0x0000},
{0x3E4E, 0x0000},
{0x3E50, 0x0000},
{0x3E52, 0x0000},
{0x3E54, 0x0000},
{0x3E56, 0x0000},
{0x3E58, 0x0000},
{0x3E5A, 0x0000},
{0x3E5C, 0x0000},
{0x3E5E, 0x0000},
{0x3E60, 0x0000},
{0x3E62, 0x0000},
{0x3E64, 0x0000},
{0x3E66, 0x0000},
{0x3E68, 0x0000},
{0x3E6A, 0x0000},
{0x3E6C, 0x0000},
{0x3E6E, 0x0000},
{0x3E70, 0x0000},
{0x3E72, 0x0000},
{0x3E74, 0x0000},
{0x3E76, 0x0000},
{0x3E78, 0x0000},
{0x3E7A, 0x0000},
{0x3E7C, 0x0000},
{0x3E7E, 0x0000},
{0x3E80, 0x0000},
{0x3E82, 0x0000},
{0x3E84, 0x0000},
{0x3E86, 0x0000},
{0x3E88, 0x0000},
{0x3E8A, 0x0000},
{0x3E8C, 0x0000},
{0x3E8E, 0x0000},
{0x3E90, 0x0000},
{0x3E92, 0x0000},
{0x3E94, 0x0000},
{0x3E96, 0x0000},
{0x3E98, 0x0000},
{0x3E9A, 0x0000},
{0x3E9C, 0x0000},
{0x3E9E, 0x0000},
{0x3EA0, 0x0000},
{0x3EA2, 0x0000},
{0x3EA4, 0x0000},
{0x3EA6, 0x0000},
{0x3EA8, 0x0000},
{0x3EAA, 0x0000},
{0x3EAC, 0x0000},
{0x3EAE, 0x0000},
{0x3EB0, 0x0000},
{0x3EB2, 0x0000},
{0x3EB4, 0x0000},
{0x3EB6, 0x0000},
{0x3EB8, 0x0000},
{0x3EBA, 0x0000},
{0x3EBC, 0x0000},
{0x3EBE, 0x0000},
{0x3EC0, 0x0000},
{0x3EC2, 0x0000},
{0x3EC4, 0x0000},
{0x3EC6, 0x0000},
{0x3EC8, 0x0000},
{0x3ECA, 0x0000},
{0x301A, 0x021C},
{0x0342, 0x10CC},
{0x0340, 0x04A4},
{0x0202, 0x0496},
{0x0312, 0x045D},
{0x31AE, 0x0201},
{0x0300, 0x0005},
{0x0302, 0x0001},
{0x0304, 0x0202},
{0x0306, 0x4040},
{0x0308, 0x000A},
{0x030A, 0x0001},
{0x0344, 0x0008},
{0x0348, 0x0787},
{0x0346, 0x0008},
{0x034A, 0x043F},
{0x034C, 0x0780},
{0x034E, 0x0438},
{0x3040, 0x0041},
{0x0104, 0x0001},
{0x3ECC, 0x008F},
{0x3ECE, 0xA8F0},
{0x3ED0, 0xFFFF},
{0x3ED6, 0x7193},
{0x3ED8, 0x8A11},
{0x30D2, 0x0020},
{0x30D4, 0x0040},
{0x3180, 0x80FF},
{0x0104, 0x0000},
{0x301A, 0x001C},
{AR0261_TABLE_END, 0x0000}
};
static struct ar0261_reg mode_1920x1080_HDR[] = {
{0x301A, 0x0019},
{AR0261_TABLE_WAIT_MS, 10},
{0x301A, 0x0218},
{0x31B0, 0x0062},
{0x31B2, 0x0046},
{0x31B4, 0x3248},
{0x31B6, 0x22A6},
{0x31B8, 0x1832},
{0x31BA, 0x1052},
{0x31BC, 0x0408},
{0x31AE, 0x0201},
{AR0261_TABLE_WAIT_MS, 1},
{0x3044, 0x0590},
{0x3EE6, 0x60AD},
{0x3EDC, 0xDBFA},
{0x301A, 0x0218},
{AR0261_TABLE_WAIT_MS, 10},
{0x3D00, 0x0481},
{0x3D02, 0xFFFF},
{0x3D04, 0xFFFF},
{0x3D06, 0xFFFF},
{0x3D08, 0x6600},
{0x3D0A, 0x0311},
{0x3D0C, 0x8C67},
{0x3D0E, 0x0808},
{0x3D10, 0x4380},
{0x3D12, 0x4343},
{0x3D14, 0x8043},
{0x3D16, 0x4330},
{0x3D18, 0x0543},
{0x3D1A, 0x4381},
{0x3D1C, 0x4C85},
{0x3D1E, 0x2022},
{0x3D20, 0x8020},
{0x3D22, 0xA093},
{0x3D24, 0x5A8A},
{0x3D26, 0x4C81},
{0x3D28, 0x5981},
{0x3D2A, 0x1E00},
{0x3D2C, 0x5F83},
{0x3D2E, 0x5C80},
{0x3D30, 0x5C81},
{0x3D32, 0x5F58},
{0x3D34, 0x6880},
{0x3D36, 0x1060},
{0x3D38, 0x8541},
{0x3D3A, 0xB350},
{0x3D3C, 0x5F10},
{0x3D3E, 0x6050},
{0x3D40, 0x5780},
{0x3D42, 0x6880},
{0x3D44, 0x2220},
{0x3D46, 0x805D},
{0x3D48, 0x8140},
{0x3D4A, 0x864B},
{0x3D4C, 0x8524},
{0x3D4E, 0x08A0},
{0x3D50, 0x55B8},
{0x3D52, 0x429C},
{0x3D54, 0x4281},
{0x3D56, 0x4081},
{0x3D58, 0x2808},
{0x3D5A, 0x2810},
{0x3D5C, 0x5727},
{0x3D5E, 0x1069},
{0x3D60, 0x4B52},
{0x3D62, 0x8265},
{0x3D64, 0x8A65},
{0x3D66, 0xA95E},
{0x3D68, 0x5080},
{0x3D6A, 0x5250},
{0x3D6C, 0x6080},
{0x3D6E, 0x6922},
{0x3D70, 0x2080},
{0x3D72, 0x5D80},
{0x3D74, 0x4080},
{0x3D76, 0x5681},
{0x3D78, 0x5781},
{0x3D7A, 0x4B86},
{0x3D7C, 0x2408},
{0x3D7E, 0x9345},
{0x3D80, 0x8144},
{0x3D82, 0x4481},
{0x3D84, 0x4586},
{0x3D86, 0x4E80},
{0x3D88, 0x4FCD},
{0x3D8A, 0x4685},
{0x3D8C, 0x0006},
{0x3D8E, 0x8143},
{0x3D90, 0x4380},
{0x3D92, 0x4343},
{0x3D94, 0x8043},
{0x3D96, 0x4380},
{0x3D98, 0x4343},
{0x3D9A, 0x8043},
{0x3D9C, 0x4380},
{0x3D9E, 0x4343},
{0x3DA0, 0x8648},
{0x3DA2, 0x4880},
{0x3DA4, 0x6B6B},
{0x3DA6, 0x814C},
{0x3DA8, 0x864D},
{0x3DAA, 0xA442},
{0x3DAC, 0x8641},
{0x3DAE, 0x804D},
{0x3DB0, 0x864C},
{0x3DB2, 0x8A45},
{0x3DB4, 0x8144},
{0x3DB6, 0x4481},
{0x3DB8, 0x4583},
{0x3DBA, 0x46B7},
{0x3DBC, 0x7386},
{0x3DBE, 0x4685},
{0x3DC0, 0x0006},
{0x3DC2, 0x8143},
{0x3DC4, 0x4380},
{0x3DC6, 0x4343},
{0x3DC8, 0x8043},
{0x3DCA, 0x4380},
{0x3DCC, 0x4343},
{0x3DCE, 0x8043},
{0x3DD0, 0x4380},
{0x3DD2, 0x4343},
{0x3DD4, 0x8648},
{0x3DD6, 0x4880},
{0x3DD8, 0x6A6A},
{0x3DDA, 0x814C},
{0x3DDC, 0x864D},
{0x3DDE, 0xA442},
{0x3DE0, 0x8641},
{0x3DE2, 0x804D},
{0x3DE4, 0x864C},
{0x3DE6, 0x8A45},
{0x3DE8, 0x8144},
{0x3DEA, 0x4481},
{0x3DEC, 0x4583},
{0x3DEE, 0x4686},
{0x3DF0, 0x73FF},
{0x3DF2, 0xD358},
{0x3DF4, 0x835B},
{0x3DF6, 0x825A},
{0x3DF8, 0x8153},
{0x3DFA, 0x5467},
{0x3DFC, 0x6363},
{0x3DFE, 0x2640},
{0x3E00, 0x6470},
{0x3E02, 0xFFFF},
{0x3E04, 0xFFFF},
{0x3E06, 0xFFED},
{0x3E08, 0x4580},
{0x3E0A, 0x4384},
{0x3E0C, 0x4380},
{0x3E0E, 0x0280},
{0x3E10, 0x8402},
{0x3E12, 0x8080},
{0x3E14, 0x6A84},
{0x3E16, 0x6A80},
{0x3E18, 0x4484},
{0x3E1A, 0x4480},
{0x3E1C, 0x4578},
{0x3E1E, 0x8270},
{0x3E20, 0x0000},
{0x3E22, 0x0000},
{0x3E24, 0x0000},
{0x3E26, 0x0000},
{0x3E28, 0x0000},
{0x3E2A, 0x0000},
{0x3E2C, 0x0000},
{0x3E2E, 0x0000},
{0x3E30, 0x0000},
{0x3E32, 0x0000},
{0x3E34, 0x0000},
{0x3E36, 0x0000},
{0x3E38, 0x0000},
{0x3E3A, 0x0000},
{0x3E3C, 0x0000},
{0x3E3E, 0x0000},
{0x3E40, 0x0000},
{0x3E42, 0x0000},
{0x3E44, 0x0000},
{0x3E46, 0x0000},
{0x3E48, 0x0000},
{0x3E4A, 0x0000},
{0x3E4C, 0x0000},
{0x3E4E, 0x0000},
{0x3E50, 0x0000},
{0x3E52, 0x0000},
{0x3E54, 0x0000},
{0x3E56, 0x0000},
{0x3E58, 0x0000},
{0x3E5A, 0x0000},
{0x3E5C, 0x0000},
{0x3E5E, 0x0000},
{0x3E60, 0x0000},
{0x3E62, 0x0000},
{0x3E64, 0x0000},
{0x3E66, 0x0000},
{0x3E68, 0x0000},
{0x3E6A, 0x0000},
{0x3E6C, 0x0000},
{0x3E6E, 0x0000},
{0x3E70, 0x0000},
{0x3E72, 0x0000},
{0x3E74, 0x0000},
{0x3E76, 0x0000},
{0x3E78, 0x0000},
{0x3E7A, 0x0000},
{0x3E7C, 0x0000},
{0x3E7E, 0x0000},
{0x3E80, 0x0000},
{0x3E82, 0x0000},
{0x3E84, 0x0000},
{0x3E86, 0x0000},
{0x3E88, 0x0000},
{0x3E8A, 0x0000},
{0x3E8C, 0x0000},
{0x3E8E, 0x0000},
{0x3E90, 0x0000},
{0x3E92, 0x0000},
{0x3E94, 0x0000},
{0x3E96, 0x0000},
{0x3E98, 0x0000},
{0x3E9A, 0x0000},
{0x3E9C, 0x0000},
{0x3E9E, 0x0000},
{0x3EA0, 0x0000},
{0x3EA2, 0x0000},
{0x3EA4, 0x0000},
{0x3EA6, 0x0000},
{0x3EA8, 0x0000},
{0x3EAA, 0x0000},
{0x3EAC, 0x0000},
{0x3EAE, 0x0000},
{0x3EB0, 0x0000},
{0x3EB2, 0x0000},
{0x3EB4, 0x0000},
{0x3EB6, 0x0000},
{0x3EB8, 0x0000},
{0x3EBA, 0x0000},
{0x3EBC, 0x0000},
{0x3EBE, 0x0000},
{0x3EC0, 0x0000},
{0x3EC2, 0x0000},
{0x3EC4, 0x0000},
{0x3EC6, 0x0000},
{0x3EC8, 0x0000},
{0x3ECA, 0x0000},
{0x301A, 0x021C},
{0x0342, 0x10CC},
{0x0340, 0x04A4},
{0x0312, 0x045D},
{0x0202, 0x1952},
{0x3088, 0x0244},
{0x303E, 0x0001},
{0x31AE, 0x0201},
{0x0300, 0x0005},
{0x0302, 0x0001},
{0x0304, 0x0202},
{0x0306, 0x4040},
{0x0308, 0x000A},
{0x030A, 0x0001},
{0x0344, 0x0008},
{0x0348, 0x0787},
{0x0346, 0x0008},
{0x034A, 0x043F},
{0x034C, 0x0780},
{0x034E, 0x0438},
{0x3040, 0x0041},
{0x0104, 0x0001},
{0x3ECC, 0x008F},
{0x3ECE, 0xA8F0},
{0x3ED0, 0xFFFF},
{0x3ED6, 0x7193},
{0x3ED8, 0x8A11},
{0x30D2, 0x0020},
{0x30D4, 0x0040},
{0x3180, 0x80FF},
{0x0104, 0x0000},
{0x301A, 0x001C},
{AR0261_TABLE_END, 0x0000}
};
static struct ar0261_reg mode_640X480[] = {
{0x301A, 0x0019},
{AR0261_TABLE_WAIT_MS, 10},
{0x301A, 0x0218},
{0x31B0, 0x0062},
{0x31B2, 0x0046},
{0x31B4, 0x3248},
{0x31B6, 0x22A6},
{0x31B8, 0x1832},
{0x31BA, 0x1052},
{0x31BC, 0x0408},
{AR0261_TABLE_WAIT_MS, 1},
{0x3044, 0x0590},
{0x3EE6, 0x60AD},
{0x3120, 0x0001},
{0x301A, 0x0218},
{AR0261_TABLE_WAIT_MS, 10},
{0x3D00, 0x0481},
{0x3D02, 0xFFFF},
{0x3D04, 0xFFFF},
{0x3D06, 0xFFFF},
{0x3D08, 0x6600},
{0x3D0A, 0x0311},
{0x3D0C, 0x8C67},
{0x3D0E, 0x0808},
{0x3D10, 0x4380},
{0x3D12, 0x4343},
{0x3D14, 0x8043},
{0x3D16, 0x4330},
{0x3D18, 0x0543},
{0x3D1A, 0x4381},
{0x3D1C, 0x4C85},
{0x3D1E, 0x2022},
{0x3D20, 0x8020},
{0x3D22, 0xA093},
{0x3D24, 0x5A8A},
{0x3D26, 0x4C81},
{0x3D28, 0x5981},
{0x3D2A, 0x1E00},
{0x3D2C, 0x5F83},
{0x3D2E, 0x5C80},
{0x3D30, 0x5C81},
{0x3D32, 0x5F58},
{0x3D34, 0x6880},
{0x3D36, 0x1060},
{0x3D38, 0x8541},
{0x3D3A, 0xB350},
{0x3D3C, 0x5F10},
{0x3D3E, 0x6050},
{0x3D40, 0x5780},
{0x3D42, 0x6880},
{0x3D44, 0x2220},
{0x3D46, 0x805D},
{0x3D48, 0x8140},
{0x3D4A, 0x864B},
{0x3D4C, 0x8524},
{0x3D4E, 0x08A0},
{0x3D50, 0x55B8},
{0x3D52, 0x429C},
{0x3D54, 0x4281},
{0x3D56, 0x4081},
{0x3D58, 0x2808},
{0x3D5A, 0x2810},
{0x3D5C, 0x5727},
{0x3D5E, 0x1069},
{0x3D60, 0x4B52},
{0x3D62, 0x8265},
{0x3D64, 0x8A65},
{0x3D66, 0xA95E},
{0x3D68, 0x5080},
{0x3D6A, 0x5250},
{0x3D6C, 0x6080},
{0x3D6E, 0x6922},
{0x3D70, 0x2080},
{0x3D72, 0x5D80},
{0x3D74, 0x4080},
{0x3D76, 0x5681},
{0x3D78, 0x5781},
{0x3D7A, 0x4B86},
{0x3D7C, 0x2408},
{0x3D7E, 0x9345},
{0x3D80, 0x8144},
{0x3D82, 0x4481},
{0x3D84, 0x4586},
{0x3D86, 0x4E80},
{0x3D88, 0x4FCD},
{0x3D8A, 0x4685},
{0x3D8C, 0x0006},
{0x3D8E, 0x8143},
{0x3D90, 0x4380},
{0x3D92, 0x4343},
{0x3D94, 0x8043},
{0x3D96, 0x4380},
{0x3D98, 0x4343},
{0x3D9A, 0x8043},
{0x3D9C, 0x4380},
{0x3D9E, 0x4343},
{0x3DA0, 0x8648},
{0x3DA2, 0x4880},
{0x3DA4, 0x6B6B},
{0x3DA6, 0x814C},
{0x3DA8, 0x864D},
{0x3DAA, 0xA442},
{0x3DAC, 0x8641},
{0x3DAE, 0x804D},
{0x3DB0, 0x864C},
{0x3DB2, 0x8A45},
{0x3DB4, 0x8144},
{0x3DB6, 0x4481},
{0x3DB8, 0x4583},
{0x3DBA, 0x46B7},
{0x3DBC, 0x7386},
{0x3DBE, 0x4685},
{0x3DC0, 0x0006},
{0x3DC2, 0x8143},
{0x3DC4, 0x4380},
{0x3DC6, 0x4343},
{0x3DC8, 0x8043},
{0x3DCA, 0x4380},
{0x3DCC, 0x4343},
{0x3DCE, 0x8043},
{0x3DD0, 0x4380},
{0x3DD2, 0x4343},
{0x3DD4, 0x8648},
{0x3DD6, 0x4880},
{0x3DD8, 0x6A6A},
{0x3DDA, 0x814C},
{0x3DDC, 0x864D},
{0x3DDE, 0xA442},
{0x3DE0, 0x8641},
{0x3DE2, 0x804D},
{0x3DE4, 0x864C},
{0x3DE6, 0x8A45},
{0x3DE8, 0x8144},
{0x3DEA, 0x4481},
{0x3DEC, 0x4583},
{0x3DEE, 0x4686},
{0x3DF0, 0x73FF},
{0x3DF2, 0xD358},
{0x3DF4, 0x835B},
{0x3DF6, 0x825A},
{0x3DF8, 0x8153},
{0x3DFA, 0x5467},
{0x3DFC, 0x6363},
{0x3DFE, 0x2640},
{0x3E00, 0x6470},
{0x3E02, 0xFFFF},
{0x3E04, 0xFFFF},
{0x3E06, 0xFFED},
{0x3E08, 0x4580},
{0x3E0A, 0x4384},
{0x3E0C, 0x4380},
{0x3E0E, 0x0280},
{0x3E10, 0x8402},
{0x3E12, 0x8080},
{0x3E14, 0x6A84},
{0x3E16, 0x6A80},
{0x3E18, 0x4484},
{0x3E1A, 0x4480},
{0x3E1C, 0x4578},
{0x3E1E, 0x8270},
{0x3E20, 0x0000},
{0x3E22, 0x0000},
{0x3E24, 0x0000},
{0x3E26, 0x0000},
{0x3E28, 0x0000},
{0x3E2A, 0x0000},
{0x3E2C, 0x0000},
{0x3E2E, 0x0000},
{0x3E30, 0x0000},
{0x3E32, 0x0000},
{0x3E34, 0x0000},
{0x3E36, 0x0000},
{0x3E38, 0x0000},
{0x3E3A, 0x0000},
{0x3E3C, 0x0000},
{0x3E3E, 0x0000},
{0x3E40, 0x0000},
{0x3E42, 0x0000},
{0x3E44, 0x0000},
{0x3E46, 0x0000},
{0x3E48, 0x0000},
{0x3E4A, 0x0000},
{0x3E4C, 0x0000},
{0x3E4E, 0x0000},
{0x3E50, 0x0000},
{0x3E52, 0x0000},
{0x3E54, 0x0000},
{0x3E56, 0x0000},
{0x3E58, 0x0000},
{0x3E5A, 0x0000},
{0x3E5C, 0x0000},
{0x3E5E, 0x0000},
{0x3E60, 0x0000},
{0x3E62, 0x0000},
{0x3E64, 0x0000},
{0x3E66, 0x0000},
{0x3E68, 0x0000},
{0x3E6A, 0x0000},
{0x3E6C, 0x0000},
{0x3E6E, 0x0000},
{0x3E70, 0x0000},
{0x3E72, 0x0000},
{0x3E74, 0x0000},
{0x3E76, 0x0000},
{0x3E78, 0x0000},
{0x3E7A, 0x0000},
{0x3E7C, 0x0000},
{0x3E7E, 0x0000},
{0x3E80, 0x0000},
{0x3E82, 0x0000},
{0x3E84, 0x0000},
{0x3E86, 0x0000},
{0x3E88, 0x0000},
{0x3E8A, 0x0000},
{0x3E8C, 0x0000},
{0x3E8E, 0x0000},
{0x3E90, 0x0000},
{0x3E92, 0x0000},
{0x3E94, 0x0000},
{0x3E96, 0x0000},
{0x3E98, 0x0000},
{0x3E9A, 0x0000},
{0x3E9C, 0x0000},
{0x3E9E, 0x0000},
{0x3EA0, 0x0000},
{0x3EA2, 0x0000},
{0x3EA4, 0x0000},
{0x3EA6, 0x0000},
{0x3EA8, 0x0000},
{0x3EAA, 0x0000},
{0x3EAC, 0x0000},
{0x3EAE, 0x0000},
{0x3EB0, 0x0000},
{0x3EB2, 0x0000},
{0x3EB4, 0x0000},
{0x3EB6, 0x0000},
{0x3EB8, 0x0000},
{0x3EBA, 0x0000},
{0x3EBC, 0x0000},
{0x3EBE, 0x0000},
{0x3EC0, 0x0000},
{0x3EC2, 0x0000},
{0x3EC4, 0x0000},
{0x3EC6, 0x0000},
{0x3EC8, 0x0000},
{0x3ECA, 0x0000},
{0x301A, 0x021C},
{0x0342, 0x08C8},
{0x0340, 0x0216},
{0x3012, 0x0214},
{0x31AE, 0x0201},
{0x0300, 0x0005},
{0x0302, 0x0001},
{0x0304, 0x0202},
{0x0306, 0x283C},
{0x0308, 0x000A},
{0x030A, 0x0001},
{0x0344, 0x0288},
{0x0348, 0x0507},
{0x0346, 0x0134},
{0x034A, 0x0313},
{0x034C, 0x0280},
{0x034E, 0x01E0},
{0x3040, 0x0041},
{0x0104, 0x0001},
{0x3ECC, 0x008F},
{0x3ECE, 0xA8F0},
{0x3ED0, 0xFFFF},
{0x3ED6, 0x7193},
{0x3ED8, 0x8A31},
{0x30D2, 0x0020},
{0x30D4, 0x0040},
{0x3180, 0x80FF},
{0x3ED0, 0xFFFF},
{0x3ED2, 0xFFFF},
{0x3ED6, 0x7193},
{0x3ED8, 0x8A31},
{0x30D2, 0x0020},
{0x30D4, 0x0040},
{0x0104, 0X0000},
{0x301A, 0x001C},
{AR0261_TABLE_END, 0x0000}
};
enum {
AR0261_MODE_1920X1080,
AR0261_MODE_1920X1080_HDR,
AR0261_MODE_640X480,
};
static struct ar0261_reg *mode_table[] = {
[AR0261_MODE_1920X1080] = mode_1920x1080,
[AR0261_MODE_1920X1080_HDR] = mode_1920x1080_HDR,
[AR0261_MODE_640X480] = mode_640X480,
};
static inline void
msleep_range(unsigned int delay_base)
{
usleep_range(delay_base*1000, delay_base*1000+500);
}
static inline void
ar0261_get_frame_length_regs(struct ar0261_reg *regs, u32 frame_length)
{
regs->addr = AR0261_FRAME_LEN_LINES;
regs->val = frame_length;
}
static inline void
ar0261_get_coarse_time_regs(struct ar0261_reg *regs, u32 coarse_time)
{
regs->addr = AR0261_COARSE_INTEGRATION_TIME;
regs->val = coarse_time;
}
static inline void
ar0261_get_coarse_time_short_regs(struct ar0261_reg *regs, u32 coarse_time)
{
regs->addr = AR0261_COARSE_INTEGRATION_SHORT_TIME;
regs->val = coarse_time;
}
static inline void
ar0261_get_gain_reg(struct ar0261_reg *regs, u16 gain)
{
regs->addr = AR0261_ANA_GAIN_GLOBAL;
regs->val = gain;
}
static int
ar0261_read_reg(struct ar0261_info *info, u16 addr, u16 *val)
{
int err;
unsigned char data[2];
err = regmap_raw_read(info->regmap, addr, data, sizeof(data));
if (!err)
*val = (u16)data[0] << 8 | data[1];
return err;
}
static int
ar0261_write_reg(struct ar0261_info *info, u16 addr, u16 val)
{
int err;
unsigned char data[2];
data[0] = (u8) (val >> 8);
data[1] = (u8) (val & 0xff);
err = regmap_raw_write(info->regmap, addr, data, sizeof(data));
if (err)
dev_err(&info->i2c_client->dev,
"%s:i2c write failed, %x = %x\n", __func__, addr, val);
return err;
}
static inline int ar0261_i2c_wr8(struct ar0261_info *info, u16 reg, u8 val)
{
return regmap_write(info->regmap, reg, val);
}
static int
ar0261_write_table(struct ar0261_info *info,
const struct ar0261_reg table[],
const struct ar0261_reg override_list[],
int num_override_regs)
{
const struct ar0261_reg *next;
int err = 0;
int i;
u16 val;
dev_info(&info->i2c_client->dev, "ar0261_write_table\n");
for (next = table; next->addr != AR0261_TABLE_END; next++) {
if (next->addr == AR0261_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 = ar0261_write_reg(info, next->addr, val);
if (err)
break;
}
return err;
}
static int
ar0261_set_mode(struct ar0261_info *info, struct ar0261_mode *mode)
{
struct device *dev = &info->i2c_client->dev;
int sensor_mode;
int err;
struct ar0261_reg reg_list[HDR_MODE_OVERRIDE_REGS];
dev_info(dev, "%s: res [%ux%u] framelen %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 == 1920) && (mode->yres == 1080)) {
if (mode->hdr_en == 0)
sensor_mode = AR0261_MODE_1920X1080;
else if (mode->hdr_en == 1)
sensor_mode = AR0261_MODE_1920X1080_HDR;
else {
dev_err(dev, "%s: invalid hdr setting to set mode %d\n",
__func__, mode->hdr_en);
return -EINVAL;
}
} else if ((mode->xres == 640) && (mode->yres == 480)) {
sensor_mode = AR0261_MODE_640X480;
} else {
dev_err(dev, "%s: invalid resolution 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.
*/
ar0261_get_frame_length_regs(reg_list, mode->frame_length);
ar0261_get_coarse_time_regs(reg_list + 1, mode->coarse_time);
ar0261_get_gain_reg(reg_list + 2, mode->gain);
/* if HDR is enabled */
if (mode->hdr_en == 1) {
ar0261_get_coarse_time_short_regs(
reg_list + 3, mode->coarse_time_short);
}
err = ar0261_write_table(info, mode_table[sensor_mode],
reg_list, mode->hdr_en ? HDR_MODE_OVERRIDE_REGS :
NORMAL_MODE_OVERRIDE_REGS);
if (err)
return err;
info->mode = sensor_mode;
dev_info(dev, "[ar0261]: stream on.\n");
return 0;
}
static int
ar0261_get_status(struct ar0261_info *info, u8 *dev_status)
{
/* TBD */
*dev_status = 0;
return 0;
}
static int
ar0261_set_frame_length(struct ar0261_info *info,
u32 frame_length,
bool group_hold)
{
struct ar0261_reg reg_list;
int ret;
ar0261_get_frame_length_regs(®_list, frame_length);
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x01);
if (ret)
return ret;
}
ret = ar0261_write_reg(info, reg_list.addr, reg_list.val);
if (ret)
return ret;
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
ar0261_set_coarse_time(struct ar0261_info *info,
u32 coarse_time,
bool group_hold)
{
int ret;
struct ar0261_reg reg_list;
ar0261_get_coarse_time_regs(®_list, coarse_time);
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x01);
if (ret)
return ret;
}
ret = ar0261_write_reg(info, reg_list.addr, reg_list.val);
if (ret)
return ret;
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
ar0261_set_hdr_coarse_time(struct ar0261_info *info,
struct ar0261_hdr *values,
bool group_hold)
{
struct ar0261_reg reg_list;
struct ar0261_reg reg_list_short;
int ret;
/* get long and short coarse time registers */
ar0261_get_coarse_time_regs(®_list, values->coarse_time_long);
ar0261_get_coarse_time_short_regs(®_list_short,
values->coarse_time_short);
/* set group hold */
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x1);
if (ret)
return ret;
}
/* writing long exposure */
ret = ar0261_write_reg(info, reg_list.addr, reg_list.val);
if (ret)
return ret;
/* writing short exposure */
ret = ar0261_write_reg(info, reg_list_short.addr,
reg_list_short.val);
if (ret)
return ret;
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
ar0261_set_gain(struct ar0261_info *info, u16 gain, bool group_hold)
{
int ret;
struct ar0261_reg reg_list;
ar0261_get_gain_reg(®_list, gain);
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x1);
if (ret)
return ret;
}
ret = ar0261_i2c_wr8(info, reg_list.addr, reg_list.val);
if (ret)
return ret;
if (group_hold) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
ar0261_set_group_hold(struct ar0261_info *info, struct ar0261_ae *ae)
{
int ret;
int count = 0;
bool grouphold_enabled = false;
struct ar0261_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)
grouphold_enabled = true;
if (grouphold_enabled) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x1);
if (ret)
return ret;
}
if (ae->gain_enable)
ar0261_set_gain(info, ae->gain, false);
if (ae->coarse_time_enable)
ar0261_set_hdr_coarse_time(info, &values, false);
if (ae->frame_length_enable)
ar0261_set_frame_length(info, ae->frame_length, false);
if (grouphold_enabled) {
ret = ar0261_i2c_wr8(info, AR0261_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int ar0261_get_sensor_id(struct ar0261_info *info)
{
int ret = 0;
int i = 0;
u16 store = 0;
pr_info("%s\n", __func__);
if (info->sensor_data.fuse_id_size)
return 0;
ret |= ar0261_write_reg(info, 0x301A, 0x0018);
ret |= ar0261_write_reg(info, 0x3054, 0x0500);
ret |= ar0261_write_reg(info, 0x3052, 0x0000);
ret |= ar0261_write_reg(info, 0x304A, 0x0400);
ret |= ar0261_write_reg(info, 0x304C, 0x0220);
ret |= ar0261_write_reg(info, 0x304A, 0x0410);
i = 0;
do {
ret |= ar0261_read_reg(info, 0x304A, &store);
usleep_range(1000, 1500);
i++;
} while (!(store & (0x1 << 5)) && (i <= 10));
if (!(store & (0x1 << 6)))
return 0;
for (i = 0; i < 16; i += 2) {
ret |= ar0261_read_reg(info, 0x3804 + i, &store);
info->sensor_data.fuse_id[i] = store;
info->sensor_data.fuse_id[i+1] = store >> 8;
}
ret |= ar0261_write_reg(info, 0x301A, 0x001C);
if (!ret)
info->sensor_data.fuse_id_size = i;
return ret;
}
static void ar0261_mclk_disable(struct ar0261_info *info)
{
dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__);
clk_disable_unprepare(info->mclk);
}
static int ar0261_mclk_enable(struct ar0261_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
ar0261_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err;
struct ar0261_info *info = file->private_data;
struct device *dev = &info->i2c_client->dev;
switch (_IOC_NR(cmd)) {
case _IOC_NR(AR0261_IOCTL_SET_MODE):
{
struct ar0261_mode mode;
if (copy_from_user(&mode,
(const void __user *)arg,
sizeof(struct ar0261_mode))) {
dev_err(dev, "%s:Failed to get mode from user.\n",
__func__);
return -EFAULT;
}
return ar0261_set_mode(info, &mode);
}
case _IOC_NR(AR0261_IOCTL_SET_FRAME_LENGTH):
return ar0261_set_frame_length(info, (u32)arg, true);
case _IOC_NR(AR0261_IOCTL_SET_COARSE_TIME):
return ar0261_set_coarse_time(info, (u32)arg, true);
case _IOC_NR(AR0261_IOCTL_SET_GAIN):
return ar0261_set_gain(info, (u16)arg, true);
case _IOC_NR(AR0261_IOCTL_GET_STATUS):
{
u8 status;
err = ar0261_get_status(info, &status);
if (err)
return err;
if (copy_to_user((void __user *)arg, &status, 1)) {
dev_err(dev, "%s:Failed to copy status to user.\n",
__func__);
return -EFAULT;
}
return 0;
}
case _IOC_NR(AR0261_IOCTL_GET_SENSORDATA):
{
err = ar0261_get_sensor_id(info);
if (err) {
dev_err(dev, "%s:Failed to get fuse id info.\n",
__func__);
return err;
}
if (copy_to_user((void __user *)arg,
&info->sensor_data,
sizeof(struct ar0261_sensordata))) {
dev_info(dev, "%s:Fail copy fuse id to user space\n",
__func__);
return -EFAULT;
}
return 0;
}
case _IOC_NR(AR0261_IOCTL_SET_GROUP_HOLD):
{
struct ar0261_ae ae;
if (copy_from_user(&ae, (const void __user *)arg,
sizeof(struct ar0261_ae))) {
dev_info(dev, "%s:fail group hold\n", __func__);
return -EFAULT;
}
return ar0261_set_group_hold(info, &ae);
}
case _IOC_NR(AR0261_IOCTL_SET_HDR_COARSE_TIME):
{
struct ar0261_hdr values;
dev_dbg(&info->i2c_client->dev,
"AR0261_IOCTL_SET_HDR_COARSE_TIME\n");
if (copy_from_user(&values,
(const void __user *)arg,
sizeof(struct ar0261_hdr))) {
err = -EFAULT;
break;
}
err = ar0261_set_hdr_coarse_time(info, &values, true);
break;
}
default:
dev_err(dev, "%s:unknown cmd.\n", __func__);
return -EINVAL;
}
return 0;
}
static int
ar0261_open(struct inode *inode, struct file *file)
{
int err;
struct miscdevice *miscdev = file->private_data;
struct ar0261_info *info;
info = container_of(miscdev, struct ar0261_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;
err = ar0261_mclk_enable(info);
if (err < 0)
return err;
if (info->pdata && info->pdata->power_on)
err = info->pdata->power_on(&info->power);
else {
dev_err(&info->i2c_client->dev,
"%s:no valid power_on function.\n", __func__);
err = -EEXIST;
}
if (err < 0)
goto ar0261_open_fail;
return 0;
ar0261_open_fail:
ar0261_mclk_disable(info);
return err;
}
static int
ar0261_release(struct inode *inode, struct file *file)
{
struct ar0261_info *info = file->private_data;
if (info->pdata && info->pdata->power_off)
info->pdata->power_off(&info->power);
ar0261_mclk_disable(info);
file->private_data = NULL;
/* warn if device is already released */
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static int ar0261_power_put(struct ar0261_power_rail *pw)
{
if (likely(pw->dvdd))
regulator_put(pw->dvdd);
if (likely(pw->avdd))
regulator_put(pw->avdd);
if (likely(pw->iovdd))
regulator_put(pw->iovdd);
pw->dvdd = NULL;
pw->avdd = NULL;
pw->iovdd = NULL;
return 0;
}
static int ar0261_regulator_get(struct ar0261_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(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 ar0261_power_get(struct ar0261_info *info)
{
struct ar0261_power_rail *pw = &info->power;
ar0261_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */
ar0261_regulator_get(info, &pw->avdd, "vana"); /* analog 2.7v */
ar0261_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */
return 0;
}
static const struct file_operations ar0261_fileops = {
.owner = THIS_MODULE,
.open = ar0261_open,
.unlocked_ioctl = ar0261_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ar0261_ioctl,
#endif
.release = ar0261_release,
};
static struct miscdevice ar0261_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ar0261",
.fops = &ar0261_fileops,
};
static int
ar0261_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ar0261_info *info;
const char *mclk_name;
int err = 0;
pr_info("[ar0261]: probing sensor.\n");
info = devm_kzalloc(&client->dev,
sizeof(struct ar0261_info), GFP_KERNEL);
if (!info) {
pr_err("[ar0261]:%s:Unable to allocate memory!\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 = -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);
}
i2c_set_clientdata(client, info);
ar0261_power_get(info);
memcpy(&info->miscdev_info,
&ar0261_device,
sizeof(struct miscdevice));
err = misc_register(&info->miscdev_info);
if (err) {
ar0261_power_put(&info->power);
pr_err("[ar0261]:%s:Unable to register misc device!\n",
__func__);
}
return err;
}
static int
ar0261_remove(struct i2c_client *client)
{
struct ar0261_info *info = i2c_get_clientdata(client);
ar0261_power_put(&info->power);
misc_deregister(&ar0261_device);
return 0;
}
static const struct i2c_device_id ar0261_id[] = {
{ "ar0261", 0 },
};
MODULE_DEVICE_TABLE(i2c, ar0261_id);
static struct i2c_driver ar0261_i2c_driver = {
.driver = {
.name = "ar0261",
.owner = THIS_MODULE,
},
.probe = ar0261_probe,
.remove = ar0261_remove,
.id_table = ar0261_id,
};
static int __init
ar0261_init(void)
{
pr_info("[ar0261] sensor driver loading\n");
return i2c_add_driver(&ar0261_i2c_driver);
}
static void __exit
ar0261_exit(void)
{
i2c_del_driver(&ar0261_i2c_driver);
}
module_init(ar0261_init);
module_exit(ar0261_exit);