触摸屏驱动总结

在公司实习时间快结束了,这段时间接触最多的就是触摸屏了,现在以gt9xx系列触摸屏总结下对于触摸屏的理解,当然首先分析源代码:

首先从probe函数开始

probe函数中比较重要的就是ret = gtp_init_panel(ts);

和ret = gtp_request_input_dev(ts);


这两个函数一个是将触摸屏的配置信息写入到触摸屏寄存器中,一个是申请input子系统

static int gtp_init_panel(struct goodix_ts_data *ts)
{
	struct i2c_client *client = ts->client;
	unsigned char *config_data;
	int ret = -EIO;

#if GTP_DRIVER_SEND_CFG
	int i;
	u8 check_sum = 0;
	u8 opr_buf[16];
	u8 sensor_id = 0;

	u8 cfg_info_group1[] = CTP_CFG_GROUP1;
	u8 cfg_info_group2[] = CTP_CFG_GROUP2;
	u8 cfg_info_group3[] = CTP_CFG_GROUP3;
	u8 cfg_info_group4[] = CTP_CFG_GROUP4;
	u8 cfg_info_group5[] = CTP_CFG_GROUP5;
	u8 cfg_info_group6[] = CTP_CFG_GROUP6;
	u8 cfg_info_group7[] = CTP_CFG_GROUP7;
	u8 cfg_info_group8[] = CTP_CFG_GROUP8;
	u8 cfg_info_group9[] = CTP_CFG_GROUP9;
	u8 cfg_info_group10[] = CTP_CFG_GROUP10;
	u8 cfg_info_group11[] = CTP_CFG_GROUP11;
	u8 cfg_info_group12[] = CTP_CFG_GROUP12;
	u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2,cfg_info_group3, cfg_info_group4,
		cfg_info_group5, cfg_info_group6,cfg_info_group7, cfg_info_group8,
		cfg_info_group9, cfg_info_group10,
		cfg_info_group11, cfg_info_group12};

	u8 cfg_info_len[] = {CFG_GROUP_LEN(cfg_info_group1),
		CFG_GROUP_LEN(cfg_info_group2),
		CFG_GROUP_LEN(cfg_info_group3),
		CFG_GROUP_LEN(cfg_info_group4),
		CFG_GROUP_LEN(cfg_info_group5),
		CFG_GROUP_LEN(cfg_info_group6),
		CFG_GROUP_LEN(cfg_info_group7),
		CFG_GROUP_LEN(cfg_info_group8),
		CFG_GROUP_LEN(cfg_info_group9),
		CFG_GROUP_LEN(cfg_info_group10),
		CFG_GROUP_LEN(cfg_info_group11),
		CFG_GROUP_LEN(cfg_info_group12),};

	lidbg("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
			cfg_info_len[0], cfg_info_len[1], cfg_info_len[2],
			cfg_info_len[3], cfg_info_len[4], cfg_info_len[5]);

	ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
	if (SUCCESS == ret) {
		if (opr_buf[0] != 0xBE) {
			ts->fw_error = 1;
			dev_err(&client->dev,
				"Firmware error, no config sent!");
			return -EINVAL;
		}
	}
	if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3])
		&& (!cfg_info_len[4]) && (!cfg_info_len[5])) {
		sensor_id = 0;
	} else {
		ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
			&sensor_id, 1);
		if (SUCCESS == ret) {
			if (sensor_id >= 0x06) {
				dev_err(&client->dev,
					"Invalid sensor_id(0x%02X), No Config Sent!",
					sensor_id);
				return -EINVAL;
			}
		} else {
			dev_err(&client->dev,
				"Failed to get sensor_id, No config sent!");
			return -EINVAL;
		}
	}
	lidbg("Sensor_ID: %d", sensor_id);


	ret = lidbg_check_id(ts->client,"9271");
	if(SUCCESS == ret)
		{
			sensor_id+=6;
		}



    lidbg_fs_log(TS_LOG_PATH, "SENSOR ID:%d\n", sensor_id);
	ts->gtp_cfg_len = cfg_info_len[sensor_id];

	if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) {
		dev_err(&client->dev,
				"Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!\n",
				sensor_id);
		return -EINVAL;
	}
	ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
		&opr_buf[0], 1);

	if (ret == SUCCESS) {
		if (opr_buf[0] < 90) {
			/* backup group config version */
			grp_cfg_version = send_cfg_buf[sensor_id][0];
			send_cfg_buf[sensor_id][0] = 0x00;
			ts->fixed_cfg = 0;
		} else {
			/* treated as fixed config, not send config */
			dev_warn(&client->dev,
				"Ic fixed config with config version(%d, 0x%02X)",
				opr_buf[0], opr_buf[0]);
			ts->fixed_cfg = 1;
		}
	} else {
		dev_err(&client->dev,
			"Failed to get ic config version!No config sent!");
		return -EINVAL;
	}
