主题 : Tiny210v2 MLC 2G 电容多点触摸屏 goodix GT801 driver 源代码 修改出来了,请大家查错 复制链接 | 浏览器收藏 | 打印
级别: 骑士
UID: 5844
精华: 9
发帖: 62
金钱: 760 两
威望: 152 点
贡献值: 9 点
综合积分: 304 分
注册时间: 2009-05-11
最后登录: 2018-02-08
楼主  发表于: 2012-11-13 18:56

 Tiny210v2 MLC 2G 电容多点触摸屏 goodix GT801 driver 源代码 修改出来了,请大家查错

管理提醒: 本帖被 xoom 执行置顶操作(2012-11-13)

首先感谢 sandan2002 提供给我的 GT801 针对其他项目的源代码。

在这份代码的基础上,进行了一点修正, 已经可以作为模块编译了。
在我的机器上看样子是好用的。 但还没有完全测试。

前两天写过一个把  goodix_touch.ko 当做黑盒分析的文章,
今天的修改虽然有源代码了,但是配置信息的部分,仍然是黑盒,不知道每一个字节的意义。
所以如果谁有  GT801 的芯片手册,麻烦发送给我看一下。

我机器使用的是自己的 toolchain, 内核是 友善的 3.0.8 ,不过配置有稍微修改。

1. 首先是内核选项, input/drivers/touchpanel 的 那个 goodix 必须选成 y。否则 I2C 通信需要的 gpio 以及其他部分有问题。
友善之臂将 I2C 通信中需要使用的 中断提醒( GPIO ) 以及中断都已经配置好了,不过需要 CONFIG_TOUCHSCREEN_GOODIX 是 y。
有兴趣的可以去查看 arch/arm/mach-s5pv210/mach-mini210.c 中的代码,搜索关键字:CONFIG_TOUCHSCREEN_GOODIX
不过这个选项在 .config 中,默认就是 y 的。
----------------------------------------------------------------------------------
CONFIG_TOUCHSCREEN_GOODIX=y
----------------------------------------------------------------------------------

2. makefile:
自己改一改,将路径改成你编译的内核的路径。
编译模块: make
清除中间文件: make clean
----------------------------------------------------------------------------------
obj-m := goodix_touch.o

KERNELDIR ?= /opt/S5PV210/rootfs/usr/src/linux-3.0.8/
PWD := $(shell pwd)

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
----------------------------------------------------------------------------------

3. goodix_touch.c
主要改了 初始化的配置数据(来源于友善的ko截取出来的), GPIO那部分的配置, 坐标上报位置修改, 以及一个 driver remove 导致 内核挂掉的问题。
需要说明一下。 touchpanel 的 (0,0) 坐标在 右上角, 显示屏的(0,0)坐标在左上角,因此需要对坐标转化一下。
----------------------------------------------------------------------------------
/*---------------------------------------------------------------------------------------------------------
* driver/input/touchscreen/goodix_touch.c
*
* Copyright(c) 2010 Goodix Technology Corp.    
*
* 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.
*
* Change Date:
*        2010.11.11, add point_queue's definiens.    
*         2011.03.09, rewrite point_queue's definiens.  
*         2011.05.12, delete point_queue for Android 2.2/Android 2.3 and so on.
*         2012.11.13, zoulz modified for tiny210v2 GT801 touchscreen
*---------------------------------------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include "goodix_touch.h"
/* #include <linux/goodix_queue.h> //used before android 2.2 */

#ifndef GUITAR_GT80X
#error The code does not match the hardware version.
#endif

const char *s3c_ts_name = "Goodix TouchScreen of GT80X";
static struct workqueue_struct *goodix_wq;

