From 14be139209156ae9b7feb02cba06f114381cd69d Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Thu, 2 Sep 2010 18:12:14 -0700 Subject: [PATCH] media: video: tegra: ov5650: Several changes to 5MP imager sensor Add ability to read sensor otp. Add support for pattern generators. Cleans up the ioctl parameters. Change-Id: I78b585fb94b5d97e2adebe718972019e4e2142de Signed-off-by: Rebecca Schultz Zavin --- drivers/media/video/tegra/ov5650.c | 181 ++++++++++++++++++++++++++--- include/media/ov5650.h | 38 +++++- 2 files changed, 197 insertions(+), 22 deletions(-) diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c index 5c92e2f6adab..c9ed7badfeb5 100644 --- a/drivers/media/video/tegra/ov5650.c +++ b/drivers/media/video/tegra/ov5650.c @@ -5,6 +5,7 @@ * * Contributors: * Rebecca Schultz Zavin + * Andrei Warkentin * * Leverage OV9640.c * @@ -20,6 +21,7 @@ #include #include #include +#include struct ov5650_reg { u16 addr; @@ -30,17 +32,48 @@ struct ov5650_info { int mode; struct i2c_client *i2c_client; struct ov5650_platform_data *pdata; + struct ov5650_otp_data otp_data; + bool otp_valid; }; #define OV5650_TABLE_WAIT_MS 0 #define OV5650_TABLE_END 1 #define OV5650_MAX_RETRIES 3 -static struct ov5650_reg mode_start[] = { +static struct ov5650_reg tp_none_seq[] = { + {0x5046, 0x00}, /* isp_off */ + {OV5650_TABLE_END, 0} +}; + +static struct ov5650_reg tp_cbars_seq[] = { + {0x503D, 0xC0}, + {0x503E, 0x00}, + {0x5046, 0x01}, /* isp_on */ + {OV5650_TABLE_END, 0} +}; + +static struct ov5650_reg tp_checker_seq[] = { + {0x503D, 0xC0}, + {0x503E, 0x0A}, + {0x5046, 0x01}, /* isp_on */ + {OV5650_TABLE_END, 0} +}; + +static struct ov5650_reg reset_seq[] = { {0x3008, 0x82}, /* reset registers pg 72 */ {OV5650_TABLE_WAIT_MS, 5}, {0x3008, 0x42}, /* register power down pg 72 */ {OV5650_TABLE_WAIT_MS, 5}, + {OV5650_TABLE_END, 0x0}, +}; + +static struct ov5650_reg *test_pattern_modes[] = { + tp_none_seq, + tp_cbars_seq, + tp_checker_seq, +}; + +static struct ov5650_reg mode_start[] = { {0x3103, 0x93}, /* power up system clock from PLL page 77 */ {0x3017, 0xff}, /* PAD output enable page 100 */ {0x3018, 0xfc}, /* PAD output enable page 100 */ @@ -95,8 +128,6 @@ static struct ov5650_reg mode_start[] = { {OV5650_TABLE_END, 0x0}, }; -static const int mode_start_len = ARRAY_SIZE(mode_start); - static struct ov5650_reg mode_2592x1944[] = { {0x3621, 0x2f}, /* analog horizontal binning/sampling not enabled. pg 108 */ @@ -382,6 +413,9 @@ static int ov5650_set_mode(struct ov5650_info *info, struct ov5650_mode *mode) return -EINVAL; } + err = ov5650_write_table(info->i2c_client, reset_seq); + if (err) + return err; err = ov5650_write_table(info->i2c_client, mode_start); if (err) return err; @@ -397,32 +431,55 @@ static int ov5650_set_mode(struct ov5650_info *info, struct ov5650_mode *mode) static int ov5650_set_frame_length(struct ov5650_info *info, u32 frame_length) { - ov5650_write_reg(info->i2c_client, 0x380e, (frame_length >> 8) & 0xff); - ov5650_write_reg(info->i2c_client, 0x380f, frame_length & 0xff); + int ret; + + ret = ov5650_write_reg(info->i2c_client, 0x380e, + (frame_length >> 8) & 0xff); + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x380f, frame_length & 0xff); + if (ret) + return ret; return 0; } static int ov5650_set_coarse_time(struct ov5650_info *info, u32 coarse_time) { - ov5650_write_reg(info->i2c_client, 0x3212, 0x01); - ov5650_write_reg(info->i2c_client, 0x3500, - (coarse_time >> 12) & 0xff); - ov5650_write_reg(info->i2c_client, 0x3501, + int ret; + + ret = ov5650_write_reg(info->i2c_client, 0x3212, 0x01); + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x3500, + (coarse_time >> 12) & 0xff); + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x3501, (coarse_time >> 4) & 0xff); - ov5650_write_reg(info->i2c_client, 0x3502, + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x3502, (coarse_time & 0xf) << 4); - ov5650_write_reg(info->i2c_client, 0x3212, 0x11); - ov5650_write_reg(info->i2c_client, 0x3212, 0xa1); + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x3212, 0x11); + if (ret) + return ret; + ret = ov5650_write_reg(info->i2c_client, 0x3212, 0xa1); + if (ret) + return ret; return 0; } static int ov5650_set_gain(struct ov5650_info *info, u16 gain) { - ov5650_write_reg(info->i2c_client, 0x350b, gain); + int ret; - return 0; + ret = ov5650_write_reg(info->i2c_client, 0x350b, gain); + + return ret; } static int ov5650_get_status(struct ov5650_info *info, u8 *status) @@ -435,10 +492,88 @@ static int ov5650_get_status(struct ov5650_info *info, u8 *status) return err; } +static int ov5650_get_otp(struct ov5650_info *info, void __user *ubuffer) +{ + int err; + uint8_t i; + uint8_t *otpp; + uint16_t computed_crc = 0; + + BUILD_BUG_ON(sizeof(struct ov5650_otp_data) != 256); + + otpp = (uint8_t *)&info->otp_data; + + /* Either we never read the OTP or CRC failure. */ + if (info->otp_valid) + goto end; + + err = ov5650_write_table(info->i2c_client, reset_seq); + if (err) + return err; + + /* Read OTP byte by byte. */ + i = (uint8_t) offsetof(struct ov5650_otp_data, part_num); + err = ov5650_write_reg(info->i2c_client, 0x3D00, i); + if (err) + return err; + + while (i < offsetof(struct ov5650_otp_data, reserved2)) { + err = ov5650_read_reg(info->i2c_client, 0x3D04, otpp + i); + if (err) + return err; + + computed_crc = crc16_byte(computed_crc, *(otpp + i)); + i++; + } + + /* Serial number is BE. */ + info->otp_data.serial_num = __be32_to_cpu(info->otp_data.serial_num); + + /* Read the CRC and compared to computed. */ + i = offsetof(struct ov5650_otp_data, crc); + err = ov5650_write_reg(info->i2c_client, 0x3D00, i); + if (err) + return err; + + while (i < offsetof(struct ov5650_otp_data, reserved3)) { + err = ov5650_read_reg(info->i2c_client, 0x3D04, otpp + i); + if (err) + return err; + i++; + } + + /* CRC is BE, so convert... */ + info->otp_data.crc = __be16_to_cpu(info->otp_data.crc); + if (info->otp_data.crc != computed_crc) { + pr_info("CRC mismatch - OTP 0x%x Calc 0x%x\n", + info->otp_data.crc, computed_crc); + return -EIO; + } + info->otp_valid = true; + +end: + if (copy_to_user(ubuffer, &info->otp_data, sizeof(info->otp_data))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + + return 0; +} + +static int ov5650_test_pattern(struct ov5650_info *info, + enum ov5650_test_pattern pattern) +{ + if (pattern >= ARRAY_SIZE(test_pattern_modes)) + return -EINVAL; + + return ov5650_write_table(info->i2c_client, + test_pattern_modes[pattern]); +} static int ov5650_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int err; struct ov5650_info *info = file->private_data; switch (cmd) { @@ -462,7 +597,6 @@ static int ov5650_ioctl(struct inode *inode, struct file *file, return ov5650_set_gain(info, (u16)arg); case OV5650_IOCTL_GET_STATUS: { - int err; u8 status; err = ov5650_get_status(info, &status); @@ -475,8 +609,22 @@ static int ov5650_ioctl(struct inode *inode, struct file *file, } return 0; } + case OV5650_IOCTL_GET_OTP: + { + err = ov5650_get_otp(info, (void __user *) arg); + if (err) + pr_err("%s %d %d\n", __func__, __LINE__, err); + return err; + } + case OV5650_IOCTL_TEST_PATTERN: + { + err = ov5650_test_pattern(info, (enum ov5650_test_pattern) arg); + if (err) + pr_err("%s %d %d\n", __func__, __LINE__, err); + return err; + } default: - return -EINVAL; + return -EINVAL; } return 0; } @@ -540,6 +688,7 @@ static int ov5650_probe(struct i2c_client *client, info->pdata = client->dev.platform_data; info->i2c_client = client; + info->otp_valid = false; i2c_set_clientdata(client, info); return 0; diff --git a/include/media/ov5650.h b/include/media/ov5650.h index 2dce30a14653..04cbcf0c0128 100755 --- a/include/media/ov5650.h +++ b/include/media/ov5650.h @@ -21,17 +21,43 @@ #include /* For IOCTL macros */ -#define OV5650_IOCTL_SET_MODE _IOWR('o', 1, struct ov5650_mode) -#define OV5650_IOCTL_SET_FRAME_LENGTH _IOWR('o', 2, u32) -#define OV5650_IOCTL_SET_COARSE_TIME _IOWR('o', 3, u32) -#define OV5650_IOCTL_SET_GAIN _IOWR('o', 4, u16) -#define OV5650_IOCTL_GET_STATUS _IOR('o', 5, u8) +#define OV5650_IOCTL_SET_MODE _IOW('o', 1, struct ov5650_mode) +#define OV5650_IOCTL_SET_FRAME_LENGTH _IOW('o', 2, __u32) +#define OV5650_IOCTL_SET_COARSE_TIME _IOW('o', 3, __u32) +#define OV5650_IOCTL_SET_GAIN _IOW('o', 4, __u16) +#define OV5650_IOCTL_GET_STATUS _IOR('o', 5, __u8) +#define OV5650_IOCTL_GET_OTP _IOR('o', 6, struct ov5650_otp_data) +#define OV5650_IOCTL_TEST_PATTERN _IOW('o', 7, enum ov5650_test_pattern) + +enum ov5650_test_pattern { + TEST_PATTERN_NONE, + TEST_PATTERN_COLORBARS, + TEST_PATTERN_CHECKERBOARD +}; + +struct ov5650_otp_data { + __u8 reserved1[6]; + __u8 part_num[8]; + __u8 lens_id[1]; + __u8 manufacture_id[2]; + __u8 factory_id[2]; + __u8 manufacture_date[9]; + __u8 manufacture_line[2]; + + __u32 serial_num; + __u8 focuser_cal[16]; + __u8 shutter_cal[16]; + __u8 reserved2[183]; + + __u16 crc; + __u8 reserved3[3]; + __u8 auto_load[2]; +} __attribute__ ((packed)); struct ov5650_mode { int xres; int yres; }; - #ifdef __KERNEL__ struct ov5650_platform_data { int (*power_on)(void); -- 2.34.1