/*
	if (ts->pdata->gtp_cfg_len) {// use config from dts
		config_data = ts->pdata->config_data;
		ts->config_data = ts->pdata->config_data;
		ts->gtp_cfg_len = ts->pdata->gtp_cfg_len;
	} else*/
	{
		config_data = devm_kzalloc(&client->dev,
			GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
				GFP_KERNEL);
		if (!config_data) {
			dev_err(&client->dev,
					"Not enough memory for panel config data\n");
			return -ENOMEM;
		}

		ts->config_data = config_data;
		config_data[0] = GTP_REG_CONFIG_DATA >> 8;
		config_data[1] = GTP_REG_CONFIG_DATA & 0xff;
		memset(&config_data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
		memcpy(&config_data[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id],
				ts->gtp_cfg_len);
	}

#if GTP_CUSTOM_CFG
	config_data[RESOLUTION_LOC] =
	(unsigned char)(GTP_MAX_WIDTH && 0xFF);
	config_data[RESOLUTION_LOC + 1] =
	(unsigned char)(GTP_MAX_WIDTH >> 8);
	config_data[RESOLUTION_LOC + 2] =
	(unsigned char)(GTP_MAX_HEIGHT && 0xFF);
	config_data[RESOLUTION_LOC + 3] =
	(unsigned char)(GTP_MAX_HEIGHT >> 8);

	if (GTP_INT_TRIGGER == 0)
		config_data[TRIGGER_LOC] &= 0xfe;
	else if (GTP_INT_TRIGGER == 1)
		config_data[TRIGGER_LOC] |= 0x01;
#endif  /* !GTP_CUSTOM_CFG */

	check_sum = 0;
	for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
		check_sum += config_data[i];

	config_data[ts->gtp_cfg_len] = (~check_sum) + 1;

#else /* DRIVER NOT SEND CONFIG */
	ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
	ret = gtp_i2c_read(ts->client, config_data,
			ts->gtp_cfg_len + GTP_ADDR_LENGTH);
	if (ret < 0) {
		dev_err(&client->dev,
				"Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n");
		ts->abs_x_max = GTP_MAX_WIDTH;
		ts->abs_y_max = GTP_MAX_HEIGHT;
		ts->int_trigger_type = GTP_INT_TRIGGER;
	}
#endif /* !DRIVER NOT SEND CONFIG */

	GTP_DEBUG_FUNC();
	if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
		ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
				+ config_data[RESOLUTION_LOC];
		ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8)
				+ config_data[RESOLUTION_LOC + 2];
		ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03;
	}
	ret = gtp_send_cfg(ts);
	if (ret < 0)
		dev_err(&client->dev, "%s: Send config error.\n", __func__);

	GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
			ts->abs_x_max, ts->abs_y_max,
			ts->int_trigger_type);

	msleep(20);
	return ret;
