tiny4412電容屏幕觸摸屏驅動-ft5406芯片

準備開發tiny4412板子,也需要電容觸摸屏ft5406 芯片,無意中看到下面這篇文章,很有參考價值,所以轉載過來呵呵。

原文地址 http://blog.csdn.net/gooogleman/article/details/7574591


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



  1. <pre class="cpp" name="code"><pre><span style="color:#000000;"></span>  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章