1379 字
7 分钟
Linux下GT911驱动移植

Kernel版本:6.6.52

开始前首先确认所用屏幕背面IC的丝印,到底是ft5426还是gt911,二者驱动不通用

客制化驱动#

若使用正点原子提供的gt9147.c驱动:

设备树#

&i2c2 {
gt911:gt911@14 {
compatible = "goodix,gt911", "goodix,gt9xx";
reg = <0x14>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gt911>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
status = "okay";
};
};
&iomuxc {
pinctrl_gt911: gt911grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x79 /* TSC_INT */
MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0
>;
};
};

6.6.52版本的Kernel DT不需要把MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09单独移到iomuxc_snvs

驱动#

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>
#include <asm/unaligned.h>
#define GT_CTRL_REG 0X8040
#define GT_MODSW_REG 0X804D
#define GT_9xx_CFGS_REG 0X8047
#define GT_1xx_CFGS_REG 0X8050
#define GT_CHECK_REG 0X80FF
#define GT_PID_REG 0X8140
#define GT_GSTID_REG 0X814E
#define GT_TP1_REG 0X814F
#define GT_TP2_REG 0X8157
#define GT_TP3_REG 0X815F
#define GT_TP4_REG 0X8167
#define GT_TP5_REG 0X816F
#define MAX_SUPPORT_POINTS 5
struct gt911_dev {
int irq_pin,reset_pin;
int irqnum;
int irqtype;
int max_x;
int max_y;
void *private_data;
struct input_dev *input;
struct i2c_client *client;
};
struct gt911_dev gt911;
const u8 irq_table[] = {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}; /* 触发方式 */
/* reset gt911 */
static int gt911_ts_reset(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0;
/* request RESET gpio */
if (gpio_is_valid(dev->reset_pin)) {
ret = devm_gpio_request_one(&client->dev,
dev->reset_pin, GPIOF_OUT_INIT_HIGH,
"gt911 reset");
if (ret) {
return ret;
}
}
/* request INT gpio */
if (gpio_is_valid(dev->irq_pin)) {
ret = devm_gpio_request_one(&client->dev,
dev->irq_pin, GPIOF_OUT_INIT_HIGH,
"gt911 int");
if (ret) {
return ret;
}
}
/* initialize gt911 follow datasheet */
gpio_set_value(dev->reset_pin, 0);
msleep(10);
gpio_set_value(dev->reset_pin, 1);
msleep(10);
gpio_set_value(dev->irq_pin, 0);
msleep(50);
gpio_direction_input(dev->irq_pin);
return 0;
}
static int gt911_read_regs(struct gt911_dev *dev, u16 reg, u8 *buf, int len)
{
int ret;
u8 regdata[2];
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->client;
regdata[0] = reg >> 8;
regdata[1] = reg & 0xFF;
msg[0].addr = client->addr;
msg[0].flags = !I2C_M_RD;
msg[0].buf = &regdata[0];
msg[0].len = 2;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = len;
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
static s32 gt911_write_regs(struct gt911_dev *dev, u16 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->client;
b[0] = reg >> 8;
b[1] = reg & 0XFF;
memcpy(&b[2],buf,len);
msg.addr = client->addr;
msg.flags = 0;
msg.buf = b;
msg.len = len + 2;
return i2c_transfer(client->adapter, &msg, 1);
}
static irqreturn_t gt911_irq_handler(int irq, void *dev_id)
{
int touch_num = 0;
int input_x, input_y;
int id = 0;
int ret = 0;
u8 data;
u8 touch_data[5];
struct gt911_dev *dev = dev_id;
ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1);
if (data == 0x00) { /* no touch data */
goto fail;
} else { /* calculate touch data */
touch_num = data & 0x0f;
}
if(touch_num) {
gt911_read_regs(dev, GT_TP1_REG, touch_data, 5);
id = touch_data[0] & 0x0F;
if(id == 0) {
input_x = touch_data[1] | (touch_data[2] << 8);
input_y = touch_data[3] | (touch_data[4] << 8);
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);
}
} else if(touch_num == 0){
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);
}
input_mt_report_pointer_emulation(dev->input, true);
input_sync(dev->input);
data = 0x00;
gt911_write_regs(dev, GT_GSTID_REG, &data, 1);
fail:
return IRQ_HANDLED;
}
static int gt911_ts_irq(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0;
/* requeset IRQ */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
gt911_irq_handler, irq_table[dev->irqtype] | IRQF_ONESHOT,
client->name, &gt911);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
static int gt911_read_firmware(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0, version = 0;
u16 id = 0;
u8 data[7]={0};
char id_str[5];
ret = gt911_read_regs(dev, GT_PID_REG, data, 6);
if (ret) {
dev_err(&client->dev, "Unable to read PID.\n");
return ret;
}
memcpy(id_str, data, 4);
id_str[4] = 0;
if (kstrtou16(id_str, 10, &id))
id = 0x1001;
version = get_unaligned_le16(&data[4]);
dev_info(&client->dev, "ID %d, version: %04x\n", id, version);
switch (id) {
case 1151:
case 1158:
case 5663:
case 5688:
ret = gt911_read_regs(dev, GT_1xx_CFGS_REG, data, 7);
break;
default:
ret = gt911_read_regs(dev, GT_9xx_CFGS_REG, data, 7);
break;
}
if (ret) {
dev_err(&client->dev, "Unable to read Firmware.\n");
return ret;
}
dev->max_x = (data[2] << 8) + data[1];
dev->max_y = (data[4] << 8) + data[3];
dev->irqtype = data[6] & 0x3;
printk("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", dev->max_x, dev->max_y, dev->irqtype);
return 0;
}
static int gt911_probe(struct i2c_client *client)
{
u8 data, ret;
gt911.client = client;
gt911.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
gt911.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
ret = gt911_ts_reset(client, &gt911);
if(ret < 0) {
goto fail;
}
data = 0x02;
gt911_write_regs(&gt911, GT_CTRL_REG, &data, 1);
mdelay(100);
data = 0x0;
gt911_write_regs(&gt911, GT_CTRL_REG, &data, 1);
mdelay(100);
ret = gt911_read_firmware(client, &gt911);
if(ret != 0) {
printk("Fail !!! check !!\r\n");
goto fail;
}
gt911.input = devm_input_allocate_device(&client->dev);
if (!gt911.input) {
ret = -ENOMEM;
goto fail;
}
gt911.input->name = client->name;
gt911.input->id.bustype = BUS_I2C;
gt911.input->dev.parent = &client->dev;
__set_bit(EV_KEY, gt911.input->evbit);
__set_bit(EV_ABS, gt911.input->evbit);
__set_bit(BTN_TOUCH, gt911.input->keybit);
input_set_abs_params(gt911.input, ABS_X, 0, gt911.max_x, 0, 0);
input_set_abs_params(gt911.input, ABS_Y, 0, gt911.max_y, 0, 0);
input_set_abs_params(gt911.input, ABS_MT_POSITION_X,0, gt911.max_x, 0, 0);
input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,0, gt911.max_y, 0, 0);
ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}
ret = input_register_device(gt911.input);
if (ret)
goto fail;
ret = gt911_ts_irq(client, &gt911);
if(ret < 0) {
goto fail;
}
dev_info(&client->dev, "GT911 Probe OK\r\n");
return 0;
fail:
return ret;
}
static void gt911_remove(struct i2c_client *client)
{
input_unregister_device(gt911.input);
}
const struct of_device_id gt911_of_match_table[] = {
{.compatible = "goodix,gt911" },
{ }
};
struct i2c_driver gt911_i2c_driver = {
.driver = {
.name = "gt911",
.owner = THIS_MODULE,
.of_match_table = gt911_of_match_table,
},
.probe = gt911_probe,
.remove = gt911_remove,
};
module_i2c_driver(gt911_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("aki");

Kernel提供驱动#

驱动文件为/drivers/input/touchscreen/goodix.c

设备树#

按照binding要求

GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Goodix GT9xx series touchscreen controller
maintainers:
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- goodix,gt1151
- goodix,gt1158
- goodix,gt5663
- goodix,gt5688
- goodix,gt911
- goodix,gt9110
- goodix,gt912
- goodix,gt9147
- goodix,gt917s
- goodix,gt927
- goodix,gt9271
- goodix,gt928
- goodix,gt9286
- goodix,gt967
reg:
enum: [ 0x5d, 0x14 ]
interrupts:
maxItems: 1
irq-gpios:
description: GPIO pin used for IRQ. The driver uses the interrupt gpio pin
as output to reset the device.
maxItems: 1
reset-gpios:
maxItems: 1
AVDD28-supply:
description: Analog power supply regulator on AVDD28 pin
VDDIO-supply:
description: GPIO power supply regulator on VDDIO pin
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-swapped-x-y: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
gt928@5d {
compatible = "goodix,gt928";
reg = <0x5d>;
interrupt-parent = <&gpio>;
interrupts = <0 0>;
irq-gpios = <&gpio1 0 0>;
reset-gpios = <&gpio1 1 0>;
};
};