/*used by GT80X-IAP 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

/*    0----1----2----3----4----5----6----7----8----9----A----B----C----D----E----F */
#if 0
static uint8_t config_info_GUITAR_CONFIG_43[ 54 ] = {
    0x30,0x19,0x05,0x06,0x28,0x02,0x14,0x14,0x10,0x50,0xB8,TOUCH_MAX_WIDTH>>8,TOUCH_MAX_WIDTH&0xFF,TOUCH_MAX_HEIGHT>>8,TOUCH_MAX_HEIGHT&0xFF,0x01,
    0x23,0x45,0x67,0x89,0xAB,0xCD,0xE1,0x00,0x00,0x32,0x32,0x05,0xCF,0x20,0x07,0x0B,
    0x8B,0x50,0x3C,0x1E,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x01
};
static uint8_t config_info_TCL_5_INCH[ 54 ] = {
    0x30,0x19,0x05,0x06,0x28,0x02,0x14,0x14,0x10,0x40,0xB8,TOUCH_MAX_WIDTH>>8,TOUCH_MAX_WIDTH&0xFF,TOUCH_MAX_HEIGHT>>8,TOUCH_MAX_HEIGHT&0xFF,0x01,
    0x23,0x45,0x67,0x89,0xAB,0xCD,0xE1,0x00,0x00,0x00,0x00,0x0D,0xCF,0x20,0x03,0x05,
    0x83,0x50,0x3C,0x1E,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x01
};
#endif
static uint8_t config_info_TINY210_7_INCH_800_480[ 54 ] = {
//    0x30,0x0f,0x05,0x06,0x28,0x02,0x14,0x14,0x10,0x28,0xb2,0x01,0xe0,0x03,0x20,0xed, // 480->0x01e0 800->0x0320
    0x30,0x0f,0x05,0x06,0x28,0x02,0x14,0x14,0x10,0x28,0xb2,TOUCH_MAX_WIDTH>>8,TOUCH_MAX_WIDTH&0xFF,TOUCH_MAX_HEIGHT>>8,TOUCH_MAX_HEIGHT&0xFF,0xed,
    0xcb,0xa9,0x87,0x65,0x43,0x21,0x00,0x00,0x00,0x00,0x00,0x4d,0xc1,0x20,0x01,0x01,
    0x41,0x64,0x3c,0x1e,0x28,0x0e,0x00,0x00,0x00,0x00,0x50,0x3c,0x32,0x71,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x01
};

/**********************************************************************    
    本程序中I2C通信方式为:
    7bit从机地址|读写位 + buf(数据地址+读写数据)
     --------------------------------------------------------------------
    |  从机地址   | buf[0](数据地址) | buf[1]~buf[MAX-1](写入或读取到的数据)  |
     --------------------------------------------------------------------
    移植前请根据自身主控格式修改!!
***********************************************************************/

//Function as i2c_master_receive, and return 2 if operation is successful.
static int i2c_read_bytes( struct i2c_client *client, uint8_t *buf, uint16_t len )
{
    struct i2c_msg msgs[2];

    //发送写地址
    //Write : which register address to read
    msgs[0].flags    = !I2C_M_RD;//写消息
    msgs[0].addr    = client->addr;
    msgs[0].len        = 1;
    msgs[0].buf        = buf;
    //接收数据
    //Read : data from that register address
    msgs[1].flags    = I2C_M_RD;
    msgs[1].addr    = client->addr;
    msgs[1].len        = len-1;
    msgs[1].buf        = buf+1;
    
    return i2c_transfer( client->adapter, msgs, 2 );
}

//Function as i2c_master_send, and return 1 if operation is successful.
static int i2c_write_bytes( struct i2c_client *client, uint8_t *data, uint16_t len )
{
    struct i2c_msg msg;
    
    msg.flags    = !I2C_M_RD;//写消息
    msg.addr    = client->addr;
    msg.len        = len;
    msg.buf        = data;        
    
    return i2c_transfer( client->adapter, &msg, 1 );
}

/*******************************************************
功能:
    GT80X初始化函数,用于发送配置信息
参数:
    ts:    struct goodix_ts_data
return:
    执行结果码,0表示正常执行
*******************************************************/
static bool goodix_init_panel( struct goodix_ts_data *ts )
{
    int    ret;
    int    count;
    
    //There are some demo configs. MUST be changed as some different panels.
    for( count = 5; count > 0; count-- ){
        
        ret = i2c_write_bytes( ts->client, config_info_TINY210_7_INCH_800_480, sizeof( config_info_TINY210_7_INCH_800_480 ) );
        if( ret == 1 ){        //Initiall success
            break;
        }
        else{
            msleep( 10 );
        }
    }                          
    
    return ( ( ret == 1 ) ? true : false );
}

