基于S3c2440的FT5x06 linux触屏驱动源码分析

FT5x06的驱动基于I2c和input驱动模型

  I2C的驱动需要根据具体的ARM芯片,一般来说,IC原厂,一般会将在linux的bsp中都会有I2C的驱动,这个部分不需要我们去写的,我们只需要将FT5X06和BSP包中的I2C驱动匹配起来就好了。而整个FT5X06也是这样做的。根据I2c子系统模型,在linu内核中arch/arm/mach-s3c2440/mach-mini2440.c 关于i2c的一般会有这个数据结构:i2c_board_info()i2c_board_info用于构建信息表来列出存在的I2C设备。这一信息用于增长新型I2C驱动的驱动模型树。然后使用i2c_register_board_info()来静态创建i2c设备驱动。也可以利用已知的适配器使用i2c_new_device()动态创建。

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

先填充 i2c_board_info结构体

 static struct i2c_board_info ft5406-i2c-dev[] __initdata = {

{
I2C_BOARD_INFO("ft5406-ts", 0x38),
.irq = IRQ_EINT14,
},

};

再在mini2440-mach-init中注册 i2c_board_info结构体

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, t5406-i2c-dev, ARRAY_SIZE(t5406-i2c-dev));


注意:

//使用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内容。


然后重新编译内核

make clean

make zImage

然后将重新编译的内核下载进板子中。


先面分析驱动程序源码:


/* drivers/input/touchscreen/ft5x06_i2c_ts.c

 *
 * Copyright (C) 2010 Pixcir, Inc.
 *
 * ft5x06_i2c_ts.c V1.0  support multi touch
 * ft5x06_i2c_ts.c V1.5  add Calibration function:
 *
 * CALIBRATION_FLAG 1
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/slab.h>  


#include <mach/gpio.h>
#include <plat/gpio-cfg.h>


#include <plat/regs-gpio.h>
#include <plat/gpio-bank-k.h>


#include "ft5x06_i2c_ts.h"


#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "qinyisong"
#define DRIVER_DESC "Ft5x06 I2C Touchscreen Driver with tune fuction"
#define DRIVER_LICENSE "GPL"




struct input_dev  *keyboard = NULL;


EXPORT_SYMBOL(keyboard);




#define FT5X06_DEBUG 0


#define SLAVE_ADDR 0x38


#ifndef I2C_MAJOR
#define I2C_MAJOR 125
#endif


#define I2C_MINORS 256


#define  CALIBRATION_FLAG 1




int tsp_keycodes[4] ={
        KEY_F4,
        KEY_F5,
        KEY_F6,
        KEY_F7
};








static unsigned char status_reg = 0;


struct i2c_dev
{
struct list_head list;
struct i2c_adapter *adap;
struct device *dev;
};


static struct i2c_driver ft5x06_i2c_ts_driver;
static struct class *i2c_dev_class;
static LIST_HEAD( i2c_dev_list);
static DEFINE_SPINLOCK( i2c_dev_list_lock);




static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list);
spin_unlock(&i2c_dev_list_lock);
kfree(i2c_dev);
}


static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
i2c_dev = NULL;


spin_lock(&i2c_dev_list_lock);
list_for_each_entry(i2c_dev, &i2c_dev_list, list)
{
if (i2c_dev->adap->nr == index)
goto found;
}
i2c_dev = NULL;
found: spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}


static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;


if (adap->nr >= I2C_MINORS)
{
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
adap->nr);
return ERR_PTR(-ENODEV);
}


i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
i2c_dev->adap = adap;


spin_lock(&i2c_dev_list_lock);
list_add_tail(&i2c_dev->list, &i2c_dev_list);
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
/*********************************V2.0-Bee-0928-BOTTOM****************************************/


static struct workqueue_struct *ft5x06_wq;


struct ft5x06_i2c_ts_data
{
struct i2c_client *client;
struct input_dev *input;
struct delayed_work work;
int irq;
};