compatiblereginterrupts必须要有,其他的无所谓,修改设备树节点如下:

&i2c2 {
gt911:gt911@14 {
compatible = "goodix,gt911", "goodix,gt9xx";
reg = <0x14>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>;
irq-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};

注意:这里的reset-gpio和irq-gpio有效电平均为高电平,不能设置成低电平,虽然硬件设计时INT和RST为外部上拉低电平有效。原因未知。

驱动由于找不到本地的xxx_cfg.bin文件导致长时间等待(一般为64s)后再进行input注册的问题可以通过在goodix.c中Probe函数中修改判断条件解决:

/* 注释掉本地cfg检索,防止找不到cfg.bin时长时间等待 */
//if (ts->load_cfg_from_disk) {
if (0){
/* update device config */
error = device_property_read_string(&client->dev,
"goodix,config-name",
&cfg_name);
if (!error)
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
"goodix/%s", cfg_name);
else
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
"goodix_%s_cfg.bin", ts->id);
error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
&client->dev, GFP_KERNEL, ts,
goodix_config_cb);
if (error) {
dev_err(&client->dev,
"Failed to invoke firmware loader: %d\n",
error);
return error;
}
return 0;
} else {
......
}
Linux下GT911驱动移植
https://arkmostima.pages.dev/posts/linux下gt911驱动移植/
作者
Aki
发布于
2025-04-13
许可协议
CC BY-NC-SA 4.0