其中
memset(&config_data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
memcpy(&config_data[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id],
				ts->gtp_cfg_len);

作用就是将配置选择的配置信息介入到寄存器中,寄存器地址存储在config_data中

config_data[0] = GTP_REG_CONFIG_DATA >> 8;
config_data[1] = GTP_REG_CONFIG_DATA & 0xff;

GTP_REG_CONFIG_DATA 为配置寄存器首地址

gtp_request_input_dev则为申请input子系统,这里我没有用系统原来带的gtp_request_input_dev而是自己重新封装了一个接口这个后面再说把

申请万input子系统后最重要的事就是创建工作队列作为触摸屏中断底半部:

ts->goodix_wq = create_singlethread_workqueue("goodix_wq");
INIT_WORK(&ts->work, goodix_ts_work_func);

所以中断进来首先进入中断函数,最终执行中断底半部,也就是
goodix_ts_work_func
函数如下:

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;
	static u8 pre_key;
#if GTP_WITH_PEN
	static u8 pre_pen;
#endif
	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;
	int ret = -1;
	struct goodix_ts_data *ts = NULL;

#if GTP_SLIDE_WAKEUP
	u8 doze_buf[3] = {0x81, 0x4B};
#endif

	GTP_DEBUG_FUNC();

	ts = container_of(work, struct goodix_ts_data, work);
#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
	if (ts->enter_update)
		return;
#endif

#if GTP_SLIDE_WAKEUP
	if (DOZE_ENABLED == doze_status) {
		ret = gtp_i2c_read(ts->client, doze_buf, 3);
		GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
		if (ret > 0) {
			if (doze_buf[2] == 0xAA) {
				dev_dbg(&ts->client->dev,
					"Slide(0xAA) To Light up the screen!");
				doze_status = DOZE_WAKEUP;
				input_report_key(
					ts->input_dev, KEY_POWER, 1);
				input_sync(ts->input_dev);
				input_report_key(
					ts->input_dev, KEY_POWER, 0);
				input_sync(ts->input_dev);
				/* clear 0x814B */
				doze_buf[2] = 0x00;
				gtp_i2c_write(ts->client, doze_buf, 3);
			} else if (doze_buf[2] == 0xBB) {
				dev_dbg(&ts->client->dev,
					"Slide(0xBB) To Light up the screen!");
				doze_status = DOZE_WAKEUP;
				input_report_key(ts->input_dev, KEY_POWER, 1);
				input_sync(ts->input_dev);
				input_report_key(ts->input_dev, KEY_POWER, 0);
				input_sync(ts->input_dev);
				/* clear 0x814B*/
				doze_buf[2] = 0x00;
				gtp_i2c_write(ts->client, doze_buf, 3);
			} else if (0xC0 == (doze_buf[2] & 0xC0)) {
				dev_dbg(&ts->client->dev,
					"double click to light up the screen!");
				doze_status = DOZE_WAKEUP;
				input_report_key(ts->input_dev, KEY_POWER, 1);
				input_sync(ts->input_dev);
				input_report_key(ts->input_dev, KEY_POWER, 0);
				input_sync(ts->input_dev);
				/* clear 0x814B */
				doze_buf[2] = 0x00;
				gtp_i2c_write(ts->client, doze_buf, 3);
			} else {
				gtp_enter_doze(ts);
			}
		}
		if (ts->use_irq)
			gtp_irq_enable(ts);

		return;
	}
#endif

	ret = gtp_i2c_read(ts->client, point_data, 12);
	if (ret < 0) {
		dev_err(&ts->client->dev,
				"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));
	}

#if GTP_HAVE_TOUCH_KEY
	key_value = point_data[3 + 8 * touch_num];

	if (key_value || pre_key) {
		for (i = 0; i < GTP_MAX_KEY_NUM; i++) {
#if GTP_DEBUG_ON
			for (ret = 0; ret < 4; ++ret) {
				if (key_codes[ret] == touch_key_array[i]) {
					GTP_DEBUG("Key: %s %s",
						key_names[ret],
						(key_value & (0x01 << i))
						? "Down" : "Up");
					break;
				}
			}
#endif

			input_report_key(ts->input_dev,
				touch_key_array[i], key_value & (0x01<<i));
		}
		touch_num = 0;
		pre_touch = 0;
	}
#endif
	pre_key = key_value;

	GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);

#if GTP_WITH_PEN
	if (pre_pen && (touch_num == 0)) {
		GTP_DEBUG("Pen touch UP(Slot)!");
		input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
		input_mt_slot(ts->input_dev, 5);
		input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
		pre_pen = 0;
	}
#endif
	if (pre_touch || touch_num) {
		s32 pos = 0;
		u16 touch_index = 0;

		coor_data = &point_data[3];
		if (touch_num) {
			id = coor_data[pos] & 0x0F;
#if GTP_WITH_PEN
			id = coor_data[pos];
			if (id == 128) {
				GTP_DEBUG("Pen touch DOWN(Slot)!");
				input_x  = coor_data[pos + 1]
					| (coor_data[pos + 2] << 8);
				input_y  = coor_data[pos + 3]
					| (coor_data[pos + 4] << 8);
				input_w  = coor_data[pos + 5]
					| (coor_data[pos + 6] << 8);

				input_report_key(ts->input_dev,
					BTN_TOOL_PEN, 1);
				input_mt_slot(ts->input_dev, 5);
				input_report_abs(ts->input_dev,
					ABS_MT_TRACKING_ID, 5);
				input_report_abs(ts->input_dev,
					ABS_MT_POSITION_X, input_x);
				input_report_abs(ts->input_dev,
					ABS_MT_POSITION_Y, input_y);
				input_report_abs(ts->input_dev,
					ABS_MT_TOUCH_MAJOR, input_w);
				GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]",
					input_x, input_y, input_w);
				pre_pen = 1;
				pre_touch = 0;
			}
#endif

			touch_index |= (0x01<<id);
		}

		GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",
			id, touch_index, pre_touch);
		for (i = 0; i < GTP_MAX_TOUCH; i++) {
#if GTP_WITH_PEN
			if (pre_pen == 1)
				break;
#endif
			if (touch_index & (0x01<<i)) {
				input_x = coor_data[pos + 1] |
						coor_data[pos + 2] << 8;
				input_y = coor_data[pos + 3] |
						coor_data[pos + 4] << 8;
				input_w = coor_data[pos + 5] |
						coor_data[pos + 6] << 8;

				gtp_touch_down(ts, id,
						input_x, input_y, input_w);
				pre_touch |= 0x01 << i;

				pos += 8;
				id = coor_data[pos] & 0x0F;
				touch_index |= (0x01<<id);
			} else {
				gtp_touch_up(ts, i);
				pre_touch &= ~(0x01 << i);
			}
		}
	}
	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)
			dev_warn(&ts->client->dev, "I2C write end_cmd error!\n");

	}
	if (ts->use_irq)
		gtp_irq_enable(ts);

	return;
}