static void ft5x06_ts_poscheck(struct work_struct *work)
{
struct ft5x06_i2c_ts_data *tsdata = container_of(work,
struct ft5x06_i2c_ts_data,
work.work);
int touch_event;
int posx1, posy1;
unsigned char Rdbuf[7],auotpnum[1],Wrbuf[1];
static int key_id=0x80;


int i,ret;


Wrbuf[0] = 0;


ret = i2c_master_send(tsdata->client, Wrbuf, 1);

#if FT5X06_DEBUG
printk("master send ret:%d\n",ret);
#endif


if (ret != 1)
{
#if FT5X06_DEBUG
dev_err(&tsdata->client->dev, "Unable to write to i2c touchscreen!\n");
#endif
goto out;
}


ret = i2c_master_recv(tsdata->client, Rdbuf, sizeof(Rdbuf));


if (ret != sizeof(Rdbuf))
{
dev_err(&tsdata->client->dev, "Unable to read i2c page!\n");
goto out;
}


posx1 = (( (Rdbuf[3] & 0x0f )<< 8) | Rdbuf[4]);
posy1 = (( (Rdbuf[5] & 0x0f )<< 8) | Rdbuf[6]);






touch_event = Rdbuf[3] & 0xc0;



if (touch_event == 0x80)
{
if (posy1 > 0 && posy1 < 800) 
{
input_report_abs(tsdata->input, ABS_X, posx1);
input_report_abs(tsdata->input, ABS_Y, posy1);
input_report_key(tsdata->input, BTN_TOUCH, 1);
input_report_abs(tsdata->input, ABS_PRESSURE, 1);
}
else if(posy1>=840 && posy1<=850 && keyboard)
{
if (posx1 > 45 && posx1 < 51)
{
key_id = 0;

}
else if ( posx1 > 205 && posx1 < 211)
{
key_id = 1;
}
else if (posx1 > 269  && posx1 < 275 )
{
key_id = 2;
}
else if ( posx1 > 397 && posx1 < 403)
{
key_id = 3;
}
input_report_key(keyboard, tsp_keycodes[key_id], 1);
}
}
else if (touch_event == 0x40)
{
if(key_id !=0x80  && keyboard)  
{    
i=key_id;
for(i=0;i<4;i++);
input_report_key(keyboard, tsp_keycodes[key_id], 0);
key_id=0x80;
}
input_report_key(tsdata->input, BTN_TOUCH, 0);
input_report_abs(tsdata->input, ABS_PRESSURE, 0);
}


input_sync(tsdata->input);


out: enable_irq(tsdata->irq);


}


static irqreturn_t ft5x06_ts_isr(int irq, void *dev_id)
{
struct ft5x06_i2c_ts_data *tsdata = dev_id;


disable_irq_nosync(irq);
queue_work(ft5x06_wq, &tsdata->work.work);


return IRQ_HANDLED;
}






static int ft5x06_i2c_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{


struct ft5x06_i2c_ts_data *tsdata;
struct input_dev *input;
struct device *dev;
struct i2c_dev *i2c_dev;
int error;
int ret;

/*s3c_gpio_cfgpin(S3C2410_GPG(11), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S3C2410_GPG(11), S3C_GPIO_PULL_UP);
ret = s3c_gpio_setpin(S3C2410_GPG(11),1);
udelay(1000);
ret = s3c_gpio_setpin(S3C2410_GPG(11),0);
udelay(1000);
s3c_gpio_setpin(S3C2410_GPG(11),1);
*/
tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
if (!tsdata)
{
dev_err(&client->dev, "failed to allocate driver data!\n");
error = -ENOMEM;
goto exit_alloc_data_failed;
}


dev_set_drvdata(&client->dev, tsdata);


input = input_allocate_device();
if (!input)
{
dev_err(&client->dev, "failed to allocate input device!\n");
error = -ENOMEM;
goto exit_input_dev_alloc_failed;
}


set_bit(EV_SYN, input->evbit);
set_bit(EV_KEY, input->evbit);
set_bit(EV_ABS, input->evbit);
set_bit(BTN_TOUCH, input->keybit);
input_set_abs_params(input, ABS_X, TOUCHSCREEN_MINX, TOUCHSCREEN_MAXX, 0, 0);
input_set_abs_params(input, ABS_Y, TOUCHSCREEN_MINY, TOUCHSCREEN_MAXY, 0, 0);

input_set_abs_params(input,ABS_PRESSURE,0,1,0,0);


input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;


input_set_drvdata(input, tsdata);


tsdata->client = client;
tsdata->input = input;


INIT_WORK(&tsdata->work.work, ft5x06_ts_poscheck);


tsdata->irq = client->irq;


error = input_register_device(input);
if (error){
dev_err(&client->dev, "failed to register input device!\n");
goto exit_input_register_device_failed;
}


error = request_irq(tsdata->irq, ft5x06_ts_isr, IRQF_TRIGGER_FALLING,client->name, tsdata);
if (error)
{
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
goto exit_irq_request_failed;
}


device_init_wakeup(&client->dev, 1);


/*********************************V2.0-Bee-0928-TOP****************************************/
i2c_dev = get_free_i2c_dev(client->adapter);
if (IS_ERR(i2c_dev))
{
error = PTR_ERR(i2c_dev);
goto  out;
}
/*********************************V2.0-Bee-0928-BOTTOM****************************************/
dev_err(&tsdata->client->dev, "insmod successfully!\n");
return 0;

out:
free_irq(tsdata->irq, tsdata);
exit_irq_request_failed:
exit_input_register_device_failed:
input_free_device(input);
exit_input_dev_alloc_failed:
kfree(tsdata);
exit_alloc_data_failed:
return error;


}


