前言
前面簡單聊了一下多點觸控協(xié)議,接下來找個驅(qū)動來看看具體實現(xiàn)。目前市面上多點觸控芯片用得比較多的主要是匯頂和敦泰。我們找一款敦泰的芯片來看看。
多點觸控驅(qū)動分析
Linux版本:5.10
芯片:FT5436 (10點觸控芯片)
(1)加載和卸載函數(shù)
static const struct i2c_device_id fts_ts_id[] = { {FTS_DRIVER_NAME, 0}, {}, }; //設(shè)備樹匹配 static const struct of_device_id fts_dt_match[] = { {.compatible = "focaltech,ft5436", }, {}, }; MODULE_DEVICE_TABLE(of, fts_dt_match); static struct i2c_driver fts_ts_driver = { .probe = fts_ts_probe, .remove = fts_ts_remove, .driver = { .name = FTS_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(fts_dt_match), }, .id_table = fts_ts_id, }; static int __init fts_ts_init(void) { int ret = 0; FTS_FUNC_ENTER(); //添加i2c設(shè)備驅(qū)動 ret = i2c_add_driver(&fts_ts_driver); if ( ret != 0 ) { FTS_ERROR("Focaltech touch screen driver init failed!"); } FTS_FUNC_EXIT(); return ret; } static void __exit fts_ts_exit(void) { i2c_del_driver(&fts_ts_driver); } module_init(fts_ts_init); module_exit(fts_ts_exit);
FT5436使用I2C接口進行通信,所以注冊i2c_driver。
(2)probe()函數(shù)
static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct fts_ts_data *ts_data = NULL;
FTS_INFO("Touch Screen(I2C BUS) driver prboe...");
//檢查I2C功能
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
FTS_ERROR("I2C not supported");
return -ENODEV;
}
//為結(jié)構(gòu)體分配內(nèi)存
ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL);
if (!ts_data) {
FTS_ERROR("allocate memory for fts_data fail");
return -ENOMEM;
}
//保存數(shù)據(jù),方便后面使用
fts_data = ts_data;
ts_data->client = client; //i2c設(shè)備
ts_data->dev = &client->dev;
ts_data->log_level = 1;
ts_data->fw_is_running = 0;
i2c_set_clientdata(client, ts_data);
//進入真正的probe流程
ret = fts_ts_probe_entry(ts_data);
//......
FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully");
return 0;
}
初始化結(jié)構(gòu)體,保存i2c設(shè)備,然后調(diào)用fts_ts_probe_entry進入設(shè)備的初始化。
static int fts_ts_probe_entry(struct fts_ts_data *ts_data)
{
int ret = 0;
int pdata_size = sizeof(struct fts_ts_platform_data);
struct device_node *np = ts_data->dev->of_node;
FTS_FUNC_ENTER();
FTS_INFO("%s", FTS_DRIVER_VERSION);
ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL);
if (!ts_data->pdata) {
FTS_ERROR("allocate memory for platform_data fail");
return -ENOMEM;
}
//獲取平臺數(shù)據(jù)
if (ts_data->dev->of_node) {
//dts解析
ret = fts_parse_dt(ts_data->dev, ts_data->pdata);
if (ret)
FTS_ERROR("device-tree parse fail");
} else {
if (ts_data->dev->platform_data) {
memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size);
} else {
FTS_ERROR("platform_data is null");
return -ENODEV;
}
}
//創(chuàng)建工作隊列
ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq");
//.....
//在輸入子系統(tǒng)中注冊設(shè)備
ret = fts_input_init(ts_data);
if (ret) {
FTS_ERROR("input initialize fail");
goto err_input_init;
}
//......
//gpio配置(中斷,復(fù)位)
ret = fts_gpio_configure(ts_data);
if (ret) {
FTS_ERROR("configure the gpios fail");
goto err_gpio_config;
}
//.....
//創(chuàng)建proc調(diào)試接口
ret = fts_create_apk_debug_channel(ts_data);
if (ret) {
FTS_ERROR("create apk debug node fail");
}
//創(chuàng)建sys調(diào)試接口
ret = fts_create_sysfs(ts_data);
if (ret) {
FTS_ERROR("create sysfs node fail");
}
#if FTS_POINT_REPORT_CHECK_EN
//初始化work(用于處理中斷數(shù)據(jù))
ret = fts_point_report_check_init(ts_data);
if (ret) {
FTS_ERROR("init point report check fail");
}
#endif
//.....
//注冊中斷
ret = fts_irq_registration(ts_data);
//.....
//初始化固件更新功能(用于為Touch模組更新固件)
ret = fts_fwupg_init(ts_data);
if (ret) {
FTS_ERROR("init fw upgrade fail");
}
//配置休眠喚醒
#if defined(CONFIG_FB)
if (ts_data->ts_workqueue) {
INIT_WORK(&ts_data->resume_work, fts_resume_work);
}
ts_data->tp.tp_resume = fts_ts_late_resume;
ts_data->tp.tp_suspend = fts_ts_early_suspend;
//通過LCD屏亮滅來決定喚醒
tp_register_fb(&ts_data->tp);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
ts_data->early_suspend.suspend = fts_ts_early_suspend;
ts_data->early_suspend.resume = fts_ts_late_resume;
register_early_suspend(&ts_data->early_suspend);
#endif
//使能中斷喚醒
if (of_property_read_bool(np, "wakeup-source"))
{
device_init_wakeup(&ts_data->client->dev, 1);
enable_irq_wake(ts_data->irq);
}
FTS_FUNC_EXIT();
return 0;
//......
}
上面主要完成如下工作:
1.解析dts
2.創(chuàng)建工作隊列(處理中斷下半部)
3.注冊輸入子系統(tǒng)設(shè)備
4.初始化GPIO(中斷和復(fù)位)
5.創(chuàng)建proc和sys調(diào)試接口
6.注冊中斷
7.初始化固件更新功能(用于升級Touch芯片上的固件)
8.配置休眠喚醒
(3)注冊輸入設(shè)備
static int fts_input_init(struct fts_ts_data *ts_data)
{
int ret = 0;
int key_num = 0;
struct fts_ts_platform_data *pdata = ts_data->pdata;
struct input_dev *input_dev;
FTS_FUNC_ENTER();
//分配輸入設(shè)備(input_dev)
input_dev = input_allocate_device();
if (!input_dev) {
FTS_ERROR("Failed to allocate memory for input device");
return -ENOMEM;
}
/* Init and register Input device */
input_dev->name = FTS_DRIVER_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = ts_data->dev;
input_set_drvdata(input_dev, ts_data);
__set_bit(EV_SYN, input_dev->evbit); //同步事件
__set_bit(EV_ABS, input_dev->evbit); //絕對坐標(biāo)事件(比如X,Y坐標(biāo)信息)
__set_bit(EV_KEY, input_dev->evbit); //按鍵事件
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
if (pdata->have_key) {
FTS_INFO("set key capabilities");
for (key_num = 0; key_num < pdata->key_number; key_num++)
input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]);
}
#if FTS_MT_PROTOCOL_B_EN
//Type B協(xié)議
//初始化SLOT(最大觸點數(shù))
input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT);
#else
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0);
#endif
//設(shè)置X,Y,接觸軸
input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
#if FTS_REPORT_PRESSURE_EN
//設(shè)置壓力值
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
#endif
//注冊輸入設(shè)備
ret = input_register_device(input_dev);
//.....
ts_data->input_dev = input_dev;
FTS_FUNC_EXIT();
return 0;
}
設(shè)置支持的輸入事件,然后注冊到輸入子系統(tǒng)中。
(4)中斷處理
static irqreturn_t fts_irq_handler(int irq, void *data)
{
struct fts_ts_data *ts_data = (struct fts_ts_data *)data;
if (!ts_data) {
FTS_ERROR("[INTR]: Invalid fts_ts_data");
return IRQ_HANDLED;
}
if (device_can_wakeup(&ts_data->client->dev))
pm_stay_awake(&ts_data->client->dev);
//讀取數(shù)據(jù)
fts_irq_read_report();
if (device_can_wakeup(&ts_data->client->dev))
pm_relax(&ts_data->client->dev);
return IRQ_HANDLED;
}
static void fts_irq_read_report(void)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
//.....
#if FTS_POINT_REPORT_CHECK_EN
//添加work到工作隊列中(延遲)
fts_prc_queue_work(ts_data);
#endif
//讀取touch數(shù)據(jù)
ret = fts_read_parse_touchdata(ts_data);
if (ret == 0) {
mutex_lock(&ts_data->report_mutex);
//上報數(shù)據(jù)
#if FTS_MT_PROTOCOL_B_EN
fts_input_report_b(ts_data); //Type B
#else
fts_input_report_a(ts_data); //Type A
#endif
mutex_unlock(&ts_data->report_mutex);
}
//......
}
讀取touch數(shù)據(jù)并解析,然后進行事件上報。
(5)讀取和解析數(shù)據(jù)
static int fts_read_parse_touchdata(struct fts_ts_data *data)
{
int ret = 0;
int i = 0;
u8 pointid = 0;
int base = 0;
struct ts_event *events = data->events;
int max_touch_num = data->pdata->max_touch_number;
u8 *buf = data->point_buf;
//通過I2C讀取芯片數(shù)據(jù)
ret = fts_read_touchdata(data);
if (ret) {
return ret;
}
//對讀取到的數(shù)據(jù)進行解析
data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
data->touch_point = 0;
if (data->ic_info.is_incell) {
if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF)
&& (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) {
FTS_DEBUG("touch buff is 0xff, need recovery state");
fts_release_all_finger();
fts_tp_state_recovery(data);
return -EIO;
}
}
//.....
for (i = 0; i < max_touch_num; i++) {
base = FTS_ONE_TCH_LEN * i;
pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4;
if (pointid >= FTS_MAX_ID)
break;
else if (pointid >= max_touch_num) {
FTS_ERROR("ID(%d) beyond max_touch_number", pointid);
return -EINVAL;
}
data->touch_point++;
events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) +
(buf[FTS_TOUCH_X_L_POS + base] & 0xFF);
events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) +
(buf[FTS_TOUCH_Y_L_POS + base] & 0xFF);
events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6;
events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4;
events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4;
events[i].p = buf[FTS_TOUCH_PRE_POS + base];
if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) {
FTS_INFO("abnormal touch data from fw");
return -EIO;
}
}
if (data->touch_point == 0) {
FTS_INFO("no touch point information");
return -EIO;
}
return 0;
}
fts_read_touchdata()從芯片中讀出數(shù)據(jù),然后對數(shù)據(jù)進行解析。
(6)事件上報
static int fts_input_report_b(struct fts_ts_data *data)
{
int i = 0;
int uppoint = 0;
int touchs = 0;
bool va_reported = false;
u32 max_touch_num = data->pdata->max_touch_number;
struct ts_event *events = data->events;
for (i = 0; i < data->touch_point; i++) {
if (fts_input_report_key(data, i) == 0) {
continue;
}
va_reported = true;
//上報ABS_MT_SLOT事件
input_mt_slot(data->input_dev, events[i].id);
//按下/抬起手指
if (EVENT_DOWN(events[i].flag)) {
//上報手指按下和坐標(biāo)等信息
//使用MT_TOOL_FINGER來確定按下和抬起,
//就不需要使用ABS_MT_TRACKING_ID來控制觸點的生命周期了
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
#if FTS_REPORT_PRESSURE_EN
if (events[i].p <= 0) {
events[i].p = 0x3f;
}
//上報壓力值
input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p);
#endif
if (events[i].area <= 0) {
events[i].area = 0x09;
}
//上報觸點的主軸長度
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
//上報X,Y坐標(biāo)
input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
touchs |= BIT(events[i].id);
data->touchs |= BIT(events[i].id);
//......
} else {
uppoint++;
//上報手指抬起
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
data->touchs &= ~BIT(events[i].id);
if (data->log_level >= 1) {
FTS_DEBUG("[B]P%d UP!", events[i].id);
}
}
}
if (unlikely(data->touchs ^ touchs)) {
for (i = 0; i < max_touch_num; i++) {
if (BIT(i) & (data->touchs ^ touchs)) {
if (data->log_level >= 1) {
FTS_DEBUG("[B]P%d UP!", i);
}
va_reported = true;
input_mt_slot(data->input_dev, i);
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
}
}
}
data->touchs = touchs;
//上報Touch按鍵事件
if (va_reported) {
/* touchs==0, there's no point but key */
if (EVENT_NO_DOWN(data) || (!touchs)) {
//所有觸點都抬起了
if (data->log_level >= 1) {
FTS_DEBUG("[B]Points All Up!");
}
input_report_key(data->input_dev, BTN_TOUCH, 0);
} else {
input_report_key(data->input_dev, BTN_TOUCH, 1);
}
}
//同步(告訴上層本次上報結(jié)束)
input_sync(data->input_dev);
return 0;
}
上報各種事件(MT_TOOL_FINGER/ABS_MT_POSITION_X/ABS_MT_POSITION_Y等 )給上層。
總結(jié)
整體分析下來,會發(fā)現(xiàn)多點觸控驅(qū)動并不難,主要就是注冊為輸入子系統(tǒng),然后中斷觸發(fā)后讀取觸控數(shù)據(jù),最后通過輸入子系統(tǒng)上報數(shù)據(jù)。所有輸入子系統(tǒng)的驅(qū)動基本都是這個套路。
審核編輯:劉清
-
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
317瀏覽量
22920 -
I2C接口
+關(guān)注
關(guān)注
1文章
149瀏覽量
26357 -
觸控芯片
+關(guān)注
關(guān)注
2文章
89瀏覽量
22232 -
FFTs
+關(guān)注
關(guān)注
0文章
2瀏覽量
5517
原文標(biāo)題:Linux驅(qū)動分析之多點觸控驅(qū)動
文章出處:【微信號:嵌入式軟件開發(fā)交流,微信公眾號:嵌入式軟件開發(fā)交流】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
聊一下IGBT驅(qū)動中的參考電位問題
什么是多點觸控技術(shù)?多點觸控是怎么實現(xiàn)的?
多點觸控技術(shù)的全解析
單點觸控和多點觸控區(qū)別是什么?原理分析
多點懸浮觸控技術(shù)將掀起一場新革命
多點觸控應(yīng)用的模擬多點觸控模擬環(huán)境搭建
Bada系統(tǒng)學(xué)習(xí)-使用多點觸控
簡單聊一聊DPT技術(shù)-double pattern technology
聊一聊芯片設(shè)計的NDR是什么?
多點觸控和單點觸控區(qū)別
多點觸控和單點觸控哪個好
多點觸控是觸摸屏嗎_多點觸控功能
分享一下多點電極液位開關(guān)的特點與優(yōu)勢

簡單聊一下多點觸控協(xié)議
評論