/*读取GT80X的版本号并打印*/
static int  goodix_read_version( struct goodix_ts_data *ts )
{
    #define GT80X_VERSION_LENGTH    40    
    int        ret;
    uint8_t version[ 2 ] = { 0x69,0xff };                        //command of reading Guitar's version
    uint8_t version_data[ GT80X_VERSION_LENGTH + 1 ] = { 0x6a };//store touchscreen version infomation

    memset( version_data+1, 0, GT80X_VERSION_LENGTH );

    ret = i2c_write_bytes( ts->client, version, 2 );
    if( ret != 1 ){
        return ret;
    }

    msleep( 50 );                        

    // version_data -> [0]:write:register [1-39]:read:version [40]:\0:version-string-NULL
    ret = i2c_read_bytes( ts->client, version_data, GT80X_VERSION_LENGTH );
    if( ret != 2 ){
        strncpy( version_data+1, "NULL", 4 );
    }
    dev_info( &ts->client->dev, "GT80X Version: %s\n", version_data+1 );

    version[1] = 0x00;                //cancel the command
    i2c_write_bytes( ts->client, version, 2 );
    
    return 0;
}

/*******************************************************    
功能:
    触摸屏工作函数
    由中断触发,接受1组坐标数据,校验后再分析输出
参数:
    ts:    client私有数据结构体
return:
    执行结果码,0表示正常执行
********************************************************/
static void goodix_ts_work_func( struct work_struct *work )
{
    static struct point_node pointer[ MAX_FINGER_NUM ];
    static uint8_t finger_last = 0;    //last time fingers' state

    struct point_node * p = NULL;
    uint8_t read_position = 0;
    uint8_t point_data[ READ_BYTES_NUM ]={ 0 };
    uint8_t finger, finger_current;                //record which finger is changed
    uint8_t check_sum = 0;
    unsigned int x, y;
    int count = 0;
    int ret = -1;
    
    struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work);

    if( (ts->use_shutdown) && (gpio_get_value( ts->gpio_shutdown )) ){
        goto NO_ACTION;                    //The data is invalid.
    }

    //if i2c-transfer is failed, let it restart less than 10 times. FOR DEBUG.
    /*
    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",s3c_ts_name);
        return ;
    }
    */
    ret = i2c_read_bytes( ts->client, point_data, sizeof( point_data ) );
    if( ret <= 0 ){

        dev_dbg( &(ts->client->dev), "I2C transfer error. ERROR Number:%d\n ", ret );
        
        ts->retry++;
        if( ts->retry >= 100 ){    //It's not normal for too much i2c-error.

            dev_err( &(ts->client->dev ), "Reset the chip for i2c error.\n " );
            ts->retry = 0;
            
            if( ts->power ){
                ts->power( ts, 0 );
                ts->power( ts, 1 );
            }
            else{
                goodix_init_panel( ts );
                msleep( 200 );
            }
        }
        goto XFER_ERROR;
    }    
    
    //如果能够保证在INT中断后及时的读取坐标数据,可以不进行校验
    if( !ts->use_irq ){

        switch( point_data[1] & 0x1f ){
            case 0:
                break;

            case 1:
                for( count=1; count<8; count++ ){
                    check_sum += (int)point_data[ count ];
                }
                read_position = 8;
                break;

            case 2:
            case 3:
                for( count=1; count<13; count++ ){
                    check_sum += (int)point_data[ count ];
                }
                read_position = 13;
                break;    

            default:        //(point_data[1]& 0x1f) > 3
                for( count=1; count<34;count++ ){
                    check_sum += (int)point_data[ count ];
                }
                read_position = 34;
                break;
        }

        if( check_sum != point_data[ read_position ] ){
            goto XFER_ERROR;
        }
    }
    
    //The bits indicate which fingers pressed down
    finger_current    = point_data[1] & 0x1f;
    finger            = finger_current^finger_last;    

    if( ( finger == 0 ) && ( finger_current == 0 ) ){
        goto NO_ACTION;                    //no action
    }
    else if( finger == 0 ){
        goto BIT_NO_CHANGE;                //the same as last time
    }
        
    //check which point(s) DOWN or UP
    for( count = 0; count < MAX_FINGER_NUM;  count++ ){

        p = &pointer[count];
        p->id = count;
        if( ( finger_current & FLAG_MASK ) != 0 ){
            p->state = FLAG_DOWN;
        }
        else{
            if( ( finger & FLAG_MASK ) != 0 ){        //send press release.
                p->state = FLAG_UP;
            }
            else{
                p->state = FLAG_INVALID;
            }
        }
        
        finger            >>= 1;
        finger_current    >>= 1;    
    }
    finger_last = point_data[1] & 0x1f;    //restore last presse state.

