1. 程式人生 > >電容觸控式螢幕驅動---基於FT5406

電容觸控式螢幕驅動---基於FT5406

最近剛除錯好郭泰的電容觸控式螢幕,現記錄下。

----------------------------------------------------------------------------------------

cpu:s5pv210

touch ic :FT5406

 平臺:android

  1. 首先,分析下 FT5406 的基本電路介面:
    基本都是通用的介面,如 I2C 介面,INT,WAKE,RST。如圖:

    以上可知,我們在驅動中必須定義一箇中斷口,來啟動接收觸控資料,一個gpio腳來複位FT5406。
    wake:主要靠cpu傳送一個喚醒指令給FT5406。
    #define pin_irq     S5PV210_GPH0(1)
    #define pin_rst     S5PV210_GPB(1)
  2. 再次,需確認FT5406的從地址,以便於I2C訪問得到。這個可以根據FT5406資料手冊查詢到.

    可知從地址高位必須為:3,低位必須根據i2ccon設定的值來確定,這點很奇怪。
    我這邊找到的從地址為:0x38
  3. 基本的東西確認好後,剩下的就是根據FT5406資料手冊上的指令,開始寫驅動了。
    在此之前,我們先了解下驅動如何實現電容屏的多點觸控,其實很簡單,主要需要
    觸控式螢幕IC FT5406 能夠捕獲多點資料,這點電容屏基本多能支援到捕獲2點以上,而
    FT5406 可以捕獲5個觸控點,編寫驅動時,只要去獲取這幾個點的資料,然後上報
    就行。格式如圖:




    解釋:
    02h : 捕獲的觸控點個數
    03h- 1eh :對應每個點的x,y座標數值。

----------------------------------------------------------------------------------------------------------------------------------------------

首先配置 i2c_board_info,把從地址提供過去。

//arch/arm/mach-s5pv210/mach-smdk110.c

//i2c_board_info用於構建資訊表來列出存在的I2C裝置。這一資訊用於增長新型I2C驅動的驅動模型樹。對於主機板,它使用i2c_register_board_info()

來靜態建立。對於子板,利用已知的介面卡使用i2c_new_device()動態建立。
//I2C 
裝置建立模板
struct i2c_board_info {
    char type[I2C_NAME_SIZE];  //
晶片型別,用於初始化i2c_client.name
    unsigned short flags;  //
用於初始化i2c_client.flags
    unsigned short addr;  //
儲存於i2c_client.addr
    void *platform_data;  //
儲存於i2c_client.dev.platform_data

    struct dev_archdata *archdata;  //拷貝至i2c_client.dev.archdata
    int irq;  //
儲存於i2c_client.irq
};

static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_TOUCHSCREEN_CDTLCD
{
I2C_BOARD_INFO("ft5x0x_ts", 0x38),
.irq = IRQ_EINT1,
},
#endif 
};

//使用Linux I2C驅動棧,系統可以在初始化時宣告板載資訊表。這些應該在靠近arch_initcall()時的板子相關的初始化程式碼或同等情況時,在I2C介面卡驅動被註冊之前被執行。例如,主機板初始化程式碼可以定義幾個裝置,也可以在疊板的每個子板初始化程式碼中定義。
//I2C
裝置會在相關的匯流排介面卡被註冊後建立。此後,標準驅動模型工具通常繫結新型I2C驅動至I2C裝置。對於使用這一函式宣告的裝置,在動態分配的情況下匯流排號是不可用的。
//
傳遞的板子資訊可以安全的是__initdata,但是由於不能拷貝,要小心嵌入式指標(如platform_data,functions等)
//
靜態的宣告I2C裝置
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);
@busnum: 
指定這些裝置屬於哪個匯流排
@info: I2C
裝置描述符向量
@len: 
向量中描述符的數量;為了預留特定的匯流排號,可以是0

 i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));

在你對應的machine配置裡會執行“i2c_register_board_info”一個函式,它會將一個i2c_board_info的結構體註冊進系統,

可以發現,在目錄/sys/bus/i2c/devices下的裝置就是這個i2c_board_info結構體裡所描述的I2C裝置,

/sys/bus/i2c/devices下的裝置名字就是根據i2c_board_info結構體中定義的I2C Address來命名的。

