Files
linux/drivers/input/touchscreen/goodix_touch.c
2011-06-02 12:28:56 +08:00

903 lines
26 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*---------------------------------------------------------------------------------------------------------
* driver/input/touchscreen/goodix_touch.c
*
* Copyright(c) 2010 Goodix Technology Corp. All rights reserved.
* Author: Eltonny
* Date: 2010.11.11
*
*---------------------------------------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/earlysuspend.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
//#include <mach/gpio.h>
//#include <plat/gpio-cfg.h>
//#include <plat/gpio-bank-l.h>
//#include <plat/gpio-bank-f.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <mach/iomux.h>
#include <linux/goodix_touch.h>
#include <linux/goodix_queue.h>
#ifndef GUITAR_GT80X
#error The code does not match the hardware version.
#endif
static struct workqueue_struct *goodix_wq;
/********************************************
* 管理当前手指状态的伪队列,对当前手指根据时间顺序排序
* 适用于Guitar小屏 */
static struct point_queue finger_list; //record the fingers list
/*************************************************/
const char *rk29_ts_name = "Goodix TouchScreen of GT80X";
/*used by guitar_update module */
struct i2c_client * i2c_connect_client = NULL;
EXPORT_SYMBOL(i2c_connect_client);
#ifdef CONFIG_HAS_EARLYSUSPEND
static void goodix_ts_early_suspend(struct early_suspend *h);
static void goodix_ts_late_resume(struct early_suspend *h);
#endif
/*read the gt80x register ,used i2c bus*/
static int gt80x_read_regs(struct i2c_client *client, u8 reg, u8 buf[], unsigned len)
{
int ret;
ret =i2c_master_reg8_recv(client, reg, buf, len, 200*1000);
if(ret < 0)
dev_err(&client->dev,"i2c_read fail =%d\n",ret);
return ret;
}
/* set the gt80x registe,used i2c bus*/
static int gt80x_write_regs(struct i2c_client *client, u8 reg, u8 const buf[], unsigned short len)
{
int ret;
ret = i2c_master_reg8_send(client,reg, buf, len, 200*1000);
if (ret < 0) {
dev_err(&client->dev,"i2c_write fail =%d\n",ret);
}
return ret;
}
#if 0
/*******************************************************
功能:
读取从机数据
每个读操作用两条i2c_msg组成第1条消息用于发送从机地址
第2条用于发送读取地址和取回数据每条消息前发送起始信号
参数:
client: i2c设备包含设备地址
buf[0] 首字节为读取地址
buf[1]~buf[len]:数据缓冲区
len 读取数据长度
return
执行消息数
*********************************************************/
/*Function as i2c_master_send */
static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len)
{
struct i2c_msg msgs[2];
int ret=-1;
//发送写地址
msgs[0].flags=!I2C_M_RD;//写消息
msgs[0].addr=client->addr;
msgs[0].len=1;
msgs[0].buf=&buf[0];
//接收数据
msgs[1].flags=I2C_M_RD;//读消息
msgs[1].addr=client->addr;
msgs[1].len=len-1;
msgs[1].buf=&buf[1];
ret=i2c_transfer(client->adapter,msgs,2);
return ret;
}
/*******************************************************
功能:
向从机写数据
参数:
client: i2c设备包含设备地址
buf[0] 首字节为写地址
buf[1]~buf[len]:数据缓冲区
len 数据长度
return
执行消息数
*******************************************************/
/*Function as i2c_master_send */
static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len)
{
struct i2c_msg msg;
int ret=-1;
//发送设备地址
msg.flags=!I2C_M_RD;//写消息
msg.addr=client->addr;
msg.len=len;
msg.buf=data;
ret=i2c_transfer(client->adapter,&msg,1);
return ret;
}
#endif
/*******************************************************
功能:
Guitar初始化函数用于发送配置信息获取版本信息
参数:
ts: client私有数据结构体
return
执行结果码0表示正常执行
*******************************************************/
static int goodix_init_panel(struct goodix_ts_data *ts)
{
int i, ret = -1;
u8 start_reg = 0x30;
u8 buf[53];
uint8_t config_info[53] = {
0x13,0x05,0x04,0x28,0x02,0x14,0x14,0x10,0x50,0xBA,
0x14,0x00,0x1E,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,
0xCD,0xE1,0x00,0x00,0x00,0x00,0x4D,0xC1,0x20,0x01,
0x01,0x83,0x50,0x3C,0x1E,0xB4,0x00,0x33,0x2C,0x01,
0xEC,0x3C,0x64,0x32,0x71,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01
};
ret = gt80x_write_regs(ts->client, start_reg, config_info, 53);
if (ret < 0) {
printk("download gt80x firmware err %d\n", ret);
goto error_i2c_transfer;
}
msleep(10);
#if 0
ret = gt80x_read_regs(ts->client, start_reg, buf, 53);
if (ret < 0)
{
printk("\n--%s--Read Register values error !!!\n",__FUNCTION__);
}
for (i = 0; i < 53; i++)
{
if (buf[i] != config_info[i])
{
dev_err(&ts->client->dev,"may be i2c errorat reg_add[%d] = %#x current value = %#x expect value = %#x\n",
i, 0x30 + i, buf[i], config_info[i]);
break;
}
}
#endif
return 0;
error_i2c_transfer:
return ret;
}
/*读取GT80X的版本号并打印*/
static int goodix_read_version(struct goodix_ts_data *ts)
{
int ret;
uint8_t reg1 = 0x69;
uint8_t reg2 = 0x6a;
//uint8_t version[2]={0x69,0xff}; //command of reading Guitar's version
uint8_t version[1]={0xff}; //command of reading Guitar's version
//uint8_t version_data[41]; //store touchscreen version infomation
uint8_t version_data[40]; //store touchscreen version infomation
memset(version_data, 0 , sizeof(version_data));
//version_data[0]=0x6A;
//ret=i2c_write_bytes(ts->client,version,2);
ret = gt80x_write_regs(ts->client, reg1, version, 1);
if (ret < 0)
goto error_i2c_version;
msleep(16);
//ret=i2c_read_bytes(ts->client,version_data, 40);
ret = gt80x_read_regs(ts->client, reg2, version_data, 40);
if (ret < 0)
goto error_i2c_version;
//dev_info(&ts->client->dev," Guitar Version: %s\n", &version_data[1]);
printk("%s: Guitar Version: %s\n", __func__, version_data);
version[0] = 0x00; //cancel the command
//i2c_write_bytes(ts->client, version, 2);
ret = gt80x_write_regs(ts->client, reg1, version, 1);
return 0;
error_i2c_version:
return ret;
}
/*******************************************************
功能:
触摸屏工作函数
由中断触发接受1组坐标数据校验后再分析输出
参数:
ts: client私有数据结构体
return
执行结果码0表示正常执行
********************************************************/
static void goodix_ts_work_func(struct work_struct *work)
{
static uint8_t finger_bit = 0; //last time fingers' state
uint8_t read_position = 0;
uint8_t point_data[35] = {0};
uint8_t finger = 0; //record which finger is changed
int ret = -1;
int count = 0;
int check_sum = 0;
//unsigned char start_reg = 0x00;
struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work);
struct goodix_i2c_rmi_platform_data *pdata = ts->client->dev.platform_data;
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
//if (gpio_get_value(SHUTDOWN_PORT))
if (gpio_get_value(pdata->shutdown_pin))
{
//printk(KERN_ALERT "Guitar stop working.The data is invalid. \n");
goto NO_ACTION;
}
}
//#endif
//if i2c transfer is failed, let it restart less than 10 times
if (ts->retry > 9) {
if(!ts->use_irq && (ts->timer.state != HRTIMER_STATE_INACTIVE))
hrtimer_cancel(&ts->timer);
dev_info(&(ts->client->dev), "Because of transfer error, %s stop working.\n",rk29_ts_name);
return ;
}
if(ts->bad_data)
msleep(16);
ret = gt80x_read_regs(ts->client, point_data[0], &point_data[1], 34);
if(ret <= 0)
{
//dev_err(&(ts->client->dev),"I2C transfer error. Number:%d\n ", ret);
ts->bad_data = 1;
ts->retry++;
if(ts->power)
{
ts->power(ts, 0);
ts->power(ts, 1);
}
else
{
goodix_init_panel(ts);
msleep(500);
}
goto XFER_ERROR;
}
ts->bad_data = 0;
//The bit indicate which fingers pressed down
switch (point_data[1] & 0x1f)
{
case 0:
case 1:
for (count = 1; count < 8; count++)
check_sum += (int)point_data[count];
if ((check_sum % 256) != point_data[8])
goto XFER_ERROR;
break;
case 2:
case 3:
for ( count = 1; count < 13; count++)
check_sum += (int)point_data[count];
if ((check_sum % 256) != point_data[13])
goto XFER_ERROR;
break;
default: //(point_data[1]& 0x1f) > 3
for (count = 1; count < 34; count++)
check_sum += (int)point_data[count];
if ((check_sum % 256) != point_data[34])
goto XFER_ERROR;
}
point_data[1] &= 0x1f;
finger = finger_bit ^ point_data[1];
if (finger == 0 && point_data[1] == 0)
goto NO_ACTION; //no fingers and no action
else if(finger == 0) //the same as last time
goto BIT_NO_CHANGE;
//check which point(s) DOWN or UP
for (count = 0; (finger != 0) && (count < MAX_FINGER_NUM); count++)
{
if ((finger & 0x01) == 1) //current bit is 1, so NO.postion finger is change
{
if (((finger_bit >> count) & 0x01) ==1 ) //NO.postion finger is UP
set_up_point(&finger_list, count);
else
add_point(&finger_list, count);
}
finger >>= 1;
}
BIT_NO_CHANGE:
for(count = 0; count < finger_list.length; count++)
{
if(finger_list.pointer[count].state == FLAG_UP)
{
finger_list.pointer[count].x = finger_list.pointer[count].y = 0;
finger_list.pointer[count].pressure = 0;
continue;
}
if(finger_list.pointer[count].num < 3)
read_position = finger_list.pointer[count].num*5 + 3;
else if (finger_list.pointer[count].num == 4)
read_position = 29;
if(finger_list.pointer[count].num != 3)
{
finger_list.pointer[count].x = (unsigned int) (point_data[read_position]<<8) + (unsigned int)( point_data[read_position+1]);
finger_list.pointer[count].y = (unsigned int)(point_data[read_position+2]<<8) + (unsigned int) (point_data[read_position+3]);
finger_list.pointer[count].pressure = (unsigned int) (point_data[read_position+4]);
}
else
{
finger_list.pointer[count].x = (unsigned int) (point_data[18]<<8) + (unsigned int)( point_data[25]);
finger_list.pointer[count].y = (unsigned int)(point_data[26]<<8) + (unsigned int) (point_data[27]);
finger_list.pointer[count].pressure = (unsigned int) (point_data[28]);
}
// 将触摸屏的坐标映射到LCD坐标上. 触摸屏短边为X轴LCD坐标一般长边为X轴可能需要调整原点位置
finger_list.pointer[count].x = (TOUCH_MAX_WIDTH - finger_list.pointer[count].x)*SCREEN_MAX_WIDTH/TOUCH_MAX_WIDTH;//y
finger_list.pointer[count].y = finger_list.pointer[count].y*SCREEN_MAX_HEIGHT/TOUCH_MAX_HEIGHT ; //x
gt80xy_swap(finger_list.pointer[count].x, finger_list.pointer[count].y);
//printk("%s: dx = %d dy = %d\n", __func__,
// finger_list.pointer[count].x, finger_list.pointer[count].y);
}
#ifndef GOODIX_MULTI_TOUCH
if(finger_list.pointer[0].state == FLAG_DOWN)
{
input_report_abs(ts->input_dev, ABS_X, finger_list.pointer[0].x);
input_report_abs(ts->input_dev, ABS_Y, finger_list.pointer[0].y);
}
input_report_abs(ts->input_dev, ABS_PRESSURE, pressure[0]);
input_report_key(ts->input_dev, BTN_TOUCH, finger_list.pointer[0].state);
#else
/* ABS_MT_TOUCH_MAJOR is used as ABS_MT_PRESSURE in android. */
for(count = 0; count < (finger_list.length); count++)
{
if(finger_list.pointer[count].state == FLAG_DOWN)
{
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, finger_list.pointer[count].x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, finger_list.pointer[count].y);
}
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, finger_list.pointer[count].pressure);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, finger_list.pointer[count].pressure);
input_mt_sync(ts->input_dev);
}
#if 0
read_position = finger_list.length-1;
if(finger_list.length > 0 && finger_list.pointer[read_position].state == FLAG_DOWN)
{
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, finger_list.pointer[read_position].x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, finger_list.pointer[read_position].y);
}
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, finger_list.pointer[read_position].pressure);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, finger_list.pointer[read_position].pressure);
input_mt_sync(ts->input_dev);
#endif
#endif
input_sync(ts->input_dev);
del_point(&finger_list);
finger_bit=point_data[1];
XFER_ERROR:
NO_ACTION:
if(ts->use_irq)
enable_irq(ts->client->irq);
}
/*******************************************************
功能:
计时器响应函数
由计时器触发,调度触摸屏工作函数运行;之后重新计时
参数:
timer函数关联的计时器
return
计时器工作模式HRTIMER_NORESTART表示不需要自动重启
********************************************************/
static enum hrtimer_restart goodix_ts_timer_func(struct hrtimer *timer)
{
struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
queue_work(goodix_wq, &ts->work);
if(ts->timer.state != HRTIMER_STATE_INACTIVE)
hrtimer_start(&ts->timer, ktime_set(0, 16000000), HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
/*******************************************************
功能:
中断响应函数
由中断触发,调度触摸屏处理函数运行
参数:
timer函数关联的计时器
return
计时器工作模式HRTIMER_NORESTART表示不需要自动重启
********************************************************/
//#if defined(INT_PORT)
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
struct goodix_ts_data *ts = dev_id;
disable_irq_nosync(ts->client->irq);
queue_work(goodix_wq, &ts->work);
return IRQ_HANDLED;
}
//#endif
/*******************************************************
功能:
GT80X的电源管理
参数:
on:设置GT80X运行模式0为进入Sleep模式
return
是否设置成功小于0表示设置失败
********************************************************/
//#if defined(SHUTDOWN_PORT)
static int goodix_ts_power(struct goodix_ts_data * ts, int on)
{
int ret = -1;
struct goodix_i2c_rmi_platform_data *pdata = NULL;
if(ts == NULL || (ts && !ts->use_shutdown))
return -1;
pdata = ts->client->dev.platform_data;
switch(on)
{
case 0:
gpio_set_value(pdata->shutdown_pin, 1);
msleep(5);
if(gpio_get_value(pdata->shutdown_pin)) //has been suspend
ret = 0;
break;
case 1:
gpio_set_value(pdata->shutdown_pin, 0);
msleep(5);
if(gpio_get_value(pdata->shutdown_pin)) //has been suspend
ret = -1;
else
{
ret = goodix_init_panel(ts);
if(!ret)
msleep(500);
}
break;
//default:printk("Command ERROR.\n");
}
printk(on?"Set Guitar's Shutdown LOW\n":"Set Guitar's Shutdown HIGH\n");
return 0;
}
//#endif
/*******************************************************
功能:
触摸屏探测函数
在注册驱动时调用要求存在对应的client
用于IO,中断等资源申请;设备注册;触摸屏初始化等工作
参数:
client待驱动的设备结构体
id设备ID
return
执行结果码0表示正常执行
********************************************************/
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct goodix_ts_data *ts;
int ret = 0;
int retry=0;
int count=0;
u8 test_reg = 0x30;
u8 test_buf[] = {0x01};
struct goodix_i2c_rmi_platform_data *pdata;
pdata = client->dev.platform_data;
dev_dbg(&client->dev,"Install touchscreen driver for guitar.\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{
dev_err(&client->dev, "System need I2C function.\n");
ret = -ENODEV;
goto err_check_functionality_failed;
}
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL) {
ret = -ENOMEM;
goto err_alloc_data_failed;
}
//Check I2C function
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
//ret = gpio_request(SHUTDOWN_PORT, "TS_SHUTDOWN"); //Request IO
ret = gpio_request(pdata->shutdown_pin, "TS_SHUTDOWN"); //Request IO
if (ret < 0)
{
//printk(KERN_ALERT "Failed to request GPIO:%d, ERRNO:%d\n",(int)SHUTDOWN_PORT,ret);
printk(KERN_ALERT "Failed to request GPIO:%d, ERRNO:%d\n",(int)pdata->shutdown_pin,ret);
goto err_gpio_request;
}
//gpio_direction_output(SHUTDOWN_PORT, 0); //Touchscreen is waiting to wakeup
//ret = gpio_get_value(SHUTDOWN_PORT);
gpio_direction_output(pdata->shutdown_pin, 0); //Touchscreen is waiting to wakeup
ret = gpio_get_value(pdata->shutdown_pin);
if (ret)
{
printk(KERN_ALERT "Cannot set touchscreen to work.\n");
goto err_i2c_failed;
}
}
//#endif
i2c_connect_client = client; //used by Guitar Updating.
msleep(16);
for (retry = 0; retry < 5; retry++)
{
ret = gt80x_write_regs(client, test_reg, test_buf, 1); //Test i2c.
if (ret > 0)
break;
}
if (ret < 0)
{
dev_err(&client->dev, "Warnning: I2C connection might be something wrong!\n");
goto err_i2c_failed;
}
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
ts->use_shutdown = 1;
//gpio_set_value(SHUTDOWN_PORT, 1);
gpio_set_value(pdata->shutdown_pin, 1);
}//suspend
//#endif
INIT_WORK(&ts->work, goodix_ts_work_func);
ts->client = client;
i2c_set_clientdata(client, ts);
//pdata = client->dev.platform_data;
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL) {
ret = -ENOMEM;
dev_dbg(&client->dev,"Failed to allocate input device\n");
goto err_input_dev_alloc_failed;
}
ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
#ifndef GOODIX_MULTI_TOUCH
ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
// | BIT_MASK(ABS_MT_TOUCH_MAJOR)| BIT_MASK(ABS_MT_WIDTH_MAJOR)
// BIT_MASK(ABS_MT_POSITION_X) |
// BIT_MASK(ABS_MT_POSITION_Y); // for android
input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_HEIGHT, 0, 0);
input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_WIDTH, 0, 0);
input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);
#else
input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_HEIGHT, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_WIDTH, 0, 0);
#endif
sprintf(ts->phys, "input/ts)");
ts->input_dev->name = rk29_ts_name;
ts->input_dev->phys = ts->phys;
ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->id.vendor = 0xDEAD;
ts->input_dev->id.product = 0xBEEF;
ts->input_dev->id.version = 10427;
finger_list.length = 0;
ret = input_register_device(ts->input_dev);
if (ret) {
dev_err(&client->dev,"Probe: Unable to register %s input device\n", ts->input_dev->name);
goto err_input_register_device_failed;
}
ts->use_irq = 0;
ts->retry = 0;
ts->bad_data = 0;
//#if defined(INT_PORT)
//client->irq=TS_INT;
//gpio_set_value(TS_INT, 0);
if (pdata && pdata->irq_pin) {
client->irq = gpio_to_irq(client->irq);
if (client->irq)
{
//ret = gpio_request(INT_PORT, "TS_INT"); //Request IO
ret = gpio_request(pdata->irq_pin, "TS_INT"); //Request IO
if (ret < 0)
{
//dev_err(&client->dev, "Failed to request GPIO:%d, ERRNO:%d\n",(int)INT_PORT,ret);
dev_err(&client->dev, "Failed to request GPIO:%d, ERRNO:%d\n", (int)pdata->irq_pin, ret);
goto err_int_request_failed;
}
//ret = s3c_gpio_cfgpin(INT_PORT, INT_CFG); //Set IO port function
ret = request_irq(client->irq, goodix_ts_irq_handler , IRQ_TYPE_EDGE_RISING,
client->name, ts);
if (ret != 0) {
dev_err(&client->dev,"Can't allocate touchscreen's interrupt!ERRNO:%d\n", ret);
//gpio_direction_input(INT_PORT);
//gpio_free(INT_PORT);
gpio_direction_input(pdata->irq_pin);
gpio_free(pdata->irq_pin);
goto err_int_request_failed;
}
else
{
//disable_irq(TS_INT);
disable_irq(client->irq);
ts->use_irq = 1;
dev_dbg(&client->dev,"Reques EIRQ %d succesd on GPIO:%d\n",client->irq, pdata->irq_pin);
}
}
}
//#endif
err_int_request_failed:
if (!ts->use_irq)
{
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = goodix_ts_timer_func;
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
}
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
//gpio_set_value(SHUTDOWN_PORT, 0);
gpio_set_value(pdata->shutdown_pin, 0);
msleep(10);
ts->power = goodix_ts_power;
}
//#endif
for(count = 0; count < 3; count++)
{
ret = goodix_init_panel(ts);
if(ret != 0) //Initiall failed
continue;
else
{
if(ts->use_irq)
enable_irq(client->irq);
//enable_irq(TS_INT);
break;
}
}
if(ret != 0) {
ts->bad_data = 1;
goto err_init_godix_ts;
}
goodix_read_version(ts);
msleep(500);
#ifdef CONFIG_HAS_EARLYSUSPEND
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->early_suspend.suspend = goodix_ts_early_suspend;
ts->early_suspend.resume = goodix_ts_late_resume;
register_early_suspend(&ts->early_suspend);
#endif
dev_info(&client->dev,"Start %s in %s mode\n",
ts->input_dev->name, ts->use_irq ? "Interrupt" : "Polling");
return 0;
err_init_godix_ts:
if(ts->use_irq)
{
//free_irq(TS_INT,ts);
free_irq(client->irq,ts);
//#if defined(INT_PORT)
if (pdata && pdata->irq_pin)
gpio_free(pdata->irq_pin);
//#endif
}
err_input_register_device_failed:
input_free_device(ts->input_dev);
err_input_dev_alloc_failed:
i2c_set_clientdata(client, NULL);
err_i2c_failed:
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
//gpio_direction_input(SHUTDOWN_PORT);
//gpio_free(SHUTDOWN_PORT);
gpio_direction_input(pdata->shutdown_pin);
gpio_free(pdata->shutdown_pin);
}
//#endif
err_gpio_request:
kfree(ts);
err_alloc_data_failed:
err_check_functionality_failed:
return ret;
}
/*******************************************************
功能:
驱动资源释放
参数:
client设备结构体
return
执行结果码0表示正常执行
********************************************************/
static int goodix_ts_remove(struct i2c_client *client)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
struct goodix_i2c_rmi_platform_data *pdata;
pdata = client->dev.platform_data;
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&ts->early_suspend);
#endif
if (ts->use_irq)
{
free_irq(client->irq, ts);
//#if defined(INT_PORT)
if (pdata && pdata->irq_pin)
gpio_free(pdata->irq_pin);
//gpio_free(INT_PORT);
//#endif
}
else
hrtimer_cancel(&ts->timer);
//#ifdef SHUTDOWN_PORT
if (pdata && pdata->shutdown_pin) {
if(ts->use_shutdown)
{
//gpio_direction_input(SHUTDOWN_PORT);
//gpio_free(SHUTDOWN_PORT);
gpio_direction_input(pdata->shutdown_pin);
gpio_free(pdata->shutdown_pin);
}
}
//#endif
dev_notice(&client->dev,"The driver is removing...\n");
i2c_set_clientdata(client, NULL);
input_unregister_device(ts->input_dev);
if(ts->input_dev)
kfree(ts->input_dev);
kfree(ts);
return 0;
}
//停用设备
static int goodix_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
int ret;
struct goodix_ts_data *ts = i2c_get_clientdata(client);
if (ts->use_irq)
disable_irq(client->irq);
else if(ts->timer.state)
hrtimer_cancel(&ts->timer);
ret = cancel_work_sync(&ts->work);
if(ret && ts->use_irq)
enable_irq(client->irq);
if (ts->power) {
ret = ts->power(ts,0);
if (ret < 0)
printk(KERN_ERR "%s power off failed\n", rk29_ts_name);
}
return 0;
}
//重新唤醒
static int goodix_ts_resume(struct i2c_client *client)
{
int ret;
struct goodix_ts_data *ts = i2c_get_clientdata(client);
if (ts->power) {
ret = ts->power(ts, 1);
if (ret < 0)
printk(KERN_ERR "%s power on failed\n", rk29_ts_name);
}
if (ts->use_irq)
enable_irq(client->irq);
else
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void goodix_ts_early_suspend(struct early_suspend *h)
{
struct goodix_ts_data *ts;
ts = container_of(h, struct goodix_ts_data, early_suspend);
goodix_ts_suspend(ts->client, PMSG_SUSPEND);
}
static void goodix_ts_late_resume(struct early_suspend *h)
{
struct goodix_ts_data *ts;
ts = container_of(h, struct goodix_ts_data, early_suspend);
goodix_ts_resume(ts->client);
}
#endif
//可用于该驱动的 设备名—设备ID 列表
//only one client
static const struct i2c_device_id goodix_ts_id[] = {
{ GOODIX_I2C_NAME, 0 },
{ }
};
//设备驱动结构体
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = goodix_ts_suspend,
.resume = goodix_ts_resume,
#endif
.id_table = goodix_ts_id,
.driver = {
.name = GOODIX_I2C_NAME,
.owner = THIS_MODULE,
},
};
/*******************************************************
功能:
驱动加载函数
return
执行结果码0表示正常执行
********************************************************/
static int __devinit goodix_ts_init(void)
{
int ret;
//printk(KERN_DEBUG "%s is installing...\n", rk29_ts_name);
goodix_wq = create_workqueue("goodix_wq");
if (!goodix_wq) {
printk(KERN_ALERT "Creat workqueue faiked\n");
return -ENOMEM;
}
ret=i2c_add_driver(&goodix_ts_driver);
return ret;
}
/*******************************************************
功能:
驱动卸载函数
参数:
client设备结构体
********************************************************/
static void __exit goodix_ts_exit(void)
{
printk(KERN_DEBUG "%s is exiting...\n", rk29_ts_name);
i2c_del_driver(&goodix_ts_driver);
if (goodix_wq)
destroy_workqueue(goodix_wq);
}
late_initcall(goodix_ts_init);
module_exit(goodix_ts_exit);
MODULE_DESCRIPTION("Goodix Touchscreen Driver");
MODULE_LICENSE("GPL");