BIT_NO_CHANGE:
    for( count = 0; count < MAX_FINGER_NUM; count++ ){

        p = &pointer[count];
        
        if( p->state == FLAG_INVALID ){
            continue;
        }
        if( p->state == FLAG_UP ){
            x = y = 0;
            p->pressure = 0;
            continue;
        }
        
        if( p->id < 3 ){
            read_position = p->id *5 + 3;
        }
        else{
            read_position = 29;
        }
        
        if( p->id != 3 ){
            x = (unsigned int)( point_data[ read_position   ]<<8) + (unsigned int)( point_data[ read_position+1 ] );
            y = (unsigned int)( point_data[ read_position+2 ]<<8) + (unsigned int)( point_data[ read_position+3 ] );
            p->pressure = point_data[ read_position+4 ];
        }
#if MAX_FINGER_NUM > 3
        else {
            x = (unsigned int)( point_data[18]<<8 ) + (unsigned int)( point_data[25] );
            y = (unsigned int)( point_data[26]<<8 ) + (unsigned int)( point_data[27] );
            p->pressure = point_data[28];
        }
#endif

        // 将触摸屏的坐标映射到LCD坐标上. 触摸屏短边为X轴,LCD坐标一般长边为X轴,可能需要调整原点位置
        x = x * SCREEN_MAX_WIDTH  / TOUCH_MAX_WIDTH;
        y = y * SCREEN_MAX_HEIGHT / TOUCH_MAX_HEIGHT ;
    
        p->x = SCREEN_MAX_HEIGHT - y;
        p->y = x;
    }

#ifndef GOODIX_MULTI_TOUCH    
    if( pointer[0].state == FLAG_DOWN ){
        input_report_abs( ts->input_dev, ABS_X, pointer[0].x );
        input_report_abs( ts->input_dev, ABS_Y, pointer[0].y );    
    }
    input_report_abs( ts->input_dev, ABS_PRESSURE, pointer[0].pressure);
    input_report_key( ts->input_dev, BTN_TOUCH, ( ( pointer[0].state == FLAG_INVALID) ? FLAG_UP : pointer[0].state ) );  
#else
    /* ABS_MT_TOUCH_MAJOR is used as ABS_MT_PRESSURE in android. */
    for( count = 0; count < MAX_FINGER_NUM; count++ ){
        
        p = &pointer[count];
        if( p->state == FLAG_INVALID ){
            continue;
        }
        
        if( p->state == FLAG_DOWN ){

            input_report_abs( ts->input_dev, ABS_MT_POSITION_X, p->x );
            input_report_abs( ts->input_dev, ABS_MT_POSITION_Y, p->y );
            dev_dbg( &(ts->client->dev), "Id:%d, x:%d, y:%d\n", p->id, p->x, p->y );
        }

        input_report_abs( ts->input_dev, ABS_MT_TRACKING_ID, p->id       );
        input_report_abs( ts->input_dev, ABS_MT_TOUCH_MAJOR, p->pressure );
        input_report_abs( ts->input_dev, ABS_MT_WIDTH_MAJOR, p->pressure );

        input_mt_sync( ts->input_dev );
    }
#endif

    input_sync( ts->input_dev );

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;

    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;
}

/*******************************************************    
功能:
    中断响应函数
    由中断触发,调度触摸屏处理函数运行
********************************************************/
static irqreturn_t goodix_ts_irq_handler( int irq, void *dev_id )
{
    struct goodix_ts_data *ts;
    
    ts = dev_id;
    
    disable_irq_nosync( ts->client->irq );
    queue_work( goodix_wq, &ts->work );
    
    return IRQ_HANDLED;
}