static int ft5x06_i2c_ts_remove(struct i2c_client *client)
{
int error;
struct i2c_dev *i2c_dev;
struct ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(&client->dev);
free_irq(tsdata->irq, tsdata);
/*********************************V2.0-Bee-0928-TOP****************************************/
i2c_dev = get_free_i2c_dev(client->adapter);
if (IS_ERR(i2c_dev))
{
error = PTR_ERR(i2c_dev);
return error;
}
return_i2c_dev(i2c_dev);
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, client->adapter->nr));
/*********************************V2.0-Bee-0928-BOTTOM****************************************/
input_unregister_device(tsdata->input);
kfree(tsdata);
dev_set_drvdata(&client->dev, NULL);
return 0;
}


static int ft5x06_i2c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(&client->dev);

disable_irq(tsdata->irq);

return 0;
}


static int ft5x06_i2c_ts_resume(struct i2c_client *client)
{
struct ft5x06_i2c_ts_data *tsdata = dev_get_drvdata(&client->dev);

enable_irq(tsdata->irq);


return 0;
}






static const struct i2c_device_id ft5x06_i2c_ts_id[] =
{
{ "ft5406_ts", 0 },
{ }
};
MODULE_DEVICE_TABLE( i2c, ft5x06_i2c_ts_id);


static struct i2c_driver ft5x06_i2c_ts_driver =
{ .driver =
{
.owner = THIS_MODULE,
.name = "ft5x06_i2c_ts_driver",
}, 
.probe = ft5x06_i2c_ts_probe, 
.remove = ft5x06_i2c_ts_remove,
.suspend = ft5x06_i2c_ts_suspend, 
.resume = ft5x06_i2c_ts_resume,
.id_table = ft5x06_i2c_ts_id, 
};


static int __init ft5x06_i2c_ts_init(void)
{
int ret;

ft5x06_wq = create_singlethread_workqueue("ft5x06_wq");
if(!ft5x06_wq)
return -ENOMEM;



i2c_dev_class = class_create(THIS_MODULE, "ft5x06_i2c_dev");
if (IS_ERR(i2c_dev_class))
{
ret = PTR_ERR(i2c_dev_class);
class_destroy(i2c_dev_class);
}
/********************************V2.0-Bee-0928-BOTTOM******************************************/
return i2c_add_driver(&ft5x06_i2c_ts_driver);
}


static void __exit ft5x06_i2c_ts_exit(void)
{
i2c_del_driver(&ft5x06_i2c_ts_driver);
/********************************V2.0-Bee-0928-TOP******************************************/
class_destroy(i2c_dev_class);
unregister_chrdev(I2C_MAJOR,"ft5x06_i2c_ts");
/********************************V2.0-Bee-0928-BOTTOM******************************************/
if(ft5x06_wq)
destroy_workqueue(ft5x06_wq);
}


MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);


module_init( ft5x06_i2c_ts_init);

module_exit( ft5x06_i2c_ts_exit);




将触屏驱动编译成内核模块,下载到板上。

insmod 内核模块

在/dev/input会出现event1

修改/etc/profile中的配置,将系统的标准输入改为event1,再用tslib校准触屏。

嵌入式设备中触摸屏使用非常广泛,但触摸屏的坐标和屏的坐标是不对称的,需要校准。校准广泛使用的是开源的tslib。
Tslib是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。

tslib的具体移植步骤参考我的另一篇博文http://blog.csdn.net/xiaoaid01/article/details/39251209








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值