mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
903 lines
26 KiB
C
Executable File
903 lines
26 KiB
C
Executable File
/*---------------------------------------------------------------------------------------------------------
|
||
* 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");
|