/*******************************************************    
功能:
    GT80X的电源管理
参数:
    on:设置GT80X运行模式,0为进入Sleep模式
return:
    是否设置成功,小于0表示设置失败
********************************************************/
static int goodix_ts_power( struct goodix_ts_data * ts, int on )
{
    int ret = 0;


    if( !ts->use_shutdown ){
        return -1;
    }

    if( on ){
        gpio_set_value( ts->gpio_shutdown, 0 );
        msleep( 5 );
        
        if( gpio_get_value( ts->gpio_shutdown ) ){    //has been waked up
            ret = -1;
        }
        else {
            msleep( 200 );
        }
    }
    else{
        gpio_set_value( ts->gpio_shutdown, 1 );
        msleep( 5 );
        
        if( gpio_get_value( ts->gpio_shutdown ) ){    //has been suspended
            ret = 0;
        }
    }    

    dev_dbg( &ts->client->dev, "Set Guitar's Shutdown %s. Ret:%d.\n", on?"HIGH":"LOW", ret );
    return ret;
}

//Test i2c to check device. Before it SHUTDOWN port Must be low state 30ms or more.
static bool goodix_i2c_test(struct i2c_client * client)
{
    int ret, retry;
    uint8_t test_data[1] = { 0 };    //only write a data address.
    
    for(retry=0; retry < 5; retry++)
    {
        ret =i2c_write_bytes(client, test_data, 1);    //Test i2c.
        if (ret == 1)
            break;
        msleep(5);
    }
    
    return ret==1 ? true : false;
}

/*******************************************************    
功能:
    触摸屏探测函数
    在注册驱动时调用(要求存在对应的client);
    用于IO,中断等资源申请;设备注册;触摸屏初始化等工作
参数:
    client:待驱动的设备结构体
    id:设备ID
return:
    执行结果码,0表示正常执行
********************************************************/
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct goodix_i2c_platform_data *pdata;
    struct goodix_ts_data *ts;
    int ret = 0;

    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;
    }
    
    /* 获取预定义资源 */
    pdata = client->dev.platform_data;
    if( pdata ){
        /* use this pdata such as that: */
        ts->gpio_shutdown = pdata->gpio_shutdown;
        ts->gpio_irq = pdata->gpio_irq;
        
        s3c_gpio_cfgpin(ts->gpio_irq, pdata->irq_cfg);                //INT
        //s3c_gpio_cfgpin(ts->gpio_shutdown, pdata->shutdown_cfg);    //Output
    }
    else{
        /*If pdata is not used, get value from head files. */
#ifdef SHUTDOWN_PORT
        ts->gpio_shutdown = SHUTDOWN_PORT;
#endif
#ifdef INT_PORT
        ts->gpio_irq = INT_PORT;    
        s3c_gpio_cfgpin(ts->gpio_irq, INT_CFG);    //Set INT function
        client->irq = gpio_to_irq(ts->gpio_irq);
#endif
    }
    
    if( ts->gpio_shutdown ){

        ret = gpio_request(ts->gpio_shutdown, "TS_SHUTDOWN");    //Request IO
        if( ret < 0 ){
            printk( KERN_ALERT "Failed to request GPIO:%d, ERRNO:%d\n", ts->gpio_shutdown, ret );
            goto err_gpio_request;
        }

        gpio_direction_output(ts->gpio_shutdown, 0);    //Touchscreen is waiting to wake up
        msleep( 2 );
        ret = gpio_get_value( ts->gpio_shutdown );
        if( ret ){
            printk( KERN_ALERT  "Can't set touchscreen to work.\n" );
            goto err_i2c_failed;
        }
        ts->use_shutdown = true;
        msleep(25);        //waiting for initialization of Guitar.
    }
    else{
        ts->use_shutdown = false;
    }

    //this is for s3c2400, not suitable for tiny210
#if 0
    int count=0;
    i2c_connect_client = client;                //used by Guitar Updating.
    //TODO: used to set speed of i2c transfer. Should be change as your paltform.
    s3c24xx_set_i2c_clockrate( client->adapter, 250, &count );    //set i2c <=250kHz
    dev_dbg( &client->dev, "i2c set frequency:%dkHz.\n", count );
