M7350/kernel/drivers/media/platform/msm/camera_v1/ov7692.c
2024-09-09 08:52:07 +00:00

598 lines
14 KiB
C

/* Copyright (c) 2010-2011, The Linux Foundation. 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 version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that 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.
*/
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <media/msm_camera.h>
#include <mach/camera.h>
#include "ov7692.h"
/*=============================================================
SENSOR REGISTER DEFINES
==============================================================*/
#define Q8 0x00000100
/* Omnivision8810 product ID register address */
#define REG_OV7692_MODEL_ID_MSB 0x0A
#define REG_OV7692_MODEL_ID_LSB 0x0B
#define OV7692_MODEL_ID 0x7692
/* Omnivision8810 product ID */
/* Time in milisecs for waiting for the sensor to reset */
#define OV7692_RESET_DELAY_MSECS 66
#define OV7692_DEFAULT_CLOCK_RATE 24000000
/* Registers*/
/* Color bar pattern selection */
#define OV7692_COLOR_BAR_PATTERN_SEL_REG 0x82
/* Color bar enabling control */
#define OV7692_COLOR_BAR_ENABLE_REG 0x601
/* Time in milisecs for waiting for the sensor to reset*/
#define OV7692_RESET_DELAY_MSECS 66
/*============================================================================
DATA DECLARATIONS
============================================================================*/
/* 96MHz PCLK @ 24MHz MCLK */
struct reg_addr_val_pair_struct ov7692_init_settings_array[] = {
{0x12, 0x80},
{0x0e, 0x08},
{0x69, 0x52},
{0x1e, 0xb3},
{0x48, 0x42},
{0xff, 0x01},
{0xae, 0xa0},
{0xa8, 0x26},
{0xb4, 0xc0},
{0xb5, 0x40},
{0xff, 0x00},
{0x0c, 0x00},
{0x62, 0x10},
{0x12, 0x00},
{0x17, 0x65},
{0x18, 0xa4},
{0x19, 0x0a},
{0x1a, 0xf6},
{0x3e, 0x30},
{0x64, 0x0a},
{0xff, 0x01},
{0xb4, 0xc0},
{0xff, 0x00},
{0x67, 0x20},
{0x81, 0x3f},
{0xcc, 0x02},
{0xcd, 0x80},
{0xce, 0x01},
{0xcf, 0xe0},
{0xc8, 0x02},
{0xc9, 0x80},
{0xca, 0x01},
{0xcb, 0xe0},
{0xd0, 0x48},
{0x82, 0x03},
{0x0e, 0x00},
{0x70, 0x00},
{0x71, 0x34},
{0x74, 0x28},
{0x75, 0x98},
{0x76, 0x00},
{0x77, 0x64},
{0x78, 0x01},
{0x79, 0xc2},
{0x7a, 0x4e},
{0x7b, 0x1f},
{0x7c, 0x00},
{0x11, 0x00},
{0x20, 0x00},
{0x21, 0x23},
{0x50, 0x9a},
{0x51, 0x80},
{0x4c, 0x7d},
{0x0e, 0x00},
{0x80, 0x7f},
{0x85, 0x10},
{0x86, 0x00},
{0x87, 0x00},
{0x88, 0x00},
{0x89, 0x2a},
{0x8a, 0x26},
{0x8b, 0x22},
{0xbb, 0x7a},
{0xbc, 0x69},
{0xbd, 0x11},
{0xbe, 0x13},
{0xbf, 0x81},
{0xc0, 0x96},
{0xc1, 0x1e},
{0xb7, 0x05},
{0xb8, 0x09},
{0xb9, 0x00},
{0xba, 0x18},
{0x5a, 0x1f},
{0x5b, 0x9f},
{0x5c, 0x6a},
{0x5d, 0x42},
{0x24, 0x78},
{0x25, 0x68},
{0x26, 0xb3},
{0xa3, 0x0b},
{0xa4, 0x15},
{0xa5, 0x2a},
{0xa6, 0x51},
{0xa7, 0x63},
{0xa8, 0x74},
{0xa9, 0x83},
{0xaa, 0x91},
{0xab, 0x9e},
{0xac, 0xaa},
{0xad, 0xbe},
{0xae, 0xce},
{0xaf, 0xe5},
{0xb0, 0xf3},
{0xb1, 0xfb},
{0xb2, 0x06},
{0x8c, 0x5c},
{0x8d, 0x11},
{0x8e, 0x12},
{0x8f, 0x19},
{0x90, 0x50},
{0x91, 0x20},
{0x92, 0x96},
{0x93, 0x80},
{0x94, 0x13},
{0x95, 0x1b},
{0x96, 0xff},
{0x97, 0x00},
{0x98, 0x3d},
{0x99, 0x36},
{0x9a, 0x51},
{0x9b, 0x43},
{0x9c, 0xf0},
{0x9d, 0xf0},
{0x9e, 0xf0},
{0x9f, 0xff},
{0xa0, 0x68},
{0xa1, 0x62},
{0xa2, 0x0e},
};
static bool OV7692_CSI_CONFIG;
/* 816x612, 24MHz MCLK 96MHz PCLK */
uint32_t OV7692_FULL_SIZE_WIDTH = 640;
uint32_t OV7692_FULL_SIZE_HEIGHT = 480;
uint32_t OV7692_QTR_SIZE_WIDTH = 640;
uint32_t OV7692_QTR_SIZE_HEIGHT = 480;
uint32_t OV7692_HRZ_FULL_BLK_PIXELS = 16;
uint32_t OV7692_VER_FULL_BLK_LINES = 12;
uint32_t OV7692_HRZ_QTR_BLK_PIXELS = 16;
uint32_t OV7692_VER_QTR_BLK_LINES = 12;
struct ov7692_work_t {
struct work_struct work;
};
static struct ov7692_work_t *ov7692_sensorw;
static struct i2c_client *ov7692_client;
struct ov7692_ctrl_t {
const struct msm_camera_sensor_info *sensordata;
uint32_t sensormode;
uint32_t fps_divider; /* init to 1 * 0x00000400 */
uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
uint32_t fps;
int32_t curr_lens_pos;
uint32_t curr_step_pos;
uint32_t my_reg_gain;
uint32_t my_reg_line_count;
uint32_t total_lines_per_frame;
enum ov7692_resolution_t prev_res;
enum ov7692_resolution_t pict_res;
enum ov7692_resolution_t curr_res;
enum ov7692_test_mode_t set_test;
unsigned short imgaddr;
};
static struct ov7692_ctrl_t *ov7692_ctrl;
static DECLARE_WAIT_QUEUE_HEAD(ov7692_wait_queue);
DEFINE_MUTEX(ov7692_mut);
/*=============================================================*/
static int ov7692_i2c_rxdata(unsigned short saddr,
unsigned char *rxdata, int length)
{
struct i2c_msg msgs[] = {
{
.addr = saddr,
.flags = 0,
.len = 1,
.buf = rxdata,
},
{
.addr = saddr,
.flags = I2C_M_RD,
.len = 1,
.buf = rxdata,
},
};
if (i2c_transfer(ov7692_client->adapter, msgs, 2) < 0) {
CDBG("ov7692_i2c_rxdata failed!\n");
return -EIO;
}
return 0;
}
static int32_t ov7692_i2c_txdata(unsigned short saddr,
unsigned char *txdata, int length)
{
struct i2c_msg msg[] = {
{
.addr = saddr,
.flags = 0,
.len = 2,
.buf = txdata,
},
};
if (i2c_transfer(ov7692_client->adapter, msg, 1) < 0) {
CDBG("ov7692_i2c_txdata faild 0x%x\n", ov7692_client->addr);
return -EIO;
}
return 0;
}
static int32_t ov7692_i2c_read(uint8_t raddr,
uint8_t *rdata, int rlen)
{
int32_t rc = 0;
unsigned char buf[1];
if (!rdata)
return -EIO;
memset(buf, 0, sizeof(buf));
buf[0] = raddr;
rc = ov7692_i2c_rxdata(ov7692_client->addr >> 1, buf, rlen);
if (rc < 0) {
CDBG("ov7692_i2c_read 0x%x failed!\n", raddr);
return rc;
}
*rdata = buf[0];
return rc;
}
static int32_t ov7692_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
{
int32_t rc = -EFAULT;
unsigned char buf[2];
memset(buf, 0, sizeof(buf));
buf[0] = waddr;
buf[1] = bdata;
CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
rc = ov7692_i2c_txdata(ov7692_client->addr >> 1, buf, 2);
if (rc < 0)
CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
waddr, bdata);
return rc;
}
static int32_t ov7692_sensor_setting(int update_type, int rt)
{
int32_t i, array_length;
int32_t rc = 0;
struct msm_camera_csi_params ov7692_csi_params;
switch (update_type) {
case REG_INIT:
OV7692_CSI_CONFIG = 0;
ov7692_i2c_write_b_sensor(0x0e, 0x08);
return rc;
break;
case UPDATE_PERIODIC:
if (!OV7692_CSI_CONFIG) {
ov7692_csi_params.lane_cnt = 1;
ov7692_csi_params.data_format = CSI_8BIT;
ov7692_csi_params.lane_assign = 0xe4;
ov7692_csi_params.dpcm_scheme = 0;
ov7692_csi_params.settle_cnt = 0x14;
rc = msm_camio_csi_config(&ov7692_csi_params);
msleep(10);
array_length = sizeof(ov7692_init_settings_array) /
sizeof(ov7692_init_settings_array[0]);
for (i = 0; i < array_length; i++) {
rc = ov7692_i2c_write_b_sensor(
ov7692_init_settings_array[i].reg_addr,
ov7692_init_settings_array[i].reg_val);
if (rc < 0)
return rc;
}
OV7692_CSI_CONFIG = 1;
msleep(20);
return rc;
}
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int32_t ov7692_video_config(int mode)
{
int32_t rc = 0;
int rt;
/* change sensor resolution if needed */
rt = RES_PREVIEW;
if (ov7692_sensor_setting(UPDATE_PERIODIC, rt) < 0)
return rc;
ov7692_ctrl->curr_res = ov7692_ctrl->prev_res;
ov7692_ctrl->sensormode = mode;
return rc;
}
static int32_t ov7692_set_sensor_mode(int mode,
int res)
{
int32_t rc = 0;
switch (mode) {
case SENSOR_PREVIEW_MODE:
rc = ov7692_video_config(mode);
break;
case SENSOR_SNAPSHOT_MODE:
case SENSOR_RAW_SNAPSHOT_MODE:
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int32_t ov7692_power_down(void)
{
return 0;
}
static int ov7692_probe_init_sensor(const struct msm_camera_sensor_info *data)
{
uint8_t model_id_msb, model_id_lsb = 0;
uint16_t model_id;
int32_t rc = 0;
/*The reset pin is not physically connected to the sensor.
The standby pin will do the reset hence there is no need
to request the gpio reset*/
/* Read sensor Model ID: */
rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_MSB, &model_id_msb, 1);
if (rc < 0)
goto init_probe_fail;
rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_LSB, &model_id_lsb, 1);
if (rc < 0)
goto init_probe_fail;
model_id = (model_id_msb << 8) | ((model_id_lsb & 0x00FF)) ;
CDBG("ov7692 model_id = 0x%x, 0x%x, 0x%x\n",
model_id, model_id_msb, model_id_lsb);
/* 4. Compare sensor ID to OV7692 ID: */
if (model_id != OV7692_MODEL_ID) {
rc = -ENODEV;
goto init_probe_fail;
}
goto init_probe_done;
init_probe_fail:
pr_warning(" ov7692_probe_init_sensor fails\n");
init_probe_done:
CDBG(" ov7692_probe_init_sensor finishes\n");
return rc;
}
int ov7692_sensor_open_init(const struct msm_camera_sensor_info *data)
{
int32_t rc = 0;
CDBG("%s: %d\n", __func__, __LINE__);
CDBG("Calling ov7692_sensor_open_init\n");
ov7692_ctrl = kzalloc(sizeof(struct ov7692_ctrl_t), GFP_KERNEL);
if (!ov7692_ctrl) {
CDBG("ov7692_init failed!\n");
rc = -ENOMEM;
goto init_done;
}
ov7692_ctrl->fps_divider = 1 * 0x00000400;
ov7692_ctrl->pict_fps_divider = 1 * 0x00000400;
ov7692_ctrl->fps = 30 * Q8;
ov7692_ctrl->set_test = TEST_OFF;
ov7692_ctrl->prev_res = QTR_SIZE;
ov7692_ctrl->pict_res = FULL_SIZE;
ov7692_ctrl->curr_res = INVALID_SIZE;
if (data)
ov7692_ctrl->sensordata = data;
/* enable mclk first */
msm_camio_clk_rate_set(24000000);
msleep(20);
rc = ov7692_probe_init_sensor(data);
if (rc < 0) {
CDBG("Calling ov7692_sensor_open_init fail\n");
goto init_fail;
}
rc = ov7692_sensor_setting(REG_INIT, RES_PREVIEW);
if (rc < 0)
goto init_fail;
else
goto init_done;
init_fail:
CDBG(" ov7692_sensor_open_init fail\n");
kfree(ov7692_ctrl);
init_done:
CDBG("ov7692_sensor_open_init done\n");
return rc;
}
static int ov7692_init_client(struct i2c_client *client)
{
/* Initialize the MSM_CAMI2C Chip */
init_waitqueue_head(&ov7692_wait_queue);
return 0;
}
static const struct i2c_device_id ov7692_i2c_id[] = {
{"ov7692", 0},
{ }
};
static int ov7692_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc = 0;
CDBG("ov7692_i2c_probe called!\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
CDBG("i2c_check_functionality failed\n");
goto probe_failure;
}
ov7692_sensorw = kzalloc(sizeof(struct ov7692_work_t), GFP_KERNEL);
if (!ov7692_sensorw) {
CDBG("kzalloc failed.\n");
rc = -ENOMEM;
goto probe_failure;
}
i2c_set_clientdata(client, ov7692_sensorw);
ov7692_init_client(client);
ov7692_client = client;
CDBG("ov7692_i2c_probe success! rc = %d\n", rc);
return 0;
probe_failure:
CDBG("ov7692_i2c_probe failed! rc = %d\n", rc);
return rc;
}
static int __exit ov7692_remove(struct i2c_client *client)
{
struct ov7692_work_t_t *sensorw = i2c_get_clientdata(client);
free_irq(client->irq, sensorw);
ov7692_client = NULL;
kfree(sensorw);
return 0;
}
static struct i2c_driver ov7692_i2c_driver = {
.id_table = ov7692_i2c_id,
.probe = ov7692_i2c_probe,
.remove = __exit_p(ov7692_i2c_remove),
.driver = {
.name = "ov7692",
},
};
int ov7692_sensor_config(void __user *argp)
{
struct sensor_cfg_data cdata;
long rc = 0;
if (copy_from_user(&cdata,
(void *)argp,
sizeof(struct sensor_cfg_data)))
return -EFAULT;
mutex_lock(&ov7692_mut);
CDBG("ov7692_sensor_config: cfgtype = %d\n",
cdata.cfgtype);
switch (cdata.cfgtype) {
case CFG_SET_MODE:
rc = ov7692_set_sensor_mode(cdata.mode,
cdata.rs);
break;
case CFG_PWR_DOWN:
rc = ov7692_power_down();
break;
case CFG_SET_EFFECT:
break;
default:
rc = -EFAULT;
break;
}
mutex_unlock(&ov7692_mut);
return rc;
}
static int ov7692_sensor_release(void)
{
int rc = -EBADF;
mutex_lock(&ov7692_mut);
ov7692_power_down();
kfree(ov7692_ctrl);
ov7692_ctrl = NULL;
CDBG("ov7692_release completed\n");
mutex_unlock(&ov7692_mut);
return rc;
}
static int ov7692_sensor_probe(const struct msm_camera_sensor_info *info,
struct msm_sensor_ctrl *s)
{
int rc = 0;
rc = i2c_add_driver(&ov7692_i2c_driver);
if (rc < 0 || ov7692_client == NULL) {
rc = -ENOTSUPP;
goto probe_fail;
}
msm_camio_clk_rate_set(24000000);
rc = ov7692_probe_init_sensor(info);
if (rc < 0)
goto probe_fail;
s->s_init = ov7692_sensor_open_init;
s->s_release = ov7692_sensor_release;
s->s_config = ov7692_sensor_config;
s->s_camera_type = FRONT_CAMERA_2D;
s->s_mount_angle = 0;
return rc;
probe_fail:
CDBG("ov7692_sensor_probe: SENSOR PROBE FAILS!\n");
i2c_del_driver(&ov7692_i2c_driver);
return rc;
}
static int __ov7692_probe(struct platform_device *pdev)
{
return msm_camera_drv_start(pdev, ov7692_sensor_probe);
}
static struct platform_driver msm_camera_driver = {
.probe = __ov7692_probe,
.driver = {
.name = "msm_camera_ov7692",
.owner = THIS_MODULE,
},
};
static int __init ov7692_init(void)
{
return platform_driver_register(&msm_camera_driver);
}
module_init(ov7692_init);
MODULE_DESCRIPTION("OMNI VGA YUV sensor driver");
MODULE_LICENSE("GPL v2");