/* * OmniVision OV5650 sensor driver * * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #define SIZEOF_I2C_TRANSBUF 32 struct ov5650_priv { struct v4l2_subdev subdev; struct v4l2_mbus_framefmt mf; const struct ov5650_platform_data *pdata; int ident; u16 chip_id; u8 revision; int mode; struct i2c_client *client; u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; }; static struct ov5650_priv *to_ov5650(const struct v4l2_subdev *sd) { return container_of(sd, struct ov5650_priv, subdev); } /** * struct ov5650_reg - ov5650 register format * @addr: 16-bit offset to register * @val: 8/16/32-bit register value * * Define a structure for OV5650 register initialization values */ struct ov5650_reg { u16 addr; u16 val; }; #define OV5650_TABLE_WAIT_MS 0 #define OV5650_TABLE_END 1 #define OV5650_MAX_RETRIES 3 static struct ov5650_reg tp_none_seq[] = { {0x5046, 0x00}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg tp_cbars_seq[] = { {0x503D, 0xC0}, {0x503E, 0x00}, {0x5046, 0x01}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg tp_checker_seq[] = { {0x503D, 0xC0}, {0x503E, 0x0A}, {0x5046, 0x01}, {OV5650_TABLE_END, 0x0000} }; enum { TP_NONE = 1, TP_COLORBARS = 2, TP_CHECKER = 3, }; static int test_mode; module_param(test_mode, int, 0644); static struct ov5650_reg reset_seq[] = { {0x3008, 0x82}, {OV5650_TABLE_WAIT_MS, 5}, {0x3008, 0x42}, {OV5650_TABLE_WAIT_MS, 5}, {OV5650_TABLE_END, 0x0000}, }; static struct ov5650_reg mode_start[] = { {0x3103, 0x93}, {0x3017, 0xff}, {0x3018, 0xfc}, {0x3600, 0x50}, {0x3601, 0x0d}, {0x3604, 0x50}, {0x3605, 0x04}, {0x3606, 0x3f}, {0x3612, 0x1a}, {0x3630, 0x22}, {0x3631, 0x22}, {0x3702, 0x3a}, {0x3704, 0x18}, {0x3705, 0xda}, {0x3706, 0x41}, {0x370a, 0x80}, {0x370b, 0x40}, {0x370e, 0x00}, {0x3710, 0x28}, {0x3712, 0x13}, {0x3830, 0x50}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3a00, 0x38}, {0x3603, 0xa7}, {0x3615, 0x50}, {0x3620, 0x56}, {0x3810, 0x00}, {0x3836, 0x00}, {0x3a1a, 0x06}, {0x4000, 0x01}, {0x401c, 0x48}, {0x401d, 0x08}, {0x5000, 0x06}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, {0x5046, 0x00}, {0x300f, 0x8f}, {0x3010, 0x10}, {0x3011, 0x14}, {0x3012, 0x02}, {0x3815, 0x82}, {0x3503, 0x00}, {0x3613, 0x44}, {OV5650_TABLE_END, 0x0}, }; static struct ov5650_reg mode_2592x1944[] = { {0x3621, 0x2f}, {0x3632, 0x55}, {0x3703, 0xe6}, {0x370c, 0xa0}, {0x370d, 0x04}, {0x3713, 0x2f}, {0x3800, 0x02}, {0x3801, 0x58}, {0x3802, 0x00}, {0x3803, 0x0c}, {0x3804, 0x0a}, {0x3805, 0x20}, {0x3806, 0x07}, {0x3807, 0xa0}, {0x3808, 0x0a}, {0x3809, 0x20}, {0x380a, 0x07}, {0x380b, 0xa0}, {0x380c, 0x0c}, {0x380d, 0xb4}, {0x380e, 0x07}, {0x380f, 0xb0}, {0x3818, 0xc0}, {0x381a, 0x3c}, {0x3a0d, 0x06}, {0x3c01, 0x00}, {0x3007, 0x3f}, {0x5059, 0x80}, {0x3003, 0x03}, {0x3500, 0x00}, {0x3501, 0x7a}, {0x3502, 0xd0}, {0x350a, 0x00}, {0x350b, 0x00}, {0x401d, 0x08}, {0x4801, 0x0f}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x34}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_1296x972[] = { {0x3621, 0xaf}, {0x3632, 0x5a}, {0x3703, 0xb0}, {0x370c, 0xc5}, {0x370d, 0x42}, {0x3713, 0x2f}, {0x3800, 0x03}, {0x3801, 0x3c}, {0x3802, 0x00}, {0x3803, 0x62}, {0x3804, 0x05}, {0x3805, 0x10}, {0x3806, 0x03}, {0x3807, 0xd0}, {0x3808, 0x05}, {0x3809, 0x10}, {0x380a, 0x03}, {0x380b, 0xd0}, {0x380c, 0x08}, {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0xa4}, {0x3818, 0xc1}, {0x381a, 0x00}, {0x3a0d, 0x08}, {0x3c01, 0x00}, {0x3007, 0x3b}, {0x5059, 0x80}, {0x3003, 0x03}, {0x3500, 0x00}, {0x3501, 0x5a}, {0x3502, 0x10}, {0x350a, 0x00}, {0x350b, 0x10}, {0x401d, 0x08}, {0x4801, 0x0f}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x34}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_2080x1164[] = { {0x3103, 0x93}, {0x3007, 0x3b}, {0x3017, 0xff}, {0x3018, 0xfc}, {0x3600, 0x54}, {0x3601, 0x05}, {0x3603, 0xa7}, {0x3604, 0x40}, {0x3605, 0x04}, {0x3606, 0x3f}, {0x3612, 0x1a}, {0x3613, 0x44}, {0x3615, 0x52}, {0x3620, 0x56}, {0x3623, 0x01}, {0x3630, 0x22}, {0x3631, 0x36}, {0x3632, 0x5f}, {0x3633, 0x24}, {0x3702, 0x3a}, {0x3704, 0x18}, {0x3706, 0x41}, {0x370b, 0x40}, {0x370e, 0x00}, {0x3710, 0x28}, {0x3711, 0x24}, {0x3712, 0x13}, {0x3810, 0x00}, {0x3815, 0x82}, {0x3830, 0x50}, {0x3836, 0x00}, {0x3a1a, 0x06}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3a00, 0x38}, {0x3a0d, 0x06}, {0x3c01, 0x34}, {0x401f, 0x03}, {0x4000, 0x05}, {0x401d, 0x08}, {0x4001, 0x02}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, {0x5046, 0x00}, {0x300f, 0x8f}, {0x3010, 0x10}, {0x3011, 0x14}, {0x3012, 0x02}, {0x3503, 0x00}, {0x3621, 0x2f}, {0x3703, 0xe6}, {0x370c, 0x00}, {0x370d, 0x04}, {0x3713, 0x22}, {0x3714, 0x27}, {0x3705, 0xda}, {0x370a, 0x80}, {0x3800, 0x02}, {0x3801, 0x12}, {0x3802, 0x00}, {0x3803, 0x0a}, {0x3804, 0x08}, {0x3805, 0x20}, {0x3806, 0x04}, {0x3807, 0x92}, {0x3808, 0x08}, {0x3809, 0x20}, {0x380a, 0x04}, {0x380b, 0x92}, {0x380c, 0x0a}, {0x380d, 0x96}, {0x380e, 0x04}, {0x380f, 0x9e}, {0x3818, 0xc0}, {0x381a, 0x3c}, {0x381c, 0x31}, {0x381d, 0x8e}, {0x381e, 0x04}, {0x381f, 0x92}, {0x3820, 0x04}, {0x3821, 0x19}, {0x3824, 0x01}, {0x3827, 0x0a}, {0x401c, 0x46}, {0x3003, 0x03}, {0x3500, 0x00}, {0x3501, 0x49}, {0x3502, 0xa0}, {0x350a, 0x00}, {0x350b, 0x00}, {0x4801, 0x0f}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x34}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_1920x1080[] = { {0x3103, 0x93}, {0x3007, 0x3b}, {0x3017, 0xff}, {0x3018, 0xfc}, {0x3600, 0x54}, {0x3601, 0x05}, {0x3603, 0xa7}, {0x3604, 0x40}, {0x3605, 0x04}, {0x3606, 0x3f}, {0x3612, 0x1a}, {0x3613, 0x44}, {0x3615, 0x52}, {0x3620, 0x56}, {0x3623, 0x01}, {0x3630, 0x22}, {0x3631, 0x36}, {0x3632, 0x5f}, {0x3633, 0x24}, {0x3702, 0x3a}, {0x3704, 0x18}, {0x3706, 0x41}, {0x370b, 0x40}, {0x370e, 0x00}, {0x3710, 0x28}, {0x3711, 0x24}, {0x3712, 0x13}, {0x3810, 0x00}, {0x3815, 0x82}, {0x3830, 0x50}, {0x3836, 0x00}, {0x3a1a, 0x06}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3a00, 0x38}, {0x3a0d, 0x06}, {0x3c01, 0x34}, {0x401f, 0x03}, {0x4000, 0x05}, {0x401d, 0x08}, {0x4001, 0x02}, {0x5001, 0x00}, {0x5002, 0x00}, {0x503d, 0x00}, {0x5046, 0x00}, {0x300f, 0x8f}, {0x3010, 0x10}, {0x3011, 0x14}, {0x3012, 0x02}, {0x3503, 0x00}, {0x3621, 0x2f}, {0x3703, 0xe6}, {0x370c, 0x00}, {0x370d, 0x04}, {0x3713, 0x22}, {0x3714, 0x27}, {0x3705, 0xda}, {0x370a, 0x80}, {0x3800, 0x02}, {0x3801, 0x94}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x07}, {0x3805, 0x80}, {0x3806, 0x04}, {0x3807, 0x40}, {0x3808, 0x07}, {0x3809, 0x80}, {0x380a, 0x04}, {0x380b, 0x40}, {0x380c, 0x0a}, {0x380d, 0x84}, {0x380e, 0x04}, {0x380f, 0xa4}, {0x3818, 0xc0}, {0x381a, 0x3c}, {0x381c, 0x31}, {0x381d, 0xa4}, {0x381e, 0x04}, {0x381f, 0x60}, {0x3820, 0x03}, {0x3821, 0x1a}, {0x3824, 0x01}, {0x3827, 0x0a}, {0x401c, 0x46}, {0x3003, 0x03}, {0x3500, 0x00}, {0x3501, 0x49}, {0x3502, 0xa0}, {0x350a, 0x00}, {0x350b, 0x00}, {0x4801, 0x0f}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x34}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_1280x720[] = { {0x3103, 0x93}, {0x3b07, 0x0c}, {0x3017, 0xff}, {0x3018, 0xfc}, {0x3706, 0x41}, {0x3613, 0xc4}, {0x370d, 0x42}, {0x3703, 0x9a}, {0x3630, 0x22}, {0x3605, 0x04}, {0x3606, 0x3f}, {0x3712, 0x13}, {0x370e, 0x00}, {0x370b, 0x40}, {0x3600, 0x54}, {0x3601, 0x05}, {0x3713, 0x22}, {0x3714, 0x27}, {0x3631, 0x22}, {0x3612, 0x1a}, {0x3604, 0x40}, {0x3705, 0xdb}, {0x370a, 0x81}, {0x370c, 0x00}, {0x3710, 0x28}, {0x3702, 0x3a}, {0x3704, 0x18}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3a00, 0x38}, {0x3800, 0x02}, {0x3801, 0x54}, {0x3803, 0x0c}, {0x380c, 0x0c}, {0x380d, 0xb4}, {0x380e, 0x07}, {0x380f, 0xb0}, {0x3830, 0x50}, {0x3a08, 0x12}, {0x3a09, 0x70}, {0x3a0a, 0x0f}, {0x3a0b, 0x60}, {0x3a0d, 0x06}, {0x3a0e, 0x06}, {0x3a13, 0x54}, {0x3815, 0x82}, {0x5059, 0x80}, {0x3615, 0x52}, {0x505a, 0x0a}, {0x505b, 0x2e}, {0x3713, 0x92}, {0x3714, 0x17}, {0x3804, 0x05}, {0x3805, 0x00}, {0x3806, 0x02}, {0x3807, 0xd0}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, {0x380b, 0xd0}, {0x380c, 0x08}, {0x380d, 0x72}, {0x380e, 0x02}, {0x380f, 0xe4}, {0x3815, 0x81}, {0x381c, 0x10}, {0x381d, 0x82}, {0x381e, 0x05}, {0x381f, 0xc0}, {0x3821, 0x20}, {0x3824, 0x23}, {0x3825, 0x2c}, {0x3826, 0x00}, {0x3827, 0x0c}, {0x3a08, 0x1b}, {0x3a09, 0xc0}, {0x3a0a, 0x17}, {0x3a0b, 0x20}, {0x3a0d, 0x01}, {0x3a0e, 0x01}, {0x3a1a, 0x06}, {0x3503, 0x00}, {0x3623, 0x01}, {0x3633, 0x24}, {0x3c01, 0x34}, {0x3c04, 0x28}, {0x3c05, 0x98}, {0x3c07, 0x07}, {0x3c09, 0xc2}, {0x4000, 0x05}, {0x401d, 0x28}, {0x4001, 0x02}, {0x401c, 0x42}, {0x5046, 0x09}, {0x3810, 0x40}, {0x3836, 0x41}, {0x505f, 0x04}, {0x5000, 0xfe}, {0x5001, 0x01}, {0x5002, 0x00}, {0x503d, 0x00}, /* bit[7]=1 enable test_pattern */ {0x5901, 0x00}, {0x585a, 0x01}, {0x585b, 0x2c}, {0x585c, 0x01}, {0x585d, 0x93}, {0x585e, 0x01}, {0x585f, 0x90}, {0x5860, 0x01}, {0x5861, 0x0d}, {0x5180, 0xc0}, {0x5184, 0x00}, {0x470a, 0x00}, {0x470b, 0x00}, {0x470c, 0x00}, {0x300f, 0x8e}, {0x3603, 0xa7}, {0x3632, 0x55}, {0x3620, 0x56}, {0x3621, 0xaf}, {0x3818, 0xc1}, {0x3631, 0x36}, {0x3632, 0x5f}, {0x3711, 0x24}, {0x401f, 0x03}, {0x3008, 0x02}, {0x3011, 0x14}, {0x3007, 0x3B}, {0x4801, 0x0f}, {0x3003, 0x03}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x04}, /* bit[5]=0 as CSI continuous clock */ {0x300f, 0x8f}, {0x3010, 0x10}, {0x3815, 0x82}, {0x3003, 0x01}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_1264x704[] = { {0x3600, 0x54}, {0x3601, 0x05}, {0x3604, 0x40}, {0x3705, 0xdb}, {0x370a, 0x81}, {0x3615, 0x52}, {0x3810, 0x40}, {0x3836, 0x41}, {0x4000, 0x05}, {0x401c, 0x42}, {0x401d, 0x08}, {0x5046, 0x09}, {0x3010, 0x00}, {0x3503, 0x00}, {0x3613, 0xc4}, {0x3621, 0xaf}, {0x3632, 0x55}, {0x3703, 0x9a}, {0x370c, 0x00}, {0x370d, 0x42}, {0x3713, 0x22}, {0x3800, 0x02}, {0x3801, 0x54}, {0x3802, 0x00}, {0x3803, 0x0c}, {0x3804, 0x05}, {0x3805, 0x00}, {0x3806, 0x02}, {0x3807, 0xd0}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, {0x380b, 0xd0}, {0x380c, 0x08}, {0x380d, 0x72}, {0x380e, 0x02}, {0x380f, 0xe4}, {0x3818, 0xc1}, {0x381a, 0x3c}, {0x3a0d, 0x06}, {0x3c01, 0x34}, {0x3007, 0x3b}, {0x5059, 0x80}, {0x3003, 0x03}, {0x3500, 0x04}, {0x3501, 0xa5}, {0x3502, 0x10}, {0x350a, 0x00}, {0x350b, 0x00}, {0x4801, 0x0f}, {0x300e, 0x0c}, {0x4803, 0x50}, {0x4800, 0x24}, {0x300f, 0x8b}, {0x3711, 0x24}, {0x3713, 0x92}, {0x3714, 0x17}, {0x381c, 0x10}, {0x381d, 0x82}, {0x381e, 0x05}, {0x381f, 0xc0}, {0x3821, 0x20}, {0x3824, 0x23}, {0x3825, 0x2c}, {0x3826, 0x00}, {0x3827, 0x0c}, {0x3623, 0x01}, {0x3633, 0x24}, {0x3632, 0x5f}, {0x401f, 0x03}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_320x240[] = { {0x3103, 0x93}, {0x3b07, 0x0c}, {0x3017, 0xff}, {0x3018, 0xfc}, {0x3706, 0x41}, {0x3613, 0xc4}, {0x370d, 0x42}, {0x3703, 0x9a}, {0x3630, 0x22}, {0x3605, 0x04}, {0x3606, 0x3f}, {0x3712, 0x13}, {0x370e, 0x00}, {0x370b, 0x40}, {0x3600, 0x54}, {0x3601, 0x05}, {0x3713, 0x22}, {0x3714, 0x27}, {0x3631, 0x22}, {0x3612, 0x1a}, {0x3604, 0x40}, {0x3705, 0xdc}, {0x370a, 0x83}, {0x370c, 0xc8}, {0x3710, 0x28}, {0x3702, 0x3a}, {0x3704, 0x18}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3a00, 0x38}, {0x3800, 0x02}, {0x3801, 0x54}, {0x3803, 0x0c}, {0x380c, 0x0c}, {0x380d, 0xb4}, {0x380e, 0x07}, {0x380f, 0xb0}, {0x3830, 0x50}, {0x3a08, 0x12}, {0x3a09, 0x70}, {0x3a0a, 0x0f}, {0x3a0b, 0x60}, {0x3a0d, 0x06}, {0x3a0e, 0x06}, {0x3a13, 0x54}, {0x3815, 0x82}, {0x5059, 0x80}, {0x3615, 0x52}, {0x505a, 0x0a}, {0x505b, 0x2e}, {0x3713, 0x92}, {0x3714, 0x17}, {0x3803, 0x0a}, {0x3804, 0x05}, {0x3805, 0x00}, {0x3806, 0x01}, {0x3807, 0x00}, {0x3808, 0x01}, {0x3809, 0x40}, {0x380a, 0x01}, {0x380b, 0x00}, {0x380c, 0x0a}, {0x380d, 0x04}, {0x380e, 0x01}, {0x380f, 0x38}, {0x3500, 0x00}, {0x3501, 0x13}, {0x3502, 0x80}, {0x350b, 0x7f}, {0x3815, 0x81}, {0x3824, 0x23}, {0x3825, 0x20}, {0x3826, 0x00}, {0x3827, 0x08}, {0x370d, 0xc2}, {0x3a08, 0x17}, {0x3a09, 0x64}, {0x3a0a, 0x13}, {0x3a0b, 0x80}, {0x3a00, 0x58}, {0x3a1a, 0x06}, {0x3503, 0x00}, {0x3623, 0x01}, {0x3633, 0x24}, {0x3c01, 0x34}, {0x3c04, 0x28}, {0x3c05, 0x98}, {0x3c07, 0x07}, {0x3c09, 0xc2}, {0x4000, 0x05}, {0x401d, 0x08}, {0x4001, 0x02}, {0x401c, 0x42}, {0x5046, 0x09}, {0x3810, 0x40}, {0x3836, 0x41}, {0x505f, 0x04}, {0x5001, 0x00}, {0x5002, 0x02}, {0x503d, 0x00}, {0x5901, 0x08}, {0x585a, 0x01}, {0x585b, 0x2c}, {0x585c, 0x01}, {0x585d, 0x93}, {0x585e, 0x01}, {0x585f, 0x90}, {0x5860, 0x01}, {0x5861, 0x0d}, {0x5180, 0xc0}, {0x5184, 0x00}, {0x470a, 0x00}, {0x470b, 0x00}, {0x470c, 0x00}, {0x300f, 0x8e}, {0x3603, 0xa7}, {0x3632, 0x55}, {0x3620, 0x56}, {0x3621, 0xaf}, {0x3818, 0xc3}, {0x3631, 0x36}, {0x3632, 0x5f}, {0x3711, 0x24}, {0x401f, 0x03}, {0x3011, 0x14}, {0x3007, 0x3B}, {0x300f, 0x8f}, {0x4801, 0x0f}, {0x3003, 0x03}, {0x300e, 0x0c}, {0x3010, 0x15}, {0x4803, 0x50}, {0x4800, 0x24}, {0x4837, 0x40}, {0x3815, 0x82}, {OV5650_TABLE_END, 0x0000} }; static struct ov5650_reg mode_end[] = { {0x3212, 0x00}, {0x3003, 0x01}, {0x3212, 0x10}, {0x3212, 0xa0}, {0x3008, 0x02}, {OV5650_TABLE_END, 0x0000} }; enum { OV5650_MODE_2592x1944, OV5650_MODE_2080x1164, OV5650_MODE_1920x1080, OV5650_MODE_1296x972, OV5650_MODE_1280x720, OV5650_MODE_1264x704, OV5650_MODE_320x240, OV5650_MODE_INVALID }; static struct ov5650_reg *mode_table[] = { [OV5650_MODE_2592x1944] = mode_2592x1944, [OV5650_MODE_2080x1164] = mode_2080x1164, [OV5650_MODE_1920x1080] = mode_1920x1080, [OV5650_MODE_1296x972] = mode_1296x972, [OV5650_MODE_1280x720] = mode_1280x720, [OV5650_MODE_1264x704] = mode_1264x704, [OV5650_MODE_320x240] = mode_320x240 }; static const struct v4l2_frmsize_discrete ov5650_frmsizes[] = { {2592, 1944}, {2080, 1164}, {1920, 1080}, {1296, 972}, {1280, 720}, {1264, 704}, {320, 240}, }; static int ov5650_find_mode(u32 width, u32 height) { if (width == 2592 && height == 1944) return OV5650_MODE_2592x1944; else if (width == 2080 && height == 1164) return OV5650_MODE_2080x1164; else if (width == 1920 && height == 1080) return OV5650_MODE_1920x1080; else if (width == 1296 && height == 972) return OV5650_MODE_1296x972; else if (width == 1280 && height == 720) return OV5650_MODE_1280x720; else if (width == 1264 && height == 704) return OV5650_MODE_1264x704; else if (width == 320 && height == 240) return OV5650_MODE_320x240; else { pr_err("ov5650: %dx%d is not supported\n", width, height); return OV5650_MODE_2592x1944; } } /** * ov5650_reg_read - Read a value from a register in an ov5650 sensor device * @client: i2c driver client structure * @reg: register address / offset * @val: stores the value that gets read * * Read a value from a register in an ov5650 sensor device. * The value is returned in 'val'. * Returns zero if successful, or non-zero otherwise. */ static int ov5650_reg_read(struct i2c_client *client, u16 reg, u8 *val) { int ret; u8 data[2] = {0}; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .len = 2, .buf = data, }; data[0] = (u8)(reg >> 8); data[1] = (u8)(reg & 0xff); ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) goto err; msg.flags = I2C_M_RD; msg.len = 1; ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) goto err; *val = data[0]; return 0; err: dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); return ret; } /** * Write a value to a register in ov5650 sensor device. * @client: i2c driver client structure. * @reg: Address of the register to read value from. * @val: Value to be written to a specific register. * Returns zero if successful, or non-zero otherwise. */ static int ov5650_reg_write(struct i2c_client *client, u16 reg, u8 val) { int ret; unsigned char data[3] = { (u8)(reg >> 8), (u8)(reg & 0xff), val }; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .len = 3, .buf = data, }; ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) { dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); return ret; } return 0; } static int ov5650_write_bulk_reg(struct ov5650_priv *priv, int len) { struct i2c_client *client = priv->client; u8 *data = priv->i2c_trans_buf; int err; struct i2c_msg msg; if (!client->adapter) return -ENODEV; msg.addr = client->addr; msg.flags = 0; msg.len = len; msg.buf = data; err = i2c_transfer(client->adapter, &msg, 1); if (err != 1) { dev_err(&client->dev, "I2C bulk transfer failed at %x\n", (int)data[0] << 8 | data[1]); return err; } return 0; } static int ov5650_write_table(struct ov5650_priv *priv, const struct ov5650_reg table[]) { int err; const struct ov5650_reg *next, *n_next; u8 *b_ptr = priv->i2c_trans_buf; unsigned int buf_filled = 0; u16 val; for (next = table; next->addr != OV5650_TABLE_END; next++) { if (next->addr == OV5650_TABLE_WAIT_MS) { msleep(next->val); continue; } val = next->val; if (!buf_filled) { b_ptr = priv->i2c_trans_buf; *b_ptr++ = next->addr >> 8; *b_ptr++ = next->addr & 0xff; buf_filled = 2; } *b_ptr++ = val; buf_filled++; n_next = next + 1; if (n_next->addr != OV5650_TABLE_END && n_next->addr != OV5650_TABLE_WAIT_MS && buf_filled < SIZEOF_I2C_TRANSBUF && n_next->addr == next->addr + 1) { continue; } err = ov5650_write_bulk_reg(priv, buf_filled); if (err) return err; buf_filled = 0; } return 0; } /* ----------------------------------------------------------------------------- * V4L2 subdev internal operations */ static void ov5650_set_default_fmt(struct ov5650_priv *priv) { struct v4l2_mbus_framefmt *mf = &priv->mf; mf->width = ov5650_frmsizes[OV5650_MODE_2592x1944].width; mf->height = ov5650_frmsizes[OV5650_MODE_2592x1944].height; mf->code = V4L2_MBUS_FMT_SBGGR10_1X10; mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_SRGB; } static int ov5650_s_stream(struct v4l2_subdev *sd, int enable) { struct ov5650_priv *priv = to_ov5650(sd); int ret = 0; if (!enable) { ov5650_set_default_fmt(priv); return 0; } ret = ov5650_write_table(priv, reset_seq); if (ret) return ret; ret = ov5650_write_table(priv, mode_start); if (ret) return ret; ret = ov5650_write_table(priv, mode_table[priv->mode]); if (ret) return ret; ret = ov5650_write_table(priv, mode_end); if (ret) return ret; switch (test_mode) { case TP_NONE: ret = ov5650_write_table(priv, tp_none_seq); break; case TP_COLORBARS: ret = ov5650_write_table(priv, tp_cbars_seq); break; case TP_CHECKER: ret = ov5650_write_table(priv, tp_checker_seq); break; } if (ret) return ret; return ret; } static int ov5650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct ov5650_priv *priv = to_ov5650(sd); priv->mode = ov5650_find_mode(mf->width, mf->height); memcpy(&priv->mf, mf, sizeof(struct v4l2_mbus_framefmt)); return 0; } static int ov5650_s_power(struct v4l2_subdev *sd, int on) { struct ov5650_priv *priv = to_ov5650(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct soc_camera_link *icl = soc_camera_i2c_to_link(client); if (on) ov5650_s_fmt(sd, &priv->mf); return soc_camera_set_power(&client->dev, icl, on); } static int ov5650_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { int mode; mode = ov5650_find_mode(mf->width, mf->height); mf->width = ov5650_frmsizes[mode].width; mf->height = ov5650_frmsizes[mode].height; if (mf->code != V4L2_MBUS_FMT_SBGGR8_1X8 && mf->code != V4L2_MBUS_FMT_SBGGR10_1X10) mf->code = V4L2_MBUS_FMT_SBGGR10_1X10; mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_SRGB; ov5650_s_fmt(sd, mf); return 0; } static int ov5650_enum_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code) { if (index >= 2) return -EINVAL; switch (index) { case 0: *code = V4L2_MBUS_FMT_SBGGR10_1X10; break; case 1: *code = V4L2_MBUS_FMT_SBGGR8_1X8; break; } return 0; } static int ov5650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { a->bounds.left = 0; a->bounds.top = 0; a->bounds.width = ov5650_frmsizes[OV5650_MODE_2592x1944].width; a->bounds.height = ov5650_frmsizes[OV5650_MODE_2592x1944].height; a->defrect = a->bounds; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; return 0; } static int ov5650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { a->c.left = 0; a->c.top = 0; a->c.width = ov5650_frmsizes[OV5650_MODE_2592x1944].width; a->c.height = ov5650_frmsizes[OV5650_MODE_2592x1944].height; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } /* Get chip identification */ static int ov5650_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *id) { struct ov5650_priv *priv = to_ov5650(sd); id->ident = priv->ident; id->revision = priv->revision; return 0; } static struct v4l2_subdev_video_ops ov5650_video_ops = { .s_stream = ov5650_s_stream, .s_mbus_fmt = ov5650_s_fmt, .try_mbus_fmt = ov5650_try_fmt, .enum_mbus_fmt = ov5650_enum_fmt, .cropcap = ov5650_cropcap, .g_crop = ov5650_g_crop, }; static struct v4l2_subdev_core_ops ov5650_core_ops = { .g_chip_ident = ov5650_g_chip_ident, .s_power = ov5650_s_power, }; static struct v4l2_subdev_ops ov5650_subdev_ops = { .core = &ov5650_core_ops, .video = &ov5650_video_ops, }; static int ov5650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5650_priv *priv; struct soc_camera_link *icl; u8 chipid[2]; int ret; /* Checking soc-camera interface */ icl = soc_camera_i2c_to_link(client); if (!icl) { dev_err(&client->dev, "Missing soc_camera_link for driver\n"); return -EINVAL; } /* Register OV5650 soc_camera device interface */ priv = kzalloc(sizeof(struct ov5650_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate private data!\n"); return -ENOMEM; } v4l2_i2c_subdev_init(&priv->subdev, client, &ov5650_subdev_ops); priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; priv->ident = V4L2_IDENT_OV5650; /* Detecting OV5650 sensor */ soc_camera_power_on(&client->dev, icl); ret = ov5650_reg_read(client, 0x300A, &chipid[0]); if (ret) { dev_err(&client->dev, "Failure to read Chip ID (high byte)\n"); goto err; } ret = ov5650_reg_read(client, 0x300B, &chipid[1]); if (ret) { dev_err(&client->dev, "Failure to read Chip ID (low byte)\n"); goto err; } priv->chip_id = (chipid[0] << 8) | chipid[1]; if ((priv->chip_id != 0x5650) && (priv->chip_id != 0x5651)) { dev_err(&client->dev, "Chip ID: %x not supported!\n", priv->chip_id); ret = -ENODEV; goto err; } priv->revision = (chipid[1] == 0x50) ? 0x1A : 0x1B; soc_camera_power_off(&client->dev, icl); priv->client = client; ov5650_set_default_fmt(priv); dev_info(&client->dev, "Detected a OV%x chip, revision %x\n", priv->chip_id, priv->revision); return 0; err: kfree(priv); return ret; } static int ov5650_remove(struct i2c_client *client) { struct ov5650_priv *priv = i2c_get_clientdata(client); kfree(priv); return 0; } static const struct i2c_device_id ov5650_id[] = { { "ov5650_v4l2", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ov5650_id); static struct i2c_driver ov5650_i2c_driver = { .driver = { .name = "ov5650_v4l2", }, .probe = ov5650_probe, .remove = ov5650_remove, .id_table = ov5650_id, }; static int __init ov5650_module_init(void) { return i2c_add_driver(&ov5650_i2c_driver); } static void __exit ov5650_module_exit(void) { i2c_del_driver(&ov5650_i2c_driver); } module_init(ov5650_module_init); module_exit(ov5650_module_exit); MODULE_DESCRIPTION("OmniVision OV5650 Camera driver"); MODULE_AUTHOR("Bryan Wu "); MODULE_LICENSE("GPL v2");