#endif

    ret = goodix_i2c_test( client );
    if( !ret ){
        dev_err( &client->dev, "Warnning: I2C connection might be something wrong!\n" );
        goto err_i2c_failed;
    }

    if( ts->use_shutdown ){
        gpio_set_value( ts->gpio_shutdown, 1 );        //suspend
    }
    
    INIT_WORK( &ts->work, goodix_ts_work_func );
    ts->client = client;
    i2c_set_clientdata( client, ts );
    
    ts->input_dev = input_allocate_device();
    if( !ts->input_dev ){
        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(ABS_PRESSURE);
    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
    // for android
    ts->input_dev->absbit[0] = BIT_MASK(ABS_MT_TRACKING_ID) |
                               BIT_MASK(ABS_MT_TOUCH_MAJOR) |
                               BIT_MASK(ABS_MT_WIDTH_MAJOR) |
                               BIT_MASK(ABS_MT_POSITION_X)  |
                               BIT_MASK(ABS_MT_POSITION_Y);
    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);    
    input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, MAX_FINGER_NUM, 0, 0);    
#endif    

    sprintf( ts->phys, "input/goodix-ts" );
    ts->input_dev->name            = s3c_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    = 0x1105;    

    ret = input_register_device( ts->input_dev );
    if( ret ){
        dev_err( &client->dev,"Unable to register %s input device\n", ts->input_dev->name );
        goto err_input_register_device_failed;
    }

    if( client->irq ){

        ret = gpio_request( ts->gpio_irq, "TS_INT" );    //Request IO
        if( ret < 0 ){
            dev_err( &client->dev, "Failed to request GPIO:%d, ERRNO:%d\n", ts->gpio_irq, ret );
            goto err_int_request_failed;
        }
        
        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( ts->gpio_irq );
            gpio_free( ts->gpio_irq );
            ts->use_irq = false;
            goto err_int_request_failed;
        }
        else{    
            disable_irq( client->irq );
            ts->use_irq = true;
            dev_dbg( &client->dev,"Request EIRQ %d succesd on GPIO:%d\n",client->irq, ts->gpio_irq );
        }
    }
    else{
        ts->use_irq = false;
    }

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 );
    }

    flush_workqueue( goodix_wq );
    if( ts->use_shutdown ){
        gpio_set_value( ts->gpio_shutdown, 0 );    
        ts->power = goodix_ts_power;
        msleep( 30 );
    }    
    
    ret = goodix_init_panel( ts );
    if( !ret ){
        goto err_init_godix_ts;
    }
    
    if( ts->use_irq ){
        enable_irq( client->irq );
    }
    
    goodix_read_version( ts );
    //msleep(260);
    
#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_dbg( &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( client->irq, ts );
        gpio_free( ts->gpio_irq );
    }

err_input_register_device_failed:
    input_free_device( ts->input_dev );

err_input_dev_alloc_failed:
    i2c_set_clientdata( client, NULL );

err_i2c_failed:
    if( ts->use_shutdown ){
        gpio_direction_input( ts->gpio_shutdown );
        gpio_free( ts->gpio_shutdown );
    }

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;
    
    
    ts = i2c_get_clientdata( client );
#ifdef CONFIG_HAS_EARLYSUSPEND
    unregister_early_suspend( &ts->early_suspend );
#endif
    if( ts->use_irq ){
        free_irq( client->irq, ts );    
        gpio_free( ts->gpio_irq );
    }    
    else{
        hrtimer_cancel( &ts->timer );
    }
    
    if( ts->use_shutdown ){
        gpio_direction_input( ts->gpio_shutdown );
        gpio_free( ts->gpio_shutdown );
    }

    dev_notice( &client->dev,"The driver is removing...\n" );
    i2c_set_clientdata( client, NULL );
    input_unregister_device( ts->input_dev );

    if( ts->input_dev ){
        input_free_device( 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;

    /* prevent the interrupt from occuring
     * or stop the high revolution timer
     */
    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 ){        //irq was disabled twice.
        enable_irq( client->irq );
    }
        
    if( ts->power ){

        ret = ts->power( ts,0 );
        if( ret < 0 ){
            dev_warn( &client->dev, "%s power off failed\n", s3c_ts_name );
        }
    }
    return 0;
}