所以新增一個I2C裝置時,除了需要編寫這個I2C裝置的驅動之外,還需要在machine裡面加入I2C裝置的i2c_board_info內容。

-----------------------------------------------------------------------------------------------------------------------------------------

下面的是驅動的入口函式cdtlcd_ft5406_ts_init(),分幾部分來講解分析:

static int __init cdtlcd_ft5406_ts_init(void)
{
    int ret=0;
    printk(FT5406_DEBUG_LEVEL "%s\n", __func__);
    ret = cdtlcd_i2c_init_hw(); //初始化irq,rst
....

}

// 具體實現
static int cdtlcd_i2c_init_hw(void)
{
    int error =0;
    //init rst pin
    error = gpio_request(pin_rst, "cdtlcd_gpio_rst");
    if(error < 0){
        gpio_free(pin_rst);
        printk(KERN_INFO "request pin_rst error \n");
        return error;
    }
    s3c_gpio_cfgpin(pin_rst, S3C_GPIO_OUTPUT);
    s3c_gpio_setpull(pin_rst,S3C_GPIO_PULL_UP);
    gpio_set_value(pin_rst,1);
    // init int pin
    error = gpio_request(pin_irq,"cdtlcd_gpio_int");
    if(error < 0){
        gpio_free(pin_irq);
        printk(KERN_INFO "request pin_irq error \n");
        return error;
    }
    s3c_gpio_setpull(pin_irq, S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(pin_irq, 0xf);//int
printk(KERN_INFO " hw init success! \n");
return error;
    
}


接下來,初始化devi2c這兩個結構,向這兩個結構賦值0

通過函式init_MUTEX(&i2c.wr_sem1)結構來初始化訊號量,因為在2.6.37版本以上的核心程式碼提供了這個函式來支援訊號量的初始化,其實我們如果看一下這個函式的原型,可以在include/linux/Semaphore.h標頭檔案中看到如下的原型:

#define init_MUTEX(sem) sema_init(sem 1)

也就是這個訊號量的初始化其實呼叫了sema_init(sem1);這個函式。在初始化完訊號量之後,還要通過 i2c.wr_sem.count = 1;來將i2c結構中的訊號量進行讀寫使能。

    // init global variable
    memset(&dev, 0, sizeof(struct dev_data));
    memset(&i2c, 0, sizeof(struct i2c_data));

    //init mutex object
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
    init_MUTEX(&i2c.wr_sem);
#else
    sema_init(&i2c.wr_sem,1);
#endif
    i2c.wr_sem.count = 1;


第2部分:cdtlcd_i2c_register_device註冊i2c裝置,這個是重點註冊函式,這個函式的呼叫是整個驅動載入階段最重要的一個函式,它涉及到後面的i2c驅動的註冊,申請和input子系統的註冊及中斷程式的初始化等等工作。

    //register i2c device
    ret = cdtlcd_i2c_register_device();
    if(ret < 0){
        printk(FT5406_ERROR_LEVEL "%s, init hw error\n",__func__);
        return ret;
    }

具體實現:

//register i2c device
static int cdtlcd_i2c_register_device(void)
{

    int ret = i2c_add_driver(&cdtlcd_i2c_driver);  //為i2c匯流排新增驅動程式方法

    if(ret == 0)
    {
        i2c.valid_i2c_register =1;  //標誌新增i2c驅動方法成功

        printk(FT5406_DEBUG_LEVEL "%s, add i2c device, success\n",__func__);
        if(i2c.client == NULL)
        {
            printk(FT5406_ERROR_LEVEL "%s, no i2c board information\n",__func__);
            return -1;
        }
        printk(FT5406_DEBUG_LEVEL "%s, client.addr: 0x%X\n", __func__, (unsigned int)i2c.client->addr);
printk(FT5406_DEBUG_LEVEL "%s, client.adapter: 0x%X\n", __func__, (unsigned int)i2c.client->adapter);
printk(FT5406_DEBUG_LEVEL "%s, client.driver: 0x%X\n", __func__, (unsigned int)i2c.client->driver);
        if((i2c.client->addr == 0) || (i2c.client->adapter == 0) || (i2c.client->driver == 0)){
printk(FT5406_ERROR_LEVEL "%s, invalid register\n", __func__);
return ret;
}
        //獲取觸控式螢幕IC的引數,如ID,並初始化ft5406暫存器。
        cdtlcd_i2c_read_tp_info();
        //註冊輸入子系統
        cdtlcd_register_input_device();
        //register char device
        cdtlcd_register_char_device();
        
    }
else{
printk(FT5406_ERROR_LEVEL  "%s, add i2c device, error\n", __func__);
return ret;
}
return 0;
}

第一步:先來分析下i2c裝置驅動


// declare i2c function table
static struct i2c_driver cdtlcd_i2c_driver = {
.id_table = cdtlcd_i2c_id, //裝置ID,
.driver = {.name = FT5406_NAME,
               .owner = THIS_MODULE,
    },
//.resume = cdtlcd_i2c_resume,  //i2c裝置喚醒函式,adnroid中有改動,這裡不使用
    //.suspend  = cdtlcd_i2c_suspend,  //i2c使能阻塞函式,adnroid中有改動,這裡不使用

.shutdown = cdtlcd_i2c_shutdown,  //i2c關閉函式

.probe = cdtlcd_i2c_probe,  //在i2c裝置被檢測到的時候進入該函式

.remove = __devexit_p(cdtlcd_i2c_remove), //登出i2c裝置函式
}; 

static const struct i2c_device_id cdtlcd_i2c_id[] = {
{ FT5406_NAME, 0 },{ }
};

我們一個一個的分析這些函式:

我們先分析cdtlcd_i2c_probe()函式,這個函式在驅動程式能夠在載入時或者在熱插拔下檢測到裝置是呼叫的第一個函式:

static int cdtlcd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/*在一個client嘗試訪問I2C介面卡之前,或者在測試介面卡是否支援某一裝置之前,應當首先檢測所需的functionality是否支援*/
if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){
       printk(FT5406_DEBUG_LEVEL "%s, I2C_FUNC_I2C not support\n", __func__);
        return -1;
  }
