触摸屏驱动
一.probe函数中处理
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
//若有中断事件,则调用中断处理函数---》调用队列工作--》调用工作函数goodix_ts_work_func
//此处是初始化队列工作,即将ts->work加入到goodix->wq队列中。
INIT_WORK(&ts->work, goodix_ts_work_func);
i2c_set_clientdata(client, ts);
//1.io 端口初始化
ret = gtp_request_io_port(ts);
ret = gtp_i2c_test(client);
ret = gtp_read_version(client, &version_info);
//2.触摸屏初始化调用(每个触摸屏都会由厂商提供初始化代码)
ret = gtp_init_panel(ts);
properties_kobj = kobject_create_and_add("board_properties", NULL);
if (properties_kobj)
//3.gt9xx_properties_attr_group 节点处理虚拟按键(遇到过节点使用前未初始化,造成节点使用错误)
ret = sysfs_create_group(properties_kobj,>9xx_properties_attr_group);
//4.输入设备初始化
gtp_request_input_dev(ts);
//5.中断初始化
ret = gtp_request_irq(ts);
二.深入分析细节
//1.io 端口初始化
void gtp_reset_guitar(struct i2c_client *client, s32 ms)
{
GTP_INFO("Guitar reset");
GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); // begin select I2C slave addr
msleep(ms); // T2: > 10ms
// HIGH: 0x28/0x29, LOW: 0xBA/0xBB
GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14);
msleep(2); // T3: > 100us
GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);
msleep(6); // T4: > 5ms
GTP_GPIO_AS_INPUT(GTP_RST_PORT); // end select I2C slave addr
gtp_int_sync(50);
}
static s8 gtp_request_io_port(struct goodix_ts_data *ts)
{
s32 ret = 0;
//中断端口
ret = GTP_GPIO_REQUEST(GTP_INT_PORT, "GTP_INT_IRQ");
//reset端口
ret = GTP_GPIO_REQUEST(GTP_RST_PORT, "GTP_RST_PORT");
//reset端口设置为输入
GTP_GPIO_AS_INPUT(GTP_RST_PORT);
//选择i2c地址
gtp_reset_guitar(ts->client, 20);
if(ret < 0)
{
GTP_GPIO_FREE(GTP_RST_PORT);
GTP_GPIO_FREE(GTP_INT_PORT);
}
return ret;
}
2.输入设备初始化
/*******************************************************
Function:
Request input device Function.
Input:
ts:private data.
Output:
Executive outcomes.
0: succeed, otherwise: failed.
*******************************************************/
static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
{
s8 ret = -1;
s8 phys[32];
#if GTP_HAVE_TOUCH_KEY
u8 index = 0;
#endif
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL)
{
GTP_ERROR("Failed to allocate input device.");
return -ENOMEM;
}
//设置输入设备属于哪类事件
ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
#if GTP_ICS_SLOT_REPORT
__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
input_mt_init_slots(ts->input_dev, 16); // in case of "out of memory"
#else
__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
#endif
#if GTP_HAVE_TOUCH_KEY
for (index = 0; index < GTP_MAX_KEY_NUM; index++)
{
input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]);
}
#endif
#if GTP_SLIDE_WAKEUP
input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
#endif
#if GTP_WITH_PEN
// pen support
__set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
//__set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
#endif
#if GTP_CHANGE_X2Y
GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
#endif
//设置输入设备:绝对位移事件ABS的具体值
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
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_TRACKING_ID, 0, 255, 0, 0);
sprintf(phys, "input/ts");
ts->input_dev->name = goodix_ts_name;
ts->input_dev->phys = 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 = 10427;
ret = input_register_device(ts->input_dev);
if (ret)
{
GTP_ERROR("Register %s input device failed", ts->input_dev->name);
return -ENODEV;
}
#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
return 0;
}
3.中断初始化
static s8 gtp_request_irq(struct goodix_ts_data *ts)
{
s32 ret = -1;
const u8 irq_table[] = GTP_IRQ_TAB;
GTP_DEBUG_FUNC();
GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type);
//请求中断,并设定中断触发方式
ret = request_irq(ts->client->irq,
goodix_ts_irq_handler,
irq_table[ts->int_trigger_type],
ts->client->name,
ts);
if (ret)
{
GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
GTP_GPIO_AS_INPUT(GTP_INT_PORT);
GTP_GPIO_FREE(GTP_INT_PORT);
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = goodix_ts_timer_handler;
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
return -1;
}
else
{
gtp_irq_disable(ts);
ts->use_irq = 1;
return 0;
}
}
三.当有中断发生时,调用中断处理函数
/*******************************************************
Function:
Timer interrupt service routine for polling mode.
Input:
timer: timer struct pointer
Output:
Timer work mode.
HRTIMER_NORESTART: no restart mode
*********************************************************/
static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
{
struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
GTP_DEBUG_FUNC();
//调用goodix_wq队列中的ts->work工作
queue_work(goodix_wq, &ts->work);
hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
四.调用ts->work的工作函数goodix_ts_work_func
此函数主要是读取手指数及各个手指的坐标,并上报event事件。
static void goodix_ts_work_func(struct work_struct *work)
{
u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
u8 touch_num = 0;
u8 finger = 0;
static u16 pre_touch = 0;
static u8 pre_key = 0;
u8 key_value = 0;
u8* coor_data = NULL;
s32 input_x = 0;
s32 input_y = 0;
s32 input_w = 0;
s32 id = 0;
s32 i = 0;
s32 ret = -1;
struct goodix_ts_data *ts = NULL;
GTP_DEBUG_FUNC();
ts = container_of(work, struct goodix_ts_data, work);
if (ts->enter_update)
{
return;
}
//从触摸屏I2C 读取数据
ret = gtp_i2c_read(ts->client, point_data, 12);
if (ret < 0)
{
GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
goto exit_work_func;
}
finger = point_data[GTP_ADDR_LENGTH];
if((finger & 0x80) == 0)
{
goto exit_work_func;
}
//对手指数的判断
touch_num = finger & 0x0f;
if (touch_num > GTP_MAX_TOUCH)
{
goto exit_work_func;
}
if (touch_num > 1)
{
u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
}
//上报虚拟按键的值(虚拟按键上报有两种方式:1.按键上报,2.坐标上报,所以才有判断)
#if GTP_HAVE_TOUCH_KEY
key_value = point_data[3 + 8 * touch_num];
if(key_value || pre_key)
{
#ifndef VKEY_SYS
for (i = 0; i < GTP_MAX_KEY_NUM; i++)
{
input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i));
}
#else
if (key_value == 1) {
input_x = 80;
input_y = 898;
}else if (key_value == 2) {
input_x = 240;
input_y = 898;
}else if (key_value == 4) {
input_x = 400;
input_y = 898;
}
blocking_notifier_call_chain(&touch_key_notifier, 0, NULL);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
if(key_value) {
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 8);
input_report_key(ts->input_dev, BTN_TOUCH, 1);
//printk("lht--%s--touch key_value=%d----touch_num=%d----down\n", __func__, key_value, touch_num);
}else {
input_report_abs(ts, ABS_MT_TOUCH_MAJOR, 0);
input_report_key(ts->input_dev, BTN_TOUCH, 0);
//printk("lht--%s--touch key_value=%d----touch_num=%d----up\n", __func__, key_value, touch_num);
}
input_mt_sync(ts->input_dev);
#endif
touch_num = 0;
pre_touch = 0;
}
pre_key = key_value;
GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
//printk("lht--%s---touch_num=%d---key_value=%d--\n", __func__, touch_num, key_value);
down_up_flag = touch_num || key_value;
//各个手指坐标的读取
if (touch_num)
{
for (i = 0; i < touch_num; i++)
{
coor_data = &point_data[i * 8 + 3];
id = coor_data[0] & 0x0F;
input_x = coor_data[1] | (coor_data[2] << 8);
input_y = coor_data[3] | (coor_data[4] << 8);
input_w = coor_data[5] | (coor_data[6] << 8);
//上报坐标值及w值
gtp_touch_down(ts, id, input_x, input_y, input_w);
}
}
else if (pre_touch)
{
GTP_DEBUG("Touch Release!");
//释放手指
gtp_touch_up(ts, 0);
}
pre_touch = touch_num;
#endif
input_sync(ts->input_dev);
exit_work_func:
if(!ts->gtp_rawdiff_mode)
{
ret = gtp_i2c_write(ts->client, end_cmd, 3);
if (ret < 0)
{
GTP_INFO("I2C write end_cmd error!");
}
}
if (ts->use_irq)
{
gtp_irq_enable(ts);
}
}
五.上报及释放
static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w)
{
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
input_mt_sync(ts->input_dev);
GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
}
/*******************************************************
Function:
Report touch release event
Input:
ts: goodix i2c_client private data
Output:
None.
*********************************************************/
static void gtp_touch_up(struct goodix_ts_data* ts, s32 id)
{
//input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
//input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
input_mt_sync(ts->input_dev);
}