//重新唤醒
static int goodix_ts_resume( struct i2c_client *client )
{
    int ret;
    struct goodix_ts_data *ts;
    
    ts = i2c_get_clientdata( client );
    if( ts->power ){

        ret = ts->power( ts, 1 );
        if( ret < 0 ){
            dev_warn( &client->dev, "%s power on failed\n", s3c_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,
    },
};

//驱动加载函数
//called when insert this module into kernel
static int __devinit goodix_ts_init( void )
{
    int ret;


    goodix_wq = create_singlethread_workqueue( "goodix_wq" );
    if( !goodix_wq ){

        printk( KERN_ALERT "creat %s workqueue failed.\n", s3c_ts_name );
        ret = -ENOMEM;
    }
    else{
        ret = i2c_add_driver( &goodix_ts_driver );
    }
    
    return ret;
}

//驱动卸载函数
//called when remove this module from kernel
static void __exit goodix_ts_exit( void )
{
    if( goodix_wq ){
        destroy_workqueue( goodix_wq );
    }

    i2c_del_driver( &goodix_ts_driver );
}

late_initcall( goodix_ts_init );
module_exit( goodix_ts_exit );

MODULE_DESCRIPTION("Goodix Touchscreen Driver");
MODULE_LICENSE("GPL v2");
----------------------------------------------------------------------------------


4. goodix_touch.h
----------------------------------------------------------------------------------
/*
* include/linux/goodix_touch.h
*
* Copyright (C) 2008 Goodix, Inc.
*
* 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.
*
* Change Date:
*        2010.11.11, add point_queue's definiens.    
*         2011.03.09, rewrite point_queue's definiens.  
*         2011.05.12, delete point_queue for Android 2.2/Android 2.3 and so on.
*         2012.11.13, zoulz modified for tiny210v2 GT801 touchscreen
*/

#ifndef    __LINUX_GOODIX_TOUCH_H__
#define    __LINUX_GOODIX_TOUCH_H__

#include <linux/earlysuspend.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <plat/ts.h>    /*If defined and used platform data */


//#define GOODIX_I2C_NAME "Goodix-TS"
#define GOODIX_I2C_NAME "gt80x-ts"
#define GUITAR_GT80X


//触摸屏的分辨率
#define TOUCH_MAX_HEIGHT     800
#define TOUCH_MAX_WIDTH         480
//显示屏的分辨率,根据具体平台更改,与触摸屏映射坐标相关
#define SCREEN_MAX_HEIGHT    800
#define SCREEN_MAX_WIDTH    480

//this is for S3C6400, not suitable for tiny210
//#define SHUTDOWN_PORT     S3C64XX_GPF(3)            //SHUTDOWN管脚号
//#define INT_PORT          S3C64XX_GPL(10)            //Int IO port
//#define INT_CFG            S3C_GPIO_SFN(3)            //IO configer,EINT type

#define GOODIX_MULTI_TOUCH
#ifndef GOODIX_MULTI_TOUCH
    #define MAX_FINGER_NUM 1
#else
    #define MAX_FINGER_NUM 5                //最大支持手指数(<=5)
#endif

#if defined(INT_PORT)
    #if MAX_FINGER_NUM <= 3
    #define READ_BYTES_NUM 1+2+MAX_FINGER_NUM*5
    #elif MAX_FINGER_NUM == 4
    #define READ_BYTES_NUM 1+28
    #elif MAX_FINGER_NUM == 5
    #define READ_BYTES_NUM 1+34
    #endif
#else    
    #define READ_BYTES_NUM 1+34
#endif

//#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)


#define FLAG_MASK 0x01
enum finger_state {
    FLAG_UP = 0,
    FLAG_DOWN = 1,
    FLAG_INVALID = 2,
};

struct point_node {
    uint8_t                    id;
    //uint8_t                retry;
    enum finger_state        state;
    uint8_t                    pressure;
    unsigned int            x;
    unsigned int            y;
};

struct goodix_ts_data {
    int                        retry;
    int                        panel_type;
    char                    phys[32];        
    struct i2c_client        *client;
    struct input_dev        *input_dev;
    uint8_t                    use_irq;
    uint8_t                    use_shutdown;
    uint32_t                gpio_shutdown;
    uint32_t                 gpio_irq;
    uint32_t                 screen_width;
    uint32_t                 screen_height;
    struct hrtimer            timer;
    struct work_struct        work;
    struct early_suspend    early_suspend;
    int (*power)(struct goodix_ts_data * ts, int on);
};

/* Notice: This definition used by platform_data.
* It should be move this struct info to platform head file such as plat/ts.h.
* If not used in client, it will be NULL in function of goodix_ts_probe.
*/
struct goodix_i2c_platform_data {
    uint32_t gpio_irq;            //IRQ port, use macro such as "gpio_to_irq" to get Interrupt Number.
    uint32_t irq_cfg;            //IRQ port config, must refer to master's Datasheet.
    uint32_t gpio_shutdown;        //Shutdown port number
    uint32_t shutdown_cfg;        //Shutdown port config
    uint32_t screen_width;        //screen width
    uint32_t screen_height;        //screen height
};

#endif /* __LINUX_GOODIX_TOUCH_H__ */
----------------------------------------------------------------------------------

级别: 新手上路
UID: 81493
精华: 0
发帖: 19
金钱: 95 两
威望: 19 点
贡献值: 0 点
综合积分: 38 分
注册时间: 2012-11-09
最后登录: 2013-05-04
1楼  发表于: 2012-11-22 14:07
支持
级别: 新手上路
UID: 64479
精华: 0
发帖: 34
金钱: 180 两
威望: 36 点
贡献值: 0 点
综合积分: 68 分
注册时间: 2012-03-05
最后登录: 2013-03-04
2楼  发表于: 2012-11-22 14:08
这么好的帖子没人顶   
级别: 新手上路
UID: 67420
精华: 0
发帖: 48
金钱: 250 两
威望: 50 点
贡献值: 0 点
综合积分: 96 分
注册时间: 2012-04-11
最后登录: 2013-08-14
3楼  发表于: 2012-11-23 09:53
记得坛子里有位大神说过,现在很多人都不看代码,拿来能用就行了,所以也就造成这种好贴没人顶!
级别: 侠客
UID: 81596
精华: 0
发帖: 61
金钱: 310 两
威望: 62 点
贡献值: 0 点
综合积分: 122 分
注册时间: 2012-11-11
最后登录: 2016-12-26
4楼  发表于: 2012-11-25 22:57
收藏了  
*無鈳取玳
级别: 论坛版主
UID: 27
精华: 12
发帖: 5407
金钱: 40120 两
威望: 17929 点
贡献值: 71 点
综合积分: 11054 分
注册时间: 2008-01-16
最后登录: 2014-11-22
5楼  发表于: 2012-11-27 22:43
支持
"If you have an apple and I have an apple and we exchange apples, then you and I will
still each have one apple. But if you have an idea and I have an idea and we exchange
these ideas, then each of us will have two ideas."
级别: 侠客
UID: 9241
精华: 0
发帖: 62
金钱: 325 两
威望: 71 点
贡献值: 0 点
综合积分: 124 分
注册时间: 2009-09-22
最后登录: 2017-04-03
6楼  发表于: 2012-11-28 09:32
支持!好文,学习了。
级别: 新手上路
UID: 79938
精华: 0
发帖: 40
金钱: 205 两
威望: 41 点
贡献值: 0 点
综合积分: 80 分
注册时间: 2012-10-16
最后登录: 2013-06-04
7楼  发表于: 2013-03-27 15:02
楼主。中断配置这个INT_CFG变量找不到啊!!请问原因!
级别: 侠客
UID: 91531
精华: 1
发帖: 40
金钱: 255 两
威望: 51 点
贡献值: 1 点
综合积分: 100 分
注册时间: 2013-05-11
最后登录: 2014-03-18
8楼  发表于: 2013-06-06 16:20
顶起
级别: 侠客
UID: 87780
精华: 0
发帖: 94
金钱: 470 两
威望: 94 点
贡献值: 0 点
综合积分: 188 分
注册时间: 2013-02-26
最后登录: 2016-07-27
9楼  发表于: 2013-06-17 14:26
支持下,呵呵,准备碰屏了