diff options
-rw-r--r-- | drivers/media/video/Kconfig | 6 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/ov5650.c | 1382 | ||||
-rw-r--r-- | include/media/v4l2-chip-ident.h | 1 |
4 files changed, 1390 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 651a17e04fd0..dfff30255a92 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -867,6 +867,12 @@ config SOC_CAMERA_OV5642 help This is a V4L2 camera driver for the OmniVision OV5642 sensor +config SOC_CAMERA_OV5650 + tristate "ov5650 sensor support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV5650 sensor + config SOC_CAMERA_OV6650 tristate "ov6650 sensor support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index f63ad7e88fda..ce79916258ef 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o obj-$(CONFIG_SOC_CAMERA_OV5640) += ov5640.o obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o +obj-$(CONFIG_SOC_CAMERA_OV5650) += ov5650.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o diff --git a/drivers/media/video/ov5650.c b/drivers/media/video/ov5650.c new file mode 100644 index 000000000000..e721065c222b --- /dev/null +++ b/drivers/media/video/ov5650.c @@ -0,0 +1,1382 @@ +/* + * OmniVision OV5650 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 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 <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/delay.h> +#include <linux/module.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#include <media/ov5650.h> + +#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} +}; + +static struct ov5650_reg *test_pattern_modes[] = { + tp_none_seq, + tp_cbars_seq, + tp_checker_seq, +}; + +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, 0x06}, + {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, 0x0c}, + {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[], + const struct ov5650_reg override_list[], + int num_override_regs) +{ + int err; + const struct ov5650_reg *next, *n_next; + u8 *b_ptr = priv->i2c_trans_buf; + unsigned int buf_filled = 0; + unsigned int i; + 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; + /* 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; + } + } + } + + 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_SGRBG10_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); + struct ov5650_reg reg_list[6]; + int ret = 0; + + if (enable) { + ret = ov5650_write_table(priv, reset_seq, NULL, 0); + if (ret) + return ret; + + ret = ov5650_write_table(priv, mode_start, NULL, 0); + if (ret) + return ret; + + ret = ov5650_write_table(priv, mode_table[priv->mode], + reg_list, 6); + if (ret) + return ret; + + ret = ov5650_write_table(priv, mode_end, NULL, 0); + if (ret) + return ret; + } else + ov5650_set_default_fmt(priv); + + return ret; +} + +/* Alter bus settings on camera side */ +static int ov5650_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov5650_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_10; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +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); + + if (on) { + ov5650_s_fmt(sd, &priv->mf); + ov5650_s_stream(sd, 1); + } else + ov5650_s_stream(sd, 0); + + return 0; +} + +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_SGRBG8_1X8 && + mf->code != V4L2_MBUS_FMT_SGRBG10_1X10) + mf->code = V4L2_MBUS_FMT_SGRBG10_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_SGRBG10_1X10; + break; + case 1: + *code = V4L2_MBUS_FMT_SGRBG8_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 soc_camera_ops ov5650_ops = { + .set_bus_param = ov5650_set_bus_param, + .query_bus_param = ov5650_query_bus_param, +}; + +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_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + u8 chipid[2]; + int ret; + + /* Checking soc-camera interface */ + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data 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); + icd->ops = &ov5650_ops; + + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + priv->ident = V4L2_IDENT_OV5650; + + 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; + } + + if ((chipid[0] != 0x56) || ((chipid[1] & 0x51) != chipid[1])) { + dev_err(&client->dev, "Chip ID: %x%x not supported!\n", + chipid[0], chipid[1]); + ret = -ENODEV; + goto err; + } + + priv->chip_id = (chipid[0] << 8) | chipid[1]; + priv->revision = (chipid[1] == 0x50) ? 0x1A : 0x1B; + + 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", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5650_id); + +static struct i2c_driver ov5650_i2c_driver = { + .driver = { + .name = "ov5650", + }, + .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 <pengw@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index fb929eacd2e6..a0d15e57cc6f 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -78,6 +78,7 @@ enum { V4L2_IDENT_OV9740 = 260, V4L2_IDENT_OV5642 = 261, V4L2_IDENT_OV5640 = 262, + V4L2_IDENT_OV5650 = 263, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, |