1011 lines
24 KiB
C
1011 lines
24 KiB
C
/*
|
|
* linux-3.10/drivers/media/platform/sunxi-vin/vin-cci/cci_helper.c
|
|
*
|
|
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
******************************************************************************
|
|
*
|
|
* cci_helper.c
|
|
*
|
|
* Hawkview ISP - cci_helper.c module
|
|
*
|
|
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
|
|
*
|
|
* Version Author Date Description
|
|
*
|
|
* 2.0 Yang Feng 2014/06/05 Second Version
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include "cci_helper.h"
|
|
#include "bsp_cci.h"
|
|
#include "../platform/platform_cfg.h"
|
|
#include "../modules/sensor/sensor_helper.h"
|
|
#include "../modules/sensor/camera.h"
|
|
|
|
#if defined(CONFIG_CCI_MODULE) || defined (CONFIG_CCI)
|
|
#define USE_SPECIFIC_CCI
|
|
#endif
|
|
|
|
int cci_dbg_en;
|
|
int cci_dbg_lv = 1;
|
|
#ifdef USE_SPECIFIC_CCI
|
|
#define cci_print(x, arg...) printk(KERN_INFO"[VIN_DEV_CCI]"x, ##arg)
|
|
#define cci_dbg(l, x, arg...) { \
|
|
if (cci_dbg_en && l <= cci_dbg_lv) \
|
|
printk(KERN_DEBUG"[VIN_DEV_CCI_DBG]"x, ##arg); \
|
|
}
|
|
#define cci_err(x, arg...) printk(KERN_ERR"[VIN_DEV_CCI_ERR]"x, ##arg)
|
|
#else
|
|
#define cci_print(x, arg...) printk(KERN_INFO"[VIN_DEV_I2C]"x, ##arg)
|
|
#define cci_dbg(l, x, arg...) { \
|
|
if (cci_dbg_en && l <= cci_dbg_lv) \
|
|
printk(KERN_DEBUG"[VIN_DEV_I2C_DBG]"x, ##arg);\
|
|
}
|
|
#define cci_err(x, arg...) printk(KERN_ERR"[VIN_DEV_I2C_ERR]"x, ##arg)
|
|
#endif
|
|
|
|
static void cci_device_release(struct device *dev)
|
|
{
|
|
return;
|
|
}
|
|
struct device cci_device_def = {
|
|
.release = cci_device_release,
|
|
};
|
|
|
|
#define SHOW_CCI_DEVICE_ATTR(name) \
|
|
static ssize_t cci_device_##name##_show(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
char *buf) \
|
|
{\
|
|
struct cci_driver *cci_drv = dev_get_drvdata(dev); \
|
|
cci_print("Get val = 0x%x !\n", cci_drv->name);\
|
|
return sprintf(buf, "0x%x\n", cci_drv->name); \
|
|
}
|
|
|
|
#define STORE_CCI_DEVICE_ATTR(name) \
|
|
static ssize_t cci_device_##name##_store(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
const char *buf,\
|
|
size_t count) \
|
|
{\
|
|
struct cci_driver *cci_drv = dev_get_drvdata(dev); \
|
|
unsigned int val;\
|
|
val = simple_strtoul(buf, NULL, 16);\
|
|
cci_drv->name = val;\
|
|
cci_print("Set val = 0x%x !\n", val);\
|
|
return count;\
|
|
}
|
|
|
|
SHOW_CCI_DEVICE_ATTR(addr_width)
|
|
SHOW_CCI_DEVICE_ATTR(data_width)
|
|
SHOW_CCI_DEVICE_ATTR(read_value)
|
|
SHOW_CCI_DEVICE_ATTR(read_flag)
|
|
STORE_CCI_DEVICE_ATTR(addr_width)
|
|
STORE_CCI_DEVICE_ATTR(data_width)
|
|
STORE_CCI_DEVICE_ATTR(read_flag)
|
|
|
|
static ssize_t cci_sys_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct cci_driver *cci_drv = dev_get_drvdata(dev);
|
|
cci_print("echo reg(16)|val(16) > cci_client,"
|
|
"eg: echo 30350011 > cci_client!\n");
|
|
cci_print("Note: current read_flag = %d\n", cci_drv->read_flag);
|
|
return sprintf(buf, "0x%x\n", cci_drv->read_flag);
|
|
}
|
|
|
|
static ssize_t cci_sys_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf,
|
|
size_t count)
|
|
{
|
|
static int val;
|
|
unsigned short reg, value;
|
|
struct cci_driver *cci_drv = dev_get_drvdata(dev);
|
|
struct v4l2_subdev *sd = cci_drv->sd;
|
|
|
|
val = simple_strtoul(buf, NULL, 16);
|
|
reg = (val >> 16) & 0xFFFF;
|
|
value = val & 0xFFFF;
|
|
if (0 == cci_drv->read_flag) {
|
|
cci_write(sd, (addr_type) reg, (addr_type) value);
|
|
cci_print("Write reg = 0x%x, write value = 0x%x\n", reg, value);
|
|
} else {
|
|
cci_read(sd, (addr_type) reg,
|
|
(addr_type *)&cci_drv->read_value);
|
|
cci_print("Read reg = 0x%x, read value = 0x%x\n", reg,
|
|
cci_drv->read_value);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static struct device_attribute cci_device_attrs[] = {
|
|
__ATTR(addr_width, S_IWUSR | S_IRUGO, cci_device_addr_width_show,
|
|
cci_device_addr_width_store),
|
|
__ATTR(data_width, S_IWUSR | S_IRUGO, cci_device_data_width_show,
|
|
cci_device_data_width_store),
|
|
__ATTR(read_value, S_IRUGO, cci_device_read_value_show, NULL),
|
|
__ATTR(read_flag, S_IWUSR | S_IRUGO, cci_device_read_flag_show,
|
|
cci_device_read_flag_store),
|
|
__ATTR(cci_client, S_IWUSR | S_IRUGO, cci_sys_show, cci_sys_store),
|
|
};
|
|
static int cci_sys_register(struct cci_driver *drv_data)
|
|
{
|
|
int i, ret;
|
|
drv_data->cci_device = cci_device_def;
|
|
dev_set_name(&drv_data->cci_device, drv_data->name);
|
|
if (device_register(&drv_data->cci_device))
|
|
cci_err("error device_register()\n");
|
|
dev_set_drvdata(&drv_data->cci_device, drv_data);
|
|
/* sysfs entries */
|
|
for (i = 0; i < ARRAY_SIZE(cci_device_attrs); i++) {
|
|
ret =
|
|
device_create_file(&drv_data->cci_device,
|
|
&cci_device_attrs[i]);
|
|
if (ret) {
|
|
cci_err("device_create_file error\n");
|
|
device_remove_file(&drv_data->cci_device,
|
|
&drv_data->dev_attr_cci);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int cci_sys_unregister(struct cci_driver *drv_data)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(cci_device_attrs); i++)
|
|
device_remove_file(&drv_data->cci_device, &cci_device_attrs[i]);
|
|
device_unregister(&drv_data->cci_device);
|
|
return 0;
|
|
}
|
|
|
|
static int sensor_registered(struct v4l2_subdev *sd)
|
|
{
|
|
int ret;
|
|
struct sensor_info *info = to_state(sd);
|
|
|
|
mutex_lock(&info->lock);
|
|
|
|
v4l2_subdev_call(sd, core, s_power, PWR_ON);
|
|
ret = v4l2_subdev_call(sd, core, init, 0);
|
|
v4l2_subdev_call(sd, core, s_power, PWR_OFF);
|
|
mutex_unlock(&info->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct v4l2_subdev_internal_ops sensor_internal_ops = {
|
|
.registered = sensor_registered,
|
|
};
|
|
|
|
static LIST_HEAD(cci_drv_list);
|
|
static int cci_dev_num_id;
|
|
|
|
static void cci_subdev_init(struct v4l2_subdev *sd, struct cci_driver *drv_data,
|
|
const struct v4l2_subdev_ops *ops)
|
|
{
|
|
cci_dbg(0, "cci_subdev_init!\n");
|
|
v4l2_subdev_init(sd, ops);
|
|
/* initialize name */
|
|
snprintf(sd->name, sizeof(sd->name), "%s", drv_data->name);
|
|
drv_data->sd = sd;
|
|
drv_data->id = cci_dev_num_id;
|
|
drv_data->is_registerd = 1;
|
|
v4l2_set_subdevdata(sd, drv_data);
|
|
|
|
list_add(&drv_data->cci_list, &cci_drv_list);
|
|
cci_dev_num_id++;
|
|
cci_dbg(0, "cci_dev_num_id = %d, name = %s, cci_subdev_init,sd = %p!\n",
|
|
cci_dev_num_id, drv_data->name, drv_data->sd);
|
|
}
|
|
|
|
static void cci_subdev_remove(struct cci_driver *cci_drv_p)
|
|
{
|
|
if (cci_drv_p->is_registerd == 0) {
|
|
cci_err("CCI subdev exit: this device is not registerd!\n");
|
|
return;
|
|
} else {
|
|
cci_drv_p->is_matched = 0;
|
|
cci_drv_p->is_registerd = 0;
|
|
if (cci_drv_p->sd == NULL) {
|
|
cci_err("CCI subdev exit: cci_drv_p->sd is NULL!\n");
|
|
} else {
|
|
v4l2_set_subdevdata(cci_drv_p->sd, NULL);
|
|
}
|
|
list_del(&cci_drv_p->cci_list);
|
|
cci_dev_num_id--;
|
|
}
|
|
}
|
|
|
|
struct v4l2_subdev *cci_bus_match(char *name, unsigned short cci_id,
|
|
unsigned short cci_saddr)
|
|
{
|
|
struct cci_driver *cci_drv;
|
|
list_for_each_entry(cci_drv, &cci_drv_list, cci_list) {
|
|
cci_dbg(0, "try cci dev name =%s!\n", cci_drv->name);
|
|
if (cci_drv->is_registerd == 1 && cci_drv->is_matched == 0) {
|
|
cci_dbg(0, "cci_drv->name = %s, check name = %s\n",
|
|
cci_drv->name, name);
|
|
if (!strcmp(cci_drv->name, name)) {
|
|
cci_dbg(0, "cci_bus_match name matched!\n");
|
|
cci_drv->cci_id = cci_id;
|
|
cci_drv->cci_saddr = cci_saddr;
|
|
cci_drv->is_matched = 1;
|
|
cci_dbg(0, "id = %d, saddr = %x, name = %s\n",
|
|
cci_drv->cci_id,
|
|
cci_drv->cci_saddr, cci_drv->name);
|
|
return cci_drv->sd;
|
|
}
|
|
}
|
|
cci_dbg(0, "try cci dev name =%s failed!\n", cci_drv->name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_bus_match);
|
|
void cci_bus_match_cancel(struct cci_driver *cci_drv_p)
|
|
{
|
|
if (cci_drv_p->is_registerd == 0) {
|
|
cci_err("This device is not registerd!\n");
|
|
return;
|
|
} else {
|
|
if (cci_drv_p->is_matched == 0) {
|
|
cci_err("This device has not been matched!\n");
|
|
return;
|
|
} else {
|
|
cci_drv_p->is_matched = 0;
|
|
cci_drv_p->cci_id = 0;
|
|
cci_drv_p->cci_saddr = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_bus_match_cancel);
|
|
|
|
void csi_cci_init_helper(unsigned int sel)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
cci_s_power(sel, 1);
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(csi_cci_init_helper);
|
|
|
|
void csi_cci_exit_helper(unsigned int sel)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
cci_s_power(sel, 0);
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(csi_cci_exit_helper);
|
|
|
|
void cci_lock(struct v4l2_subdev *sd)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
|
|
#else
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
i2c_lock_adapter(client->adapter);
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_lock);
|
|
|
|
void cci_unlock(struct v4l2_subdev *sd)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
|
|
#else
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
i2c_unlock_adapter(client->adapter);
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_unlock);
|
|
|
|
int cci_dev_init_helper(struct i2c_driver *sensor_driver)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
cci_dbg(0, "cci init device\n");
|
|
return sensor_driver->probe(NULL, NULL);
|
|
#else
|
|
return i2c_add_driver(sensor_driver);
|
|
#endif
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_dev_init_helper);
|
|
void cci_dev_exit_helper(struct i2c_driver *sensor_driver)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
cci_dbg(0, "cci exit device\n");
|
|
sensor_driver->remove(NULL);
|
|
#else
|
|
i2c_del_driver(sensor_driver);
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_dev_exit_helper);
|
|
|
|
static int cci_media_entity_init_helper(struct v4l2_subdev *sd,
|
|
struct cci_driver *cci_drv)
|
|
{
|
|
struct sensor_info *si;
|
|
|
|
switch (cci_drv->type) {
|
|
case CCI_TYPE_SENSOR:
|
|
si = container_of(sd, struct sensor_info, sd);
|
|
BUG_ON(si->magic_num && (si->magic_num != SENSOR_MAGIC_NUMBER));
|
|
si->sensor_pads[SENSOR_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
|
|
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
|
return media_entity_init(&sd->entity, SENSOR_PAD_NUM, si->sensor_pads, 0);
|
|
case CCI_TYPE_ACT:
|
|
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_LENS;
|
|
return media_entity_init(&sd->entity, 0, NULL, 0);
|
|
case CCI_TYPE_FLASH:
|
|
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
|
|
return media_entity_init(&sd->entity, 0, NULL, 0);
|
|
default:
|
|
return media_entity_init(&sd->entity, 0, NULL, 0);
|
|
}
|
|
}
|
|
|
|
int cci_dev_probe_helper(struct v4l2_subdev *sd, struct i2c_client *client,
|
|
const struct v4l2_subdev_ops *sensor_ops,
|
|
struct cci_driver *cci_drv)
|
|
{
|
|
if (client) {
|
|
v4l2_i2c_subdev_init(sd, client, sensor_ops);
|
|
/*change sd name to sensor driver name*/
|
|
snprintf(sd->name, sizeof(sd->name), "%s", cci_drv->name);
|
|
cci_drv->sd = sd;
|
|
v4l2_set_subdev_hostdata(sd, cci_drv);
|
|
} else {
|
|
cci_subdev_init(sd, cci_drv, sensor_ops);
|
|
}
|
|
sd->grp_id = VIN_GRP_ID_SENSOR;
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
sd->internal_ops = &sensor_internal_ops;
|
|
cci_sys_register(cci_drv);
|
|
cci_media_entity_init_helper(sd, cci_drv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_dev_probe_helper);
|
|
|
|
struct v4l2_subdev *cci_dev_remove_helper(struct i2c_client *client,
|
|
struct cci_driver *cci_drv)
|
|
{
|
|
struct v4l2_subdev *sd;
|
|
if (client) {
|
|
sd = i2c_get_clientdata(client);
|
|
cci_dbg(0, "sd = %p\n", sd);
|
|
v4l2_set_subdev_hostdata(sd, NULL);
|
|
cci_dbg(0, "sd = %p\n", sd);
|
|
} else {
|
|
sd = cci_drv->sd;
|
|
cci_dbg(0, "sd = %p, cci_drv = %p\n", sd, cci_drv);
|
|
cci_subdev_remove(cci_drv);
|
|
cci_dbg(0, "sd = %p\n", sd);
|
|
}
|
|
v4l2_device_unregister_subdev(sd);
|
|
cci_sys_unregister(cci_drv);
|
|
return sd;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_dev_remove_helper);
|
|
|
|
/*
|
|
* On most platforms, we'd rather do straight i2c I/O.
|
|
*/
|
|
|
|
int cci_read_a8_d8(struct v4l2_subdev *sd, unsigned char addr,
|
|
unsigned char *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_rd_8_8(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
unsigned char data[2];
|
|
struct i2c_msg msg[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = addr;
|
|
data[1] = 0xee;
|
|
/*
|
|
* Send out the register address...
|
|
*/
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 1;
|
|
msg[0].buf = &data[0];
|
|
/*
|
|
* ...then read back the result.
|
|
*/
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = 1;
|
|
msg[1].buf = &data[1];
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
if (ret >= 0) {
|
|
*value = data[1];
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%2x, value = 0x%2x\n ",
|
|
__func__, client->addr, addr, *value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read_a8_d8);
|
|
|
|
int cci_write_a8_d8(struct v4l2_subdev *sd, unsigned char addr,
|
|
unsigned char value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_8_8(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
struct i2c_msg msg;
|
|
unsigned char data[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = addr;
|
|
data[1] = value;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 2;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%2x, value = 0x%2x\n ",
|
|
__func__, client->addr, addr, value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a8_d8);
|
|
|
|
int cci_read_a8_d16(struct v4l2_subdev *sd, unsigned char addr,
|
|
unsigned short *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_rd_8_16(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
unsigned char data[3];
|
|
struct i2c_msg msg[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = addr;
|
|
data[1] = 0xee;
|
|
data[2] = 0xee;
|
|
/*
|
|
* Send out the register address...
|
|
*/
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 1;
|
|
msg[0].buf = &data[0];
|
|
/*
|
|
* ...then read back the result.
|
|
*/
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = 2;
|
|
msg[1].buf = &data[1];
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
if (ret >= 0) {
|
|
*value = data[1] * 256 + data[2];
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%2x, value = 0x%4x\n ",
|
|
__func__, client->addr, addr, *value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read_a8_d16);
|
|
|
|
int cci_write_a8_d16(struct v4l2_subdev *sd, unsigned char addr,
|
|
unsigned short value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_8_16(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
struct i2c_msg msg;
|
|
unsigned char data[3];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = addr;
|
|
data[1] = (value & 0xff00) >> 8;
|
|
data[2] = (value & 0x00ff);
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 3;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%2x, value = 0x%4x\n ",
|
|
__func__, client->addr, addr, value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a8_d16);
|
|
int cci_read_a16_d8(struct v4l2_subdev *sd, unsigned short addr,
|
|
unsigned char *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_rd_16_8(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
int ret;
|
|
unsigned char data[3];
|
|
struct i2c_msg msg[2];
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
data[0] = (addr & 0xff00) >> 8;
|
|
data[1] = (addr & 0x00ff);
|
|
data[2] = 0xee;
|
|
/*
|
|
* Send out the register address...
|
|
*/
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 2;
|
|
msg[0].buf = &data[0];
|
|
/*
|
|
* ...then read back the result.
|
|
*/
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = 1;
|
|
msg[1].buf = &data[2];
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
if (ret >= 0) {
|
|
*value = data[2];
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%4x, value = 0x%2x\n ",
|
|
__func__, client->addr, addr, *value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read_a16_d8);
|
|
|
|
int cci_transfer_helper(unsigned short reg, unsigned char data,
|
|
unsigned char slv)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int cci_write_a16_d8(struct v4l2_subdev *sd, unsigned short addr,
|
|
unsigned char value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_16_8(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
int ret = 0;
|
|
struct i2c_msg msg;
|
|
unsigned char data[3];
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = (addr & 0xff00) >> 8;
|
|
data[1] = (addr & 0x00ff);
|
|
data[2] = value;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 3;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%4x, value = 0x%2x\n ",
|
|
__func__, client->addr, addr, value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a16_d8);
|
|
|
|
int cci_read_a16_d16(struct v4l2_subdev *sd, unsigned short addr,
|
|
unsigned short *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_rd_16_16(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
unsigned char data[4];
|
|
struct i2c_msg msg[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = (addr & 0xff00) >> 8;
|
|
data[1] = (addr & 0x00ff);
|
|
data[2] = 0xee;
|
|
data[3] = 0xee;
|
|
/*
|
|
* Send out the register address...
|
|
*/
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 2;
|
|
msg[0].buf = &data[0];
|
|
/*
|
|
* ...then read back the result.
|
|
*/
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = 2;
|
|
msg[1].buf = &data[2];
|
|
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
if (ret >= 0) {
|
|
*value = data[2] * 256 + data[3];
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%4x, value = 0x%4x\n ",
|
|
__func__, client->addr, addr, *value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read_a16_d16);
|
|
|
|
int cci_write_a16_d16(struct v4l2_subdev *sd, unsigned short addr,
|
|
unsigned short value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_16_16(cci_drv->cci_id, addr, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
struct i2c_msg msg;
|
|
unsigned char data[4];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = (addr & 0xff00) >> 8;
|
|
data[1] = (addr & 0x00ff);
|
|
data[2] = (value & 0xff00) >> 8;
|
|
data[3] = (value & 0x00ff);
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 4;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, addr = 0x%4x, value = 0x%4x\n ",
|
|
__func__, client->addr, addr, value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a16_d16);
|
|
|
|
int cci_read_a0_d16(struct v4l2_subdev *sd, unsigned short *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_rd_0_16(cci_drv->cci_id, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
struct i2c_msg msg;
|
|
unsigned char data[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = 0xee;
|
|
data[1] = 0xee;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 1;
|
|
msg.len = 2;
|
|
msg.buf = &data[0];
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
*value = data[0] * 256 + data[1];
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, value = 0x%4x\n ", __func__,
|
|
client->addr, *value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read_a0_d16);
|
|
|
|
int cci_write_a0_d16(struct v4l2_subdev *sd, unsigned short value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_0_16(cci_drv->cci_id, value, cci_drv->cci_saddr);
|
|
#else
|
|
|
|
struct i2c_msg msg;
|
|
unsigned char data[2];
|
|
int ret;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
data[0] = (value & 0xff00) >> 8;
|
|
data[1] = (value & 0x00ff);
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 2;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret >= 0) {
|
|
ret = 0;
|
|
} else {
|
|
cci_err("at %s, slave = 0x%x, value = 0x%4x\n ", __func__,
|
|
client->addr, value);
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a0_d16);
|
|
|
|
int cci_write_a16_d8_continuous_helper(struct v4l2_subdev *sd,
|
|
unsigned short addr, unsigned char *vals,
|
|
uint size)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
return cci_wr_a16_d8_continuous(cci_drv->cci_id, addr, vals,
|
|
cci_drv->cci_saddr, size);
|
|
#else
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct i2c_msg msg;
|
|
unsigned char data[2 + 32];
|
|
unsigned char *p = vals;
|
|
int ret, i, len;
|
|
while (size > 0) {
|
|
len = size > 32 ? 32 : size;
|
|
data[0] = (addr & 0xff00) >> 8;
|
|
data[1] = (addr & 0x00ff);
|
|
|
|
for (i = 2; i < 2 + len; i++)
|
|
data[i] = *p++;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = 2 + len;
|
|
msg.buf = data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
|
|
if (ret > 0) {
|
|
ret = 0;
|
|
} else if (ret < 0) {
|
|
cci_err("sensor_write error!\n");
|
|
break;
|
|
}
|
|
addr += len;
|
|
size -= len;
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write_a16_d8_continuous_helper);
|
|
|
|
int cci_write(struct v4l2_subdev *sd, addr_type addr, data_type value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
#else
|
|
struct cci_driver *cci_drv = v4l2_get_subdev_hostdata(sd);
|
|
#endif
|
|
int addr_width = cci_drv->addr_width;
|
|
int data_width = cci_drv->data_width;
|
|
|
|
if (8 == addr_width && 8 == data_width) {
|
|
return cci_write_a8_d8(sd, (unsigned char)addr,
|
|
(unsigned char)value);
|
|
} else if (8 == addr_width && 16 == data_width) {
|
|
return cci_write_a8_d16(sd, (unsigned char)addr, value);
|
|
} else if (16 == addr_width && 8 == data_width) {
|
|
return cci_write_a16_d8(sd, addr, (unsigned char)value);
|
|
} else if (16 == addr_width && 16 == data_width) {
|
|
return cci_write_a16_d16(sd, addr, value);
|
|
} else if (0 == addr_width && 16 == data_width) {
|
|
return cci_write_a0_d16(sd, value);
|
|
} else {
|
|
cci_err("at %s error, addr_width = %d , data_width = %d!\n ",\
|
|
__func__, addr_width, data_width);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_write);
|
|
|
|
int cci_read(struct v4l2_subdev *sd, addr_type addr, data_type *value)
|
|
{
|
|
#ifdef USE_SPECIFIC_CCI
|
|
struct cci_driver *cci_drv = v4l2_get_subdevdata(sd);
|
|
#else
|
|
struct cci_driver *cci_drv = v4l2_get_subdev_hostdata(sd);
|
|
#endif
|
|
int addr_width = cci_drv->addr_width;
|
|
int data_width = cci_drv->data_width;
|
|
*value = 0;
|
|
if (8 == addr_width && 8 == data_width) {
|
|
return cci_read_a8_d8(sd, (unsigned char)addr,
|
|
(unsigned char *)value);
|
|
} else if (8 == addr_width && 16 == data_width) {
|
|
return cci_read_a8_d16(sd, (unsigned char)addr, value);
|
|
} else if (16 == addr_width && 8 == data_width) {
|
|
return cci_read_a16_d8(sd, addr, (unsigned char *)value);
|
|
} else if (16 == addr_width && 16 == data_width) {
|
|
return cci_read_a16_d16(sd, addr, value);
|
|
} else if (0 == addr_width && 16 == data_width) {
|
|
return cci_read_a0_d16(sd, value);
|
|
} else {
|
|
cci_err("%s error! addr_width = %d , data_width = %d!\n ",\
|
|
__func__, addr_width, data_width);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cci_read);
|
|
|
|
/*
|
|
* On most platforms, we'd rather do straight i2c I/O.
|
|
*/
|
|
int sensor_read(struct v4l2_subdev *sd, addr_type reg, data_type *value)
|
|
{
|
|
int ret = 0, cnt = 0;
|
|
|
|
ret = cci_read(sd, reg, value);
|
|
while ((ret != 0) && (cnt < 2)) {
|
|
ret = cci_read(sd, reg, value);
|
|
cnt++;
|
|
}
|
|
if (cnt > 0)
|
|
printk("%s sensor read retry = %d\n", sd->name, cnt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sensor_read);
|
|
|
|
int sensor_write(struct v4l2_subdev *sd, addr_type reg, data_type value)
|
|
{
|
|
int ret = 0, cnt = 0;
|
|
|
|
ret = cci_write(sd, reg, value);
|
|
while ((ret != 0) && (cnt < 2)) {
|
|
ret = cci_write(sd, reg, value);
|
|
cnt++;
|
|
}
|
|
if (cnt > 0)
|
|
printk("%s sensor write retry = %d\n", sd->name, cnt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sensor_write);
|
|
|
|
/*
|
|
* Write a list of register settings;
|
|
*/
|
|
int sensor_write_array(struct v4l2_subdev *sd, struct regval_list *regs,
|
|
int array_size)
|
|
{
|
|
int ret = 0, i = 0;
|
|
|
|
if (!regs)
|
|
return -EINVAL;
|
|
|
|
while (i < array_size) {
|
|
if (regs->addr == REG_DLY) {
|
|
msleep(regs->data);
|
|
} else {
|
|
ret = sensor_write(sd, regs->addr, regs->data);
|
|
if (ret < 0)
|
|
printk("%s sensor write array error!\n",\
|
|
sd->name);
|
|
}
|
|
i++;
|
|
regs++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sensor_write_array);
|
|
|
|
int cci_set_saddr(struct v4l2_subdev *sd, unsigned short value)
|
|
{
|
|
/*struct cci_driver *cci_drv;*/
|
|
struct i2c_client *cci_drv;
|
|
if (sd == NULL)
|
|
return -1;
|
|
cci_drv = v4l2_get_subdevdata(sd);
|
|
if (cci_drv == NULL) {
|
|
sensor_dbg("cci_drv is NULL\n");
|
|
return -1;
|
|
}
|
|
/*for cci address*/
|
|
/*cci_drv->cci_saddr = value;*/
|
|
cci_drv->addr = value;
|
|
/*sensor_dbg("cci saddr=0x%x\n", cci_drv->cci_saddr);*/
|
|
/*sensor_dbg("addr=0x%x\n", cci_drv->addr);*/
|
|
return 0;
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(cci_set_saddr);
|
|
|
|
int cci_get_saddr(struct v4l2_subdev *sd, unsigned short *value)
|
|
{
|
|
struct cci_driver *cci_drv;
|
|
if (sd == NULL)
|
|
return -1;
|
|
cci_drv = v4l2_get_subdevdata(sd);
|
|
if (cci_drv == NULL)
|
|
return -1;
|
|
if (value == NULL)
|
|
return -1;
|
|
*value = cci_drv->cci_saddr;
|
|
return 0;
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(cci_get_saddr);
|