函数的核心其实是上报input时间,进入中断后最终会调用gtp_touch_down和gtp_touch_up来上报触摸按下和松开。触摸原装驱动就分析到这,下面说我写的两个接口:

我的两个接口一个是用来初始化input子系统,一个用来上报坐标信息如下:

首先定义初始化和上报的函数:

struct lidbg_ts_data
{
	s32 x[10];
	s32 y[10];
	s32 w[10];
	s32 id[10];
	u8 touch_num;
	u16 touch_index;
	u16 *pre_touch;
};
struct lidbg_input_data
{
	u16 abs_x_max;
	u16 abs_y_max;
};
void lidbg_touch_main(int argc, char **argv);
void lidbg_touch_report(struct input_dev * input_dev, struct lidbg_ts_data * pdata);
int lidbg_init_input(struct input_dev **input_dev,struct lidbg_input_data *pinput);
初始化的话我们要传入需要初始化的input指针,所以要用二级指针来:

int lidbg_init_input(struct input_dev **input_dev,struct lidbg_input_data *pinput)
{
    	int ret;
	char phys[32];
	lidbg("%s:----------------wsx-------------------\n",__FUNCTION__);
	*input_dev = input_allocate_device();
	if (input_dev == NULL) {
		lidbg("Failed to allocate input device.\n");
		return -ENOMEM;
	}
	(*input_dev)->evbit[0] =
		BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; 
	set_bit(BTN_TOOL_FINGER, (*input_dev)->keybit);
	__set_bit(INPUT_PROP_DIRECT, (*input_dev)->propbit);
	#if GTP_CHANGE_X2Y
	GTP_SWAP(pinput->abs_x_max, pinput->abs_y_max);
	#endif
	input_mt_init_slots(*input_dev, 5);/* in case of "out of memory" */ 
	input_set_abs_params(*input_dev, ABS_MT_POSITION_X,
				0, pinput->abs_x_max, 0, 0);
	input_set_abs_params(*input_dev, ABS_MT_POSITION_Y,
				0, pinput->abs_y_max, 0, 0);
	input_set_abs_params(*input_dev, ABS_MT_WIDTH_MAJOR,
				0, 255, 0, 0);
	input_set_abs_params(*input_dev, ABS_MT_TOUCH_MAJOR,
				0, 255, 0, 0);
	input_set_abs_params(*input_dev, ABS_MT_TRACKING_ID,
				0, 255, 0, 0);
	snprintf(phys, 32, "input/ts");
	//strcpy(input_dev->name,"Goodix-CTP");
	(*input_dev)->name = "Goodix-CTP";
	(*input_dev)->phys = phys;
	(*input_dev)->id.bustype = BUS_I2C;
	(*input_dev)->id.vendor = 0xDEAD;
	(*input_dev)->id.product = 0xBEEF;
	(*input_dev)->id.version = 10427;
	lidbg("before register\n");
	ret = input_register_device(*input_dev);

	if (ret) {
		lidbg("---------wsx------input device failed.\n");
		input_free_device(*input_dev);
		input_dev = NULL;
		return ret;
	}
	lidbg("after register\n");
	return 0;

}