/*當檢測到的adapter支援這個裝置時,將client賦值到i2c.client中,這樣board中的地址和irq就可以獲取到了*/


i2c.client = client;
    printk(FT5406_DEBUG_LEVEL "%s, i2c new style format\n", __func__);
printk("%s, IRQ: 0x%X\n", __func__, client->irq);
return 0;
}

函式:cdtlcd_i2c_remove():

static int cdtlcd_i2c_remove(struct i2c_client *client)
{
printk(FT5406_DEBUG_LEVEL "%s\n", __func__);
free_irq(client->irq, &i2c);
destroy_workqueue(i2c.irq_work_queue);
input_unregister_device(i2c.input_dev); 
   return 0;
}


函式:cdtlcd_i2c_shutdown()

static void cdtlcd_i2c_shutdown(struct i2c_client *client)
{
  printk(FT5406_DEBUG_LEVEL "%s\n", __func__);
}

-------------------------------------------------------------------------------------------------------------------------------------

第2步,讀取觸控資訊,並初始化:

// read tp info
static int cdtlcd_i2c_read_tp_info(void)
{
    int rom_value,ret;
    unsigned char buf[8]={0};
    ret = cdtlcd_read_reg(FT5X0X_REG_FIRMID,buf);  //讀取 0xa5的韌體ID
    if(ret < 0){
        return -1;
    }
    printk(FT5406_DEBUG_LEVEL "%s , firmware version[%0x h]: 0x%0x\n",__func__,FT5X0X_REG_FIRMID,buf[0]);
    
    ret = cdtlcd_read_reg(FT5X0X_REG_FT5201ID,buf); //讀取ft5406的 ID
    if(ret < 0){
        return -1;
    }
    printk(FT5406_DEBUG_LEVEL "%s , FT5201ID[%0x h]: 0x%0x\n",__func__,FT5X0X_REG_FT5201ID,buf[0]);

    cdtlcd_set_reg(FT5X0X_REG_PMODE,PMODE_ACTIVE);  //設定 0x00h = 0x00,為正常模式
    cdtlcd_read_reg(FT5X0X_REG_PMODE,buf);
    printk("FT5X0X_REG_PMODE=0x%x\n",buf[0]);
    
    cdtlcd_read_reg(0x80,buf);   //讀取ft5406的靈敏度,此IC支援的觸控式螢幕,觸控沒反應基本需要調此引數。
    rom_value = buf[0];
    printk("read 0x80=0x%x\n",buf[0]);
    cdtlcd_read_reg(0x88,buf);   //上報頻率,如何觸控感應太慢,可調節此地址。
    printk("read 0x88=0x%x\n",buf[0]);
    {   
        printk("write para to reg\n");
        cdtlcd_set_reg(0x80,0x10);  //設定 80h 值,s5pv210支援時,數值應小於0x3f,太大了觸控沒反應
        cdtlcd_set_reg(0x88,0x9);
        cdtlcd_read_reg(0x80,buf);
        printk("read 0x80=0x%x\n",buf[0]);
        cdtlcd_read_reg(0x88,buf);
        printk("read 0x88=0x%x\n",buf[0]);
    }
    i2c.event.touch_point = TOUCH_POINT;
    i2c.event.pressure = PRESS_MAX;
    printk(FT5406_DEBUG_LEVEL "%s, max_tp: %d, max_btn: %d\n", __func__,SCREEN_MAX_X ,SCREEN_MAX_Y );
    return ret;
}

