/*
* sp0821.c sp0821 yuv module
*
* Author: Bruce <sunchengwei@longcheer.com>
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/gameport.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/kthread.h>
#include <linux/regulator/consumer.h>
#include "sp_sp0821_yuv.h"
#include "../../../aw37004/aw37004.h"
#include <soc/oplus/system/oplus_project.h>
#include <linux/videodev2.h>
#include <linux/cdev.h>
#include <linux/atomic.h>
#include <linux/types.h>
extern void ISP_MCLK3_EN (bool En);
extern int aw37004_camera_power_up(int out_iotype, unsigned int out_val);
#define kal_uint16 unsigned short
#define kal_uint32 unsigned int
/*****************************************************************
* sp0821 marco
******************************************************************/
#define SP0821_DRIVER_VERSION "V2.0"
#define SP0821_PRODUCT_NUM 4
#define SP0821_PRODUCT_NAME_LEN 8
#define SP0821_SENSOR_ID 0x3a6c
#define SP0821_MCLK_ON "sp0821_mclk_on"
#define SP0821_MCLK_OFF "sp0821_mclk_off"
/*****************************************************************
* sp0821 global global variable
******************************************************************/
static unsigned char read_reg_id = 0;
static unsigned char read_reg_value = 0;
static int read_reg_flag = 0;
static int driver_flag = 0;
struct sp0821 *g_sp0821 = NULL;
/**********************************************************
* i2c write and read
**********************************************************/
static void sp0821_i2c_write(struct sp0821 *sp0821, int address, int data)
{
u8 i2c_buf[8];
struct i2c_client *client = sp0821->i2c_client;
struct i2c_msg msg[1];
msg[0].flags = !I2C_M_RD;
msg[0].addr = client->addr;
msg[0].len = 3;
msg[0].buf = i2c_buf;
i2c_buf[0] = (address & 0xff00)>>8;
i2c_buf[1] = (address & 0xff);
i2c_buf[2] = data;
i2c_transfer(client->adapter, msg, 1);
//printk("write sp0821 addr: 0x%4X val:0x%4X\n", address, data);
}
static unsigned char sp0821_i2c_read(struct sp0821 *sp0821, int address)
{
unsigned char rxdata = 0x00;
unsigned char i2c_buf[4];
int ret = 0;
int retry = 2;
u8 i2c_addr[2];
struct i2c_client *client = sp0821->i2c_client;
struct i2c_msg msgs[2];
i2c_addr[0] = (address & 0xff00)>>8;
i2c_addr[1] = (address & 0xff);
msgs[0].flags = 0;
msgs[0].addr = (client->addr);
msgs[0].len = 2;
msgs[0].buf = i2c_addr;
msgs[1].flags = I2C_M_RD;
msgs[1].addr = (client->addr);
msgs[1].len = 1;
msgs[1].buf = i2c_buf;
while (retry > 0)
{
ret = i2c_transfer(client->adapter, msgs, 2);
//qvga_dev_err(&client->dev, "%s: read step1 ret:%d msgs[1].addr=%x\n", __func__, ret, msgs[1].addr);
if (retry >0)
{
mdelay(20);
if (ret == 0)
{
return 0;
}
}
retry--;
mdelay(2);
}
rxdata = i2c_buf[0];
return rxdata;
}
static struct sp0821 *sp0821_malloc_init(struct i2c_client *client)
{
struct sp0821 *sp0821 =
devm_kzalloc(&client->dev, sizeof(struct sp0821), GFP_KERNEL);
if (sp0821 == NULL) {
qvga_dev_err(&client->dev, "%s: devm_kzalloc failed.\n", __func__);
return NULL;
}
sp0821->i2c_client = client;
pr_info("%s enter , client_addr = 0x%02x\n", __func__,
sp0821->i2c_client->addr);
return sp0821;
}
#if 1
void sp0821_Init(struct sp0821 *sp0821)
{
/*SYS MCLK=24MHZ*/
sp0821_i2c_write(sp0821, 0x0103,0x01);
sp0821_i2c_write(sp0821, 0x0100,0x00);
sp0821_i2c_write(sp0821, 0x309b,0xf0);
sp0821_i2c_write(sp0821, 0x30b0,0x0a);
sp0821_i2c_write(sp0821, 0x30b8,0x21);
sp0821_i2c_write(sp0821, 0x320c,0x01);
sp0821_i2c_write(sp0821, 0x320d,0x6a);
sp0821_i2c_write(sp0821, 0x320e,0x01);
sp0821_i2c_write(sp0821, 0x320f,0xba);
sp0821_i2c_write(sp0821, 0x3301,0x04);
sp0821_i2c_write(sp0821, 0x3304,0x0c);
sp0821_i2c_write(sp0821, 0x3305,0x00);
sp0821_i2c_write(sp0821, 0x3306,0x10);
sp0821_i2c_write(sp0821, 0x3307,0x02);
sp0821_i2c_write(sp0821, 0x3308,0x04);
sp0821_i2c_write(sp0821, 0x330a,0x00);
sp0821_i2c_write(sp0821, 0x330b,0x30);
sp0821_i2c_write(sp0821, 0x330e,0x01);
sp0821_i2c_write(sp0821, 0x330f,0x01);
sp0821_i2c_write(sp0821, 0x3310,0x01);
sp0821_i2c_write(sp0821, 0x331e,0x09);
sp0821_i2c_write(sp0821, 0x3333,0x10);
sp0821_i2c_write(sp0821, 0x3334,0x40);
sp0821_i2c_write(sp0821, 0x334c,0x01);
sp0821_i2c_write(sp0821, 0x33b3,0x3e);
sp0821_i2c_write(sp0821, 0x349f,0x02);
sp0821_i2c_write(sp0821, 0x34a6,0x01);
sp0821_i2c_write(sp0821, 0x34a7,0x07);
sp0821_i2c_write(sp0821, 0x34a8,0x3a);
sp0821_i2c_write(sp0821, 0x34a9,0x38);
sp0821_i2c_write(sp0821, 0x34e9,0x38);
sp0821_i2c_write(sp0821, 0x34f8,0x07);
sp0821_i2c_write(sp0821, 0x3630,0x65);
sp0821_i2c_write(sp0821, 0x3637,0x47);
sp0821_i2c_write(sp0821, 0x363a,0xe0);
sp0821_i2c_write(sp0821, 0x3670,0x03);
sp0821_i2c_write(sp0821, 0x3674,0x75);
sp0821_i2c_write(sp0821, 0x3675,0x65);
sp0821_i2c_write(sp0821, 0x3676,0x65);
sp0821_i2c_write(sp0821, 0x367c,0x01);
sp0821_i2c_write(sp0821, 0x367d,0x03);
sp0821_i2c_write(sp0821, 0x3690,0xe0);
sp0821_i2c_write(sp0821, 0x3691,0xe1);
sp0821_i2c_write(sp0821, 0x3692,0xe1);
sp0821_i2c_write(sp0821, 0x3693,0xe1);
sp0821_i2c_write(sp0821, 0x3694,0x03);
sp0821_i2c_write(sp0821, 0x3695,0x07);
sp0821_i2c_write(sp0821, 0x3696,0x07);
sp0821_i2c_write(sp0821, 0x37f9,0x29);
sp0821_i2c_write(sp0821, 0x3900,0x91);
sp0821_i2c_write(sp0821, 0x3904,0x0f);
sp0821_i2c_write(sp0821, 0x3908,0x00);
sp0821_i2c_write(sp0821, 0x391b,0x07);
sp0821_i2c_write(sp0821, 0x391c,0x0a);
sp0821_i2c_write(sp0821, 0x391d,0x15);
sp0821_i2c_write(sp0821, 0x391e,0x28);
sp0821_i2c_write(sp0821, 0x391f,0x41);
sp0821_i2c_write(sp0821, 0x3948,0x00);//blc
sp0821_i2c_write(sp0821, 0x4509,0x10);
sp0821_i2c_write(sp0821, 0x470b,0x0a);
sp0821_i2c_write(sp0821, 0x470d,0x06);
sp0821_i2c_write(sp0821, 0x5000,0xc2);
sp0821_i2c_write(sp0821, 0x5001,0x01);
sp0821_i2c_write(sp0821, 0x5170,0x2c);
sp0821_i2c_write(sp0821, 0x5172,0xc1);
sp0821_i2c_write(sp0821, 0x518b,0x00);//again
sp0821_i2c_write(sp0821, 0x518c,0x20);
sp0821_i2c_write(sp0821, 0x518d,0x01);//shutter
sp0821_i2c_write(sp0821, 0x518e,0x7c);
sp0821_i2c_write(sp0821, 0x518f,0x00);
sp0821_i2c_write(sp0821, 0x519e,0x10);
sp0821_i2c_write(sp0821, 0x300a,0x00);//SIP input
sp0821_i2c_write(sp0821, 0x0100,0x01);
/*shutter gain must write after stream on */
sp0821_i2c_write(sp0821, 0x518b,0x03);//again=4x
sp0821_i2c_write(sp0821, 0x518c,0x20);
sp0821_i2c_write(sp0821, 0x518d,0x01);//shutter=20ms
sp0821_i2c_write(sp0821, 0x518e,0xb0);
sp0821_i2c_write(sp0821, 0x518f,0x00);
sp0821_i2c_write(sp0821, 0x519e,0x10);
} /* sensor_init */
#endif
int sp0821_GetSensorID(struct sp0821 *sp0821)
{
int retry = 2;
unsigned char reg_data = 0x00;
//check if sensor ID correct
do {
reg_data = sp0821_i2c_read(sp0821, 0x3107)<<8|sp0821_i2c_read(sp0821, 0x3108);
qvga_dev_err(sp0821->dev, "drv-%s: Read MSB Sensor ID = 0x%02x\n", __func__, reg_data);
// if (reg_data == SP0821_SENSOR_ID) {
if (1) {
qvga_dev_err(sp0821->dev, "drv-%s: Read Sensor ID sucess = 0x%02x\n", __func__, reg_data);
driver_flag = 1;
return 0;
} else {
qvga_dev_err(sp0821->dev, "rv-%s: Read Sensor ID Fail = 0x%02x\n", __func__, reg_data);
driver_flag = 0;
}
mdelay(10);
retry--;
} while (retry > 0);
return -1;
}
static void sp0821_vcam_control(struct sp0821 *sp0821, bool flag)
{
// struct regulator *vcama;
struct regulator *vcamio;
struct regulator *vcamd;
int ret;
int ret1;
qvga_dev_info(sp0821->dev, "%s enter\n", __func__);
vcamd = regulator_get(sp0821->dev,"vcamd");
if (IS_ERR(vcamd)) {
qvga_dev_err(sp0821->dev, "%s get regulator vcamd failed\n", __func__);
regulator_put(vcamd);
return;
} else {
qvga_dev_err(sp0821->dev, "%s get regulator vcamd success\n", __func__);
}
if (flag) {
regulator_set_voltage(vcamd, 1200000, 1200000);
ret = regulator_enable(vcamd);
} else {
regulator_disable(vcamd);
}
// vcama = regulator_get(sp0821->dev,"vcama");
// if (IS_ERR(vcama)) {
// qvga_dev_err(sp0821->dev, "%s get regulator vcama failed\n", __func__);
// regulator_put(vcama);
// return;
// }
// if (flag) {
// regulator_set_voltage(vcama, 2800000, 2800000);
// ret = regulator_enable(vcama);
// } else {
// regulator_disable(vcama);
// }
if (flag) {
ret1 = aw37004_camera_power_up(2, 2800);
if (ret1 == 0) {
qvga_dev_err(sp0821->dev, "%s get regulator vcama success\n", __func__);
} else {
qvga_dev_err(sp0821->dev, "%s get regulator vcama failed\n", __func__);
}
}
vcamio = regulator_get(sp0821->dev,"vcamio");
if (IS_ERR(vcamio)) {
qvga_dev_err(sp0821->dev, "%s get regulator vcamio failed\n", __func__);
regulator_put(vcamio);
return;
} else {
qvga_dev_err(sp0821->dev, "%s get regulator vcamio success\n", __func__);
}
if (flag) {
regulator_set_voltage(vcamio, 1800000, 1800000);
ret = regulator_enable(vcamio);
} else {
regulator_disable(vcamio);
}
return;
}
static void sp0821_hw_on_reset(struct sp0821 *sp0821)
{
qvga_dev_info(sp0821->dev, "%s enter\n", __func__);
if (gpio_is_valid(sp0821->reset_gpio)) {
gpio_set_value_cansleep(sp0821->reset_gpio, 1);
}
}
static void sp0821_hw_on_reset1(struct sp0821 *sp0821)
{
qvga_dev_info(sp0821->dev, "%s enter\n", __func__);
if (gpio_is_valid(sp0821->reset_gpio1)) {
gpio_set_value_cansleep(sp0821->reset_gpio1, 1);
}
}
static void sp0821_hw_off_reset(struct sp0821 *sp0821)
{
qvga_dev_info(sp0821->dev, "%s enter\n", __func__);
if (gpio_is_valid(sp0821->reset_gpio)) {
gpio_set_value_cansleep(sp0821->reset_gpio, 0);
udelay(50);
gpio_set_value_cansleep(sp0821->reset_gpio, 1);
udelay(50);
gpio_set_value_cansleep(sp0821->reset_gpio, 0);
}
}
static void sp0821_hw_off_reset1(struct sp0821 *sp0821)
{
qvga_dev_info(sp0821->dev, "%s enter\n", __func__);
if (gpio_is_valid(sp0821->reset_gpio1)) {
gpio_set_value_cansleep(sp0821->reset_gpio1, 0);
}
}
static void sp0821_hw_on(struct sp0821 *sp0821)
{
sp0821_hw_on_reset1(sp0821);
sp0821_hw_on_reset(sp0821);
sp0821_Init(sp0821);
sp0821->hwen_flag = 1;
}
static void sp0821_hw_off(struct sp0821 *sp0821)
{
sp0821_hw_off_reset(sp0821);
sp0821->hwen_flag = 0;
}
static ssize_t sp0821_get_reg(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
if (read_reg_flag) {
len += snprintf(buf + len, PAGE_SIZE - len, "The reg 0x%02X value is 0x%02X\n",
read_reg_id, read_reg_value);
read_reg_flag = 0;
read_reg_id = 0;
read_reg_value = 0;
} else {
len += snprintf(buf + len, PAGE_SIZE - len, "Please echo reg id into reg\n");
}
return len;
}
static ssize_t sp0821_set_reg(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
unsigned int databuf[2] = { 0 };
unsigned char reg_data = 0x00;
if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) {
sp0821_i2c_write(g_sp0821, databuf[0], databuf[1]);
}
else if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 1) {
reg_data = sp0821_i2c_read(g_sp0821, databuf[0]);
read_reg_id = databuf[0];
read_reg_value = reg_data;
read_reg_flag = 1;
}
return len;
}
static ssize_t sp0821_get_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
if (driver_flag) {
len += snprintf(buf + len, PAGE_SIZE - len, "%s\n",
"sp_sp0821_yuv");
} else {
len += snprintf(buf + len, PAGE_SIZE - len, "%s\n",
"none");
}
return len;
}
static ssize_t sp0821_get_light(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
unsigned char reg_data1 = 0x00;
//unsigned char reg_data2 = 0x00;
u16 light = 0;
reg_data1 = sp0821_i2c_read(g_sp0821, 0x5160);
//reg_data2 = sp0821_i2c_read(g_sp0821, 0x516b);
//light = (reg_data1<<8) + reg_data2;
light = reg_data1;
qvga_dev_err(g_sp0821->dev, "%s: sp0821 light=%d, %d\n", __func__, light, reg_data1);
len += snprintf(buf + len, PAGE_SIZE - len, "%d\n",
light);
return len;
}
static ssize_t sp0821_set_light(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
ssize_t ret;
unsigned int state;
ret = kstrtouint(buf, 10, &state);
if (ret) {
qvga_dev_err(g_sp0821->dev, "%s: fail to change str to int\n",
__func__);
return ret;
}
if (state == 0)
sp0821_hw_off(g_sp0821); /*OFF*/
else
sp0821_hw_on(g_sp0821); /*ON*/
return len;
}
static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO,
sp0821_get_reg, sp0821_set_reg);
static DEVICE_ATTR(cam_name, S_IWUSR | S_IRUGO,
sp0821_get_name, NULL);
static DEVICE_ATTR(light, S_IWUSR | S_IRUGO,
sp0821_get_light, sp0821_set_light);
static struct attribute *sp0821_attributes[] = {
&dev_attr_reg.attr,
&dev_attr_cam_name.attr,
&dev_attr_light.attr,
NULL
};
static struct attribute_group sp0821_attribute_group = {
.attrs = sp0821_attributes
};
static void sp0821_parse_gpio_dt(struct sp0821 *sp0821,
struct device_node *np)
{
qvga_dev_info(sp0821->dev, "%s enter, dev_i2c%d@0x%02X\n", __func__,
sp0821->i2c_seq, sp0821->i2c_addr);
sp0821->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (sp0821->reset_gpio < 0) {
qvga_dev_err(sp0821->dev,
"%s: no reset gpio provided, hardware reset unavailable\n",
__func__);
sp0821->reset_gpio = -1;
} else {
qvga_dev_info(sp0821->dev, "%s: reset gpio provided ok\n",
__func__);
}
sp0821->reset_gpio1 = of_get_named_gpio(np, "reset-gpio1", 0);
if (sp0821->reset_gpio1 < 0) {
qvga_dev_err(sp0821->dev,
"%s: no reset gpio1 provided, hardware reset unavailable\n",
__func__);
sp0821->reset_gpio1 = -1;
} else {
qvga_dev_info(sp0821->dev, "%s: reset gpio1 provided ok\n",
__func__);
}
}
static void sp0821_parse_dt(struct sp0821 *sp0821, struct device_node *np)
{
qvga_dev_info(sp0821->dev, "%s enter, dev_i2c%d@0x%02X\n", __func__,
sp0821->i2c_seq, sp0821->i2c_addr);
sp0821_parse_gpio_dt(sp0821, np);
}
/****************************************************************************
* sp0821 i2c driver
*****************************************************************************/
static int sp0821_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device_node *np = client->dev.of_node;
struct pinctrl *sp0821_pinctrl = NULL;
struct pinctrl_state *set_state = NULL;
struct pinctrl_state *sp0821_mclk_on = NULL;
struct pinctrl_state *sp0821_mclk_off = NULL;
struct sp0821 *sp0821 = NULL;
struct class *qvga_class;
struct device *dev;
int ret = -1;
pr_err("scw %s enter , i2c%d@0x%02x\n", __func__,
client->adapter->nr, client->addr);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
qvga_dev_err(&client->dev, "%s: check_functionality failed\n",
__func__);
ret = -ENODEV;
goto exit_check_functionality_failed;
}
sp0821 = sp0821_malloc_init(client);
g_sp0821 = sp0821;
sp0821->i2c_seq = sp0821->i2c_client->adapter->nr;
sp0821->i2c_addr = sp0821->i2c_client->addr;
if (sp0821 == NULL) {
dev_err(&client->dev, "%s: failed to parse device tree node\n",
__func__);
ret = -ENOMEM;
goto exit_devm_kzalloc_failed;
}
sp0821->dev = &client->dev;
i2c_set_clientdata(client, sp0821);
sp0821_parse_dt(sp0821, np);
if (gpio_is_valid(sp0821->reset_gpio)) {
ret = devm_gpio_request_one(&client->dev,
sp0821->reset_gpio,
GPIOF_OUT_INIT_LOW, "sp0821_rst");
if (ret) {
qvga_dev_err(&client->dev,
"%s: rst request failed\n", __func__);
goto exit_gpio_request_failed;
}
}
if (gpio_is_valid(sp0821->reset_gpio1)) {
ret = devm_gpio_request_one(&client->dev,
sp0821->reset_gpio1,
GPIOF_OUT_INIT_LOW, "sp0821_rst1");
if (ret) {
qvga_dev_err(&client->dev,
"%s: rst1 request failed\n", __func__);
goto exit_gpio_request_failed;
}
}
sp0821_pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR_OR_NULL(sp0821_pinctrl)) {
qvga_dev_err(&client->dev, "%s: sp0821_pinctrl not defined\n", __func__);
} else {
set_state = pinctrl_lookup_state(sp0821_pinctrl, SP0821_MCLK_ON);
if (IS_ERR_OR_NULL(set_state)) {
qvga_dev_err(&client->dev, "%s: sp0821_pinctrl lookup failed for mclk on\n", __func__);
} else {
sp0821_mclk_on = set_state;
}
set_state = pinctrl_lookup_state(sp0821_pinctrl, SP0821_MCLK_OFF);
if (IS_ERR_OR_NULL(set_state)) {
qvga_dev_err(&client->dev, "%s: sp0821_pinctrl lookup failed for mclk off\n", __func__);
} else {
sp0821_mclk_off = set_state;
}
ret = pinctrl_select_state(sp0821_pinctrl, sp0821_mclk_off);
if (ret < 0) {
qvga_dev_err(&client->dev, "%s: sp0821_pinctrl select failed for mclk off\n", __func__);
}
}
//power on camera
sp0821_hw_off_reset1(sp0821);
mdelay(5);
sp0821_vcam_control(sp0821, true);
mdelay(1);
ret = pinctrl_select_state(sp0821_pinctrl, sp0821_mclk_on);
if (ret < 0) {
qvga_dev_err(&client->dev, "%s: sp0821_pinctrl select failed for mclk on\n", __func__);
}
sp0821_hw_on_reset1(sp0821);
sp0821_hw_on_reset(sp0821);
mdelay(5);
// sp0821->hwen_flag = 1;
/* sp0821 sensor id */
ret = sp0821_GetSensorID(sp0821);
if (ret < 0) {
qvga_dev_err(&client->dev,
"%s: sp0821read_sensorid failed ret=%d\n", __func__,
ret);
goto exit_i2c_check_id_failed;
}
//power off camera
sp0821_vcam_control(sp0821, false);
sp0821_hw_off_reset1(sp0821);
// sp0821_Init(sp0821);
qvga_class = class_create(THIS_MODULE, "qvga_cam");
dev = device_create(qvga_class, NULL, client->dev.devt, NULL, "qvga_depth");
ret = sysfs_create_group(&dev->kobj, &sp0821_attribute_group);
if (ret < 0) {
qvga_dev_err(&client->dev,
"%s failed to create sysfs nodes\n", __func__);
}
return 0;
exit_i2c_check_id_failed:
sp0821_vcam_control(sp0821, false);
sp0821_hw_off_reset1(sp0821);
if (gpio_is_valid(sp0821->reset_gpio))
devm_gpio_free(&client->dev, sp0821->reset_gpio);
exit_gpio_request_failed:
devm_kfree(&client->dev, sp0821);
sp0821 = NULL;
exit_devm_kzalloc_failed:
exit_check_functionality_failed:
return ret;
}
static int sp0821_i2c_remove(struct i2c_client *client)
{
struct sp0821 *sp0821 = i2c_get_clientdata(client);
if (gpio_is_valid(sp0821->reset_gpio))
devm_gpio_free(&client->dev, sp0821->reset_gpio);
if (gpio_is_valid(sp0821->reset_gpio1))
devm_gpio_free(&client->dev, sp0821->reset_gpio1);
devm_kfree(&client->dev, sp0821);
sp0821 = NULL;
return 0;
}
static const struct of_device_id sp0821_of_match[] = {
{.compatible = "sc,sp_sp0821_yuv"},
{},
};
static struct i2c_driver sp0821_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "sp_sp0821_yuv",
.of_match_table = sp0821_of_match,
},
.probe = sp0821_i2c_probe,
.remove = sp0821_i2c_remove,
};
static int __init sp0821_yuv_init(void)
{
int ret;
pr_info("%s: driver version: %s\n", __func__,
SP0821_DRIVER_VERSION);
ret = i2c_add_driver(&sp0821_i2c_driver);
if (ret) {
pr_info("****[%s] Unable to register driver (%d)\n",
__func__, ret);
return ret;
}
return 0;
}
static void __exit sp0821_yuv_exit(void)
{
pr_info("%s enter\n", __func__);
i2c_del_driver(&sp0821_i2c_driver);
}
module_init(sp0821_yuv_init);
module_exit(sp0821_yuv_exit);
MODULE_AUTHOR("wangyuqiu@longcheer.com>");
MODULE_DESCRIPTION("sp0821 yuv driver");
MODULE_LICENSE("GPL v2");
这个代码哪里有问题可能会导致编译报错的?