然后是上报函数,上报的话我们要考虑到多点触控的问题,每次中断扫描支持的最大手指数,如果只有一个手指摁下,那么其他默认松开,其中touch_index用来表示按下的是哪个手指,而pre_touch用来记录具体某根手指是否摁下定义成u16是为了节省空间,一位代表一根手指:

void lidbg_touch_report(struct input_dev *input_dev,struct lidbg_ts_data *pdata)
{
    	int i;
	
	for (i = 0; i < GTP_MAX_TOUCH; i++)  
    	{  
		input_mt_slot(input_dev, pdata->id[i]);  
            if ((pdata->touch_index) & (0x01<<i)) 
            	{  
			#if GTP_CHANGE_X2Y
			GTP_SWAP(pdata->x[i], pdata->y[i]);
			#endif

		   
                      	input_mt_slot(input_dev, pdata->id[i]);
		        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
		        input_report_abs(input_dev, ABS_MT_POSITION_X, pdata->x[i]);
		        input_report_abs(input_dev, ABS_MT_POSITION_Y, pdata->y[i]);
		        input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, pdata->w[i]);
			input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, pdata->w[i]);
		        lidbg("%d,%d[%d,%d];\n", pdata->touch_num,pdata->id[i], pdata->x[i], pdata->y[i]);
			*(pdata->pre_touch) |= 0x01 << i;
			pdata->touch_index |= (0x01 << pdata->id[i+1]);
		      
			
        	}  
       		else  
        	{  
			lidbg("release\n");
           	    	input_mt_slot(input_dev, i);
		    	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
		    	*(pdata->pre_touch) &= ~(0x01 << i);
        	}  
		
	}  
  		input_sync(input_dev);
      
}

这里我们是每次上报完所有手指才同步,当然也可以每次上报一次就同步但是效率不高!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值