第3步:註冊輸入子系統
 在linux系統中,一般的輸入,如:滑鼠,鍵盤,觸控式螢幕等事件型別的輸入,都是使用2.6核心的input子系統機制來進行核心與使用者空間的資料互動。

// register input device
static int cdtlcd_register_input_device(void)
{
    int ret ,key;
    i2c.input_dev = input_allocate_device(); //為input子系統裝置分配記憶體,即進行裝置名的建立

    if(i2c.input_dev == NULL){
printk(FT5406_ERROR_LEVEL "%s, allocate input device, error\n", __func__);
return -1;
}
//告訴input能夠支援哪些事件
    i2c.input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
    i2c.input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    input_set_abs_params(i2c.input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
    input_set_abs_params(i2c.input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
    input_set_abs_params(i2c.input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0 );
    input_set_abs_params(i2c.input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
    input_set_abs_params(i2c.input_dev, ABS_MT_TRACKING_ID, 0, TOUCH_POINT, 0, 0);

/*
for(key=0; key<sizeof(touch_key_code); key++){
        if(touch_key_code[key] <= 0){
            continue;
}
        set_bit(touch_key_code[key] & KEY_MAX, i2c.input_dev->keybit);
}
    */
    i2c.input_dev->name = FT5406_NAME;
i2c.input_dev->id.bustype = BUS_I2C;
i2c.input_dev->dev.parent = &(i2c.client)->dev;
    
    ret = input_register_device(i2c.input_dev);
    if(ret){
        printk(FT5406_ERROR_LEVEL "%s, register input device, error\n", __func__);
        return ret;
    }
    printk(FT5406_DEBUG_LEVEL "%s, register input device, success\n", __func__);
    i2c.valid_input_register = 1;
    /*android 架構的睡眠喚醒*/
#ifdef CONFIG_HAS_EARLYSUSPEND
i2c.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
i2c.early_suspend.suspend = cdtlcd_i2c_early_suspend;
i2c.early_suspend.resume = cdtlcd_i2c_late_resume;
register_early_suspend(&i2c.early_suspend);
#endif

    if(i2c.client->irq !=0)
    {
/*建立一個工作佇列有一個專用的核心執行緒*/


        i2c.irq_work_queue = create_singlethread_workqueue("cdtlcd_i2c_irq_queue");
        if(i2c.irq_work_queue)
        {
            INIT_WORK(&i2c.irq_work, cdtlcd_i2c_irq_work_queue_func);
            init_timer(&i2c.timer);
            i2c.timer.data = (unsigned long)&i2c;
            i2c.timer.function = cdtlcd_i2c_timer;

            if(request_irq(i2c.client->irq, cdtlcd_i2c_irq, IRQF_TRIGGER_LOW, "cdtlcd_i2c_irq", &i2c))
            {
                printk(FT5406_DEBUG_LEVEL "%s, request irq, error\n", __func__);
            }
            else{
i2c.valid_irq_request = 1;
printk(FT5406_DEBUG_LEVEL "%s, request irq, success\n", __func__);
            }
        }

    }
    return 0;
}

在整個驅動程式中要用到input子系統的關鍵是將有中斷髮送過來的觸控式螢幕資料:

主要的執行過程是,在觸控式螢幕引起一箇中斷,中斷函式cdtlcd_i2c_irq()啟動一個工作佇列,執行佇列函式cdtlcd_i2c_irq_work_queue_func(),佇列函式最終呼叫cdtlcd_i2c_process_and_report()函式,將從中斷中收集到的資料資訊報告給input子系統。下面分析觸控式螢幕資料上報函式。

 static irqreturn_t cdtlcd_i2c_irq(int irq, void *dev_id)
{ /*遮蔽中斷,在open時再開啟*/

disable_irq_nosync(i2c.client->irq);
/*當驅動事件被觸發之後,通過queue_work函式進入驅動工作區i2c.irq_work_queue所對應的函式,進行驅動層嚮應用層的資訊上報*/

queue_work(i2c.irq_work_queue, &i2c.irq_work);
    #ifdef DEBUG 
    printk(FT5406_ERROR_LEVEL "%s, irq --------------->\n", __func__);
    #endif
return IRQ_HANDLED;
}

static void cdtlcd_i2c_timer(unsigned long handle)
{
    struct i2c_data *priv = (void *)handle;

    schedule_work(&priv->irq_work); //檢測是否鬆開
    #ifdef DEBUG 
    printk(FT5406_ERROR_LEVEL "%s, timer------------>\n", __func__);
    #endif
}

開始讀取資料,上報x,y座標

static void 
cdtlcd_i2c_irq_work_queue_func(
struct work_struct *work)
{
int ret;
struct i2c_data *priv =  
container_of(work, struct i2c_data, irq_work);

msleep(10);

ret = cdtlcd_i2c_process_and_report();

  if (ret == 0){
   mod_timer(&priv->timer, jiffies + msecs_to_jiffies(0));
}else if (ret == 1){
    enable_irq(i2c.client->irq);
}else if(ret < 0){
  msleep(3000);
  printk(FT5406_ERROR_LEVEL "%s, process error\n", __func__);
  
  enable_irq(i2c.client->irq);
  }
}

 static int 
cdtlcd_i2c_process_and_report(
void)
{
int i, len, ret, x, y;
unsigned char tp_id;
struct input_dev *input = i2c.input_dev;
unsigned char buf[32]={0};
    
    
  // read i2c data from device

//讀取 00-1fh 的地址資料,看上圖
  ret = ft5x0x_i2c_rxdata(buf, 31);

if(ret < 0){
#ifdef DEBUG_MSG
printk(KERN_INFO "ilitek i2c read data error\n");
#endif
return ret;
}

    
#ifdef DEBUG_MSG
for(i=0;i<32;i++)
printk(KERN_INFO "i2c[0x%0x] = 0x%0x\n",i,buf[i]);
#endif

ret = 0; 
    //get valid point

獲取有幾個觸控dian

tp_id = buf[2]& 0x07;
    
#ifdef DEBUG_MSG
        printk(KERN_INFO "tp_point[02h] = %d\n",tp_id);
#endif
    i2c.event.touch_point = tp_id;
    if(tp_id >0) 
    {// parse point 觸控點座標資料等處理分析

    switch (tp_id) {
case 5:
i2c.event.x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
i2c.event.y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
            x = i2c.event.x5;
            y = i2c.event.y5;
描述了從接觸開始到釋放的整個過程的集合
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TRACKING_ID, 5);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_X, x);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_Y, y);

 上報的觸控點的長軸
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);

/*產生一個 SYN_MT_REPORT event來標記一個點的結束,告訴接收方接收當前手指的資訊並準備接收其它手指的觸控資訊*/
    input_mt_sync(i2c.input_dev);
            #ifdef DEBUG_MSG
    printk(KERN_INFO "input press x=%d y%d\n",x,y);
    #endif
        case 4:
i2c.event.x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
i2c.event.y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
             x = i2c.event.x4;
            y = i2c.event.y4;
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TRACKING_ID, 4);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_X, x);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_Y, y);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
    input_mt_sync(i2c.input_dev);
            #ifdef DEBUG_MSG
    printk(KERN_INFO "input press x=%d y%d\n",x,y);
    #endif
case 3:
i2c.event.x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
i2c.event.y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
            x = i2c.event.x3;
            y = i2c.event.y3;
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TRACKING_ID, 3);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_X, x);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_Y, y);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
    input_mt_sync(i2c.input_dev);
            #ifdef DEBUG_MSG
    printk(KERN_INFO "input press x=%d y%d\n",x,y);
    #endif
        case 2:
i2c.event.x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
i2c.event.y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
            x = i2c.event.x2;
            y = i2c.event.y2;
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TRACKING_ID, 2);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_X, x);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_Y, y);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
    input_mt_sync(i2c.input_dev);
            #ifdef DEBUG_MSG
    printk(KERN_INFO "input press x=%d y%d\n",x,y);
    #endif
        case 1:
i2c.event.x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
i2c.event.y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
            x = i2c.event.x1;
            y = i2c.event.y1;
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TRACKING_ID,1);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_X, x);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_POSITION_Y, y);
    input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
    input_mt_sync(i2c.input_dev);
            #ifdef DEBUG_MSG
    printk(KERN_INFO "input press x=%d y%d\n",x,y);
    #endif
            break;
default:
    return -1;
    }
    }
    else{
input_event(i2c.input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
input_mt_sync(i2c.input_dev);
#ifdef DEBUG_MSG
printk(KERN_INFO "realse\n");
#endif
ret = 1; // stop timer interrupt
    }

    input_sync(i2c.input_dev);

return ret;
}


多點觸控上報基本就完成了。

第4步:註冊字元裝置,為後續的ic升級提供應用介面,一般不大使用,這裡提供基本框架。
cdtlcd_register_char_device()

static int cdtlcd_register_char_device(void)
{
    int ret;
// allocate character device driver buffer
ret = alloc_chrdev_region(&dev.devno, 0, 1, FT5406_FILE_DRIVER_NAME);
    if(ret){
        printk(FT5406_DEBUG_LEVEL "%s, can't allocate chrdev\n", __func__);
return ret;
}
    printk(FT5406_DEBUG_LEVEL "%s, register chrdev(%d, %d)\n", __func__, MAJOR(dev.devno), MINOR(dev.devno));
// initialize character device driver
cdev_init(&dev.cdev, &cdtlcd_fops);
dev.cdev.owner = THIS_MODULE;
    ret = cdev_add(&dev.cdev, dev.devno, 1);
    if(ret < 0){
        printk(FT5406_ERROR_LEVEL "%s, add character device error, ret %d\n", __func__, ret);
return ret;

dev.class = class_create(THIS_MODULE, FT5406_FILE_DRIVER_NAME);
if(IS_ERR(dev.class)){
        printk(FT5406_ERROR_LEVEL "%s, create class, error\n", __func__);
return ret;
    }
    device_create(dev.class, NULL, dev.devno, NULL, "ft5x0x_ctrl");
return 0;
}


-----------------------------------------------------------------------------------------------------------------

接下來分析下,android的睡眠喚醒函式。

#ifdef CONFIG_HAS_EARLYSUSPEND
i2c.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
i2c.early_suspend.suspend = cdtlcd_i2c_early_suspend;
i2c.early_suspend.resume = cdtlcd_i2c_late_resume;
register_early_suspend(&i2c.early_suspend);
#endif

static void cdtlcd_i2c_early_suspend(struct early_suspend *h)
{
cdtlcd_i2c_suspend(i2c.client, PMSG_SUSPEND);
printk("%s\n", __func__);
}

static void cdtlcd_i2c_late_resume(struct early_suspend *h)
{
cdtlcd_i2c_resume(i2c.client);
printk("%s\n", __func__);
}
static int 
cdtlcd_i2c_suspend(
struct i2c_client *client, pm_message_t mesg)
{

if(i2c.valid_irq_request != 0){
disable_irq(i2c.client->irq);
                printk(FT5406_DEBUG_LEVEL "%s, disable i2c irq\n", __func__);
}
  
#ifdef CDTLCD_SLEEP_MODE
設定ft5406當前模式為睡眠模式
   int ret = cdtlcd_set_reg(CDTLCD_TP_CMD_SLEEP_MODE,PMODE_HIBERNATE );
if(ret < 0){
printk(FT5406_ERROR_LEVEL "%s, i2c write error, ret %d\n", __func__, ret);
}
#endif
  
return 0;
}

static int 
cdtlcd_i2c_resume(
struct i2c_client *client)
{
  if(i2c.valid_irq_request != 0){
          enable_irq(i2c.client->irq);
          printk(FT5406_DEBUG_LEVEL "%s, enable i2c irq\n", __func__);
  }

    return 0;
}