v3S drive gt911 touch

1, Modify device tree

Add pio node in sun8i-v3s-licheepi-zero-dock.dts (in fact, it's OK not to add it after testing, as will be described later)

&pio{
			/* Key */
			key_pins:key_pins@0{
					pins = "PG2";
					function = "gpio_in";
					bias-pull-up;
			};

			/* Touch reset */
			ts_reset_pin: ts_reset_pin@0 {
					pins = "PB3";
					function = "gpio_out";
			};	
};

Add under &i2c node

	/*  touch */
	gt911:gt911@5d {
		compatible = "goodix,gt911","goodix,gt9xx";
		reg = <0x5d>;

		pinctrl-names = "default";
		/*pinctrl-0 = <&ts_reset_pin>; 				*/	
		interrupt-parent = <&pio>;
		interrupts = <1 2 IRQ_TYPE_EDGE_FALLING>; /* (PB2) */

		irq-gpio = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* (PB2) */
		rst-gpio = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* RST (PB3) */
		status = "okay";
	};


Then compile the device tree and download it to the development board for operation.

2, Write driver

gt911.c

#include <linux/module.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/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>



/**
 * file name: gt9xx
 * date: 2021-09-01  15:04
 * version: 1.0
 * author:luatao
 * describe: gt9xx device drive
 */
#define GT_CTRL_REG  	             0x8040 / * gt9147 control register*/
#define GT_MODSW_REG  	         0x804d / * gt9147 mode switching register*/
#define GT_CFGS_REG  	         0x8047 / * gt9147 configure start address register*/
#define GT_CHECK_REG  	         0x80ff / * gt9147 checksum register*/
#define GT_PID_REG  		         0x8140 / * gt9147 product ID register*/

#define GT_GSTID_REG  	         0x814e / * touch condition currently detected by gt9147*/
#define GT_TP1_REG  		         0X814F / * data address of the first touch point*/
#define GT_TP2_REG  		         0X8157 	/*  Second touch point data address*/
#define GT_TP3_REG  		         0X815F / * data address of the third touch point*/
#define GT_TP4_REG  		         0X8167 / * data address of the fourth touch point*/
#define GT_TP5_REG  		         0X816F 	/*  Fifth touch point data address*/
#define MAX_ SUPPORT_ Points 5 / * up to 5 capacitive touch points*/


/* Equipment structure customization */
struct gt911_dev{
    int irq_pin, reset_pin;   /* Interrupt and reset IO */
    int irqnum;   /* interrupt number */
  //  void *private_date;  /*  Private data*/
    struct input_dev *input;  /* input structural morphology */
    struct i2c_client *client; /* i2c client */
 };

/* Define an equipment structure */
struct gt911_dev gt911;   /*gt911 equipment */
/*
const unsigned char GT911_CT[]=
{
                                0x5A,0x20,0x03,0xE0,0x01,0x05,0x3D,0x00,0x02,0x08,0x28,
                                0x08,0x5A,0x46,0x03,0x05,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x04,0x04,0x04,0x04,0x03,0x88,0x29,0x0A,0x4B,0x4D,0x0C,
                                0x08,0x00,0x00,0x00,0x21,0x02,0x1D,0x00,0x01,0x00,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x64,0x94,0xD5,
                                0x02,0x07,0x00,0x00,0x04,0x83,0x48,0x00,0x77,0x4D,0x00,
                                0x6D,0x53,0x00,0x64,0x59,0x00,0x5A,0x60,0x00,0x5A,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x00,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10,0x12,
                                0x14,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x02,0x04,0x06,0x08,0x0F,0x10,0x12,0x16,0x18,0x1C,0x1D,
                                0x1E,0x1F,0x20,0x21,0x22,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
                                0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD6,0x01,
};*/


/* Read multiple register data from gt911 
 @param - *dev        : gt911 equipment 
 @param - reg           : First register address to read
 @param - *buf         : Read data 
 @param - len          : Length of data to read 
 @return                    :Operation results
*/
static int gt911_read_regs(struct gt911_dev *dev, u16 reg, u8 *buf, int len)
{
    int ret = 0;
    u8 regdata[2]; // Register data
    struct i2c_msg msg[2];   /* Transmitted message read command */
    struct i2c_client *client = (struct i2c_client *)dev->client;  /* Private data */

    /* gt911 The register length is 2 bytes */
    regdata[0] = (reg >> 8) & 0xFF;  // High 8 bits
    regdata[1] = reg & 0xFF;  // Lower 8 bits 

    /* msg[0] Send the first address to read for */
    msg[0].addr = client->addr;  /* Device address */
    msg[0].flags = 0;                       /* Mark as send data */
    msg[0].buf = &regdata[0];              /* The first address of the data to be read */
    msg[0].len = 2;                     /* reg length */

    /* msg[1]Read data */
    msg[1].addr = client->addr;  /* Device address */
    msg[1].flags = I2C_M_RD;    /* Mark as read data */
    msg[1].buf = buf;              /* Read data buffer  */
    msg[1].len = len;                     /* Read data length */

    ret = i2c_transfer(client->adapter, msg, 2);  /* Send 2 messages to the bus */
    if(ret == 2){  /* Transmission succeeded */
        ret = 0;
    }else{
        printk("i2c_transfer failed!\r\n");
        return -EREMOTEIO;
    }

    return ret;
}

/* Write data from gt911 multiple registers 
 @param - *dev        : gt911 equipment 
 @param - reg           : First address of register to be written
 @param - *buf         : Write data buffer
 @param - len          : Length of data to write 
 @return                    :Operation results
*/
static s32 gt911_write_regs(struct gt911_dev *dev, uint16_t reg, uint8_t *buf, int len)
{
    uint8_t buf1[256];
    struct i2c_msg msg;   /* Transmitted messages */
    struct i2c_client *client = (struct i2c_client *)dev->client;  
    buf1[0] = (reg >> 8) & 0xFF;  /* Register header address */
    buf1[1] = reg & 0xFF;  
    memcpy(&buf1[2], buf, len);  /* Copy the data to be written to the data buf1 */
    
    /* msg Processing data */
    msg.addr = client->addr;  /* Device address */
    msg.flags = 0;    /* Mark as write data */
    msg.buf = buf1;              /* Data buffer to write to  */
    msg.len = len + 2;                     /* Length of data written */

    return  i2c_transfer(client->adapter, &msg, 1);  /* Send 1 message to the bus */
}


/* Read the specified register value from gt911 and read a register
 @param - *dev        : ap3216 equipment 
 @param - reg           : Register to read
 @return                    :Read register value
*/

//static unsigned char  gt911_read_reg(struct gt911_dev *dev, u8 reg)
//{
  // struct i2c_ client *client = (struct i2c_ client *)dev->client;  /*  Private data*/

	//return i2c_smbus_read_byte_data(client, reg);  /*  Read one byte of data*/
//}

/* Write the specified value to the gt911 specified register and write a register 
 @param - *dev        : ap3216 equipment 
 @param - reg           : Register to write
 @param - data         : Value to write 
 @return                    :nothing
*/
static void gt911_write_reg(struct gt911_dev *dev, uint16_t reg, uint8_t data)
{
    uint8_t buf = 0;
	buf = data;
	gt911_write_regs(dev, reg, &buf, 1);  /* Call the method to write multiple registers */
}

/* Touch interrupt processing function */
static irqreturn_t gt911_irq_handler(int irq, void *dev_id)
{
    u8 touch_num = 0, status, havakey ; // The maximum number of touch points is 5. Is the touch data ready? Is there a key pressed 
    int input_x, input_y, id = 0;  // x. y coordinate touch ID
    int ret = 0;  // Return value
    u8 data,touch_data[5];  // Touch data
    struct gt911_dev *dev = dev_id; // Touch device structure

    /* Determine whether to enter interrupt */
    //printk("cd %s\r\n",__FUNCTION__);

    /* Read coordinate point register */
    ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1); 
    /* The representation of this one bit data:
       bit7: 1 Indicates that the coordinates (or keys) are ready, and the master can read. 0 indicates that they are not ready, and the data is invalid
       bit4: 1 Indicates that there is a key, 0 indicates that there is no key (released)
       bit3~0: Number of coordinate points on the screen  */
    if(data == 0x00){  /* No touch data*/
        goto fail;
    }else{  /* Statistics touch information */
        status = data >> 7;  // Take the highest order 
        havakey = (data >> 4) & 0x01; 
        touch_num = data & 0x0f;  // Only take the lower 4 digits 
    }

    /* Single touch is not applicable to multi touch*/
    if(touch_num){  /* Touch press   */
        gt911_read_regs(dev, GT_TP1_REG, touch_data, 5);  // Read the first touch point and read 5 data continuously 
        id = touch_data[0] & 0x0F; 
        if(id == 0){
            input_x = (touch_data[1] | (touch_data[2] << 8)) & 0x0fff;  // x coordinate
            input_y = (touch_data[3] | (touch_data[4] << 8)) & 0x0fff;  // y coordinate

            input_mt_slot(dev->input, id); // ABS generation_ MT_ The coordinates of which touch point is the slot event report 
            input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);   // Designated finger touch continuous touch
            input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);   // Reporting touch point coordinate information 
            input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);   // Reporting touch point coordinate information 

            printk("x = %d, y = %d\r\n", input_x, input_y);  //Print coordinate information 
        }
    }else if(touch_num == 0){  // Single touch release 
        input_mt_slot(dev->input, id); /* Report touch points */
        input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); // Turn off finger touch 
    }

        input_mt_report_pointer_emulation(dev->input, true); 
        input_sync(dev->input);   /* Synchronization data reporting completed */

        data = 0x00;  /* Write 0 to 0x814E register, otherwise it will always enter interrupt */
        gt911_write_regs(dev, GT_GSTID_REG, &data, 1);   //write in

fail:
    return IRQ_HANDLED;
}

/* Request IO and reset gt911 
 @param - *client           : i2C controller
 @param - *dev         : Custom touch devices
 @return                    :0: Success other negative values: failure
*/
static int gt911_ts_reset(struct i2c_client * client, struct gt911_dev *dev)
{
    int ret = 0;
    printk("cd %s\r\n",__FUNCTION__);
    /* Request reset IO */
    if(gpio_is_valid(dev->reset_pin)){  // Determine whether gpio is legal
    /* Request to reset IO and default output high level */
        ret = devm_gpio_request_one(&client->dev,     
                                                                        dev->reset_pin,
                                                                        GPIOF_OUT_INIT_LOW,
                                                                        "gt911 reset");
        if(ret){  // Application failed
            printk("request reset_pin failed!\r\n");
             return ret;
        }
    }

    /* Request interrupt IO*/
     if(gpio_is_valid(dev->irq_pin)){  // Determine whether gpio is legal
    /* Request to reset IO and default output high level */
        ret = devm_gpio_request_one(&client->dev,     
                                                                        dev->irq_pin,   //  Pin number
                                                                        GPIOF_OUT_INIT_LOW,   // Default level state 
                                                                        "gt911 irq");  // Name whatever
        if(ret){  // Application failed
            printk("request irq_pin failed!\r\n");
             return ret;
        }
    }

    /* Initialize gt911  */
    gpio_set_value(dev->reset_pin, 0); /* reset */
    msleep(10);
    gpio_set_value(dev->reset_pin, 1); /* Stop reset */
    msleep(10);
    gpio_set_value(dev->irq_pin, 0);    /* Pull down INT pin */
    msleep(50);
    gpio_direction_input(dev->irq_pin); /* INT Pin set as input */

    /* There is an address judgment */

	return 0;
}

/* gt911 Interrupt initialization 
 @param - *client           : i2C controller
 @param - *dev         : Custom touch devices
 @return                    :0: Success other negative values: failure
*/
static int gt911_ts_irq(struct i2c_client * client, struct gt911_dev *dev)
{
    int ret = 0; // Return value

    /* Application interruption  */
    ret = devm_request_threaded_irq(&client->dev,
                                                                            client->irq,
                                                                            NULL,
                                                                            gt911_irq_handler,
                                                                            IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                                                            client->name,
                                                                            &gt911);
    if(ret){
        dev_err(&client->dev, "Unable to request touchscreen IRQ.\r\n");
        return ret;
    }

    printk("gt911 handler irq number: %d\r\n", client->irq);  // Print out the interrupt number
    return 0;
}

/* Send gt911 configuration parameters 
 @param - *dev           : Custom touch devices
 @param - mode         : 0 :Parameters not saved to flash 1: parameters saved to flash
 @return                    :nothing
*/
/* Screen configuration information  */
/*
void gt911_send_cfg(void)
{
    u8 regdata[186] = {0};
    unsigned int i = 0,ret = 0;
    u8 softVersion = 0;  // Software version number 
    u8 gt911_id[6] = {0}; // Product ID
    u8 irqmode = 0;  // Interrupt trigger mode
    u8 crc = 0;  // Checksum 

    //  Read software version number 
    gt911_read_regs(&gt911, GT_CFGS_REG, &softVersion,1); 
    printk("soft version:%d\r\n", softVersion);

    // Read product ID 
    printk("ID: ");
    gt911_read_regs(&gt911, GT_PID_REG, gt911_id,6); 
    for(i = 0; i< 6;i++)
        printk("%d ", gt911_id[i]);

    printk("\r\n");

    // Read interrupt trigger mode 
    gt911_read_regs(&gt911, GT_MODSW_REG, &irqmode,1); 
    printk("irqmode:%d\r\n", irqmode);

    // Read 184 registers 
    gt911_read_regs(&gt911, GT_CFGS_REG, regdata,184); 
    for(i= 0; i < 186; i++){
        printk("%#X ", regdata[i]);
        if(i < 184){
            crc += regdata[i];  // Checksum 
        }
    }
    printk("\r\n");
    crc = (~crc) + 1;
    printk("crc:%d\r\n", crc);

    // Software reset 
    gt911_write_reg(&gt911, GT_CTRL_REG, 2);

    // 186 registers configured 
    // Get the configuration information of the device tree 
    ret = of_property_read_u8_array(gt911.client->dev.of_node, "goodix,cfg-group0", regdata, 186);
    if (ret < 0) {
        printk("goodix,cfg-group0 property read failed\r\n");
    } else {
        printk("reg data:\r\n");
        for(i = 0; i < 186; i++){
            printk("%X ", regdata[i]);
        }
        printk("\r\n");
    }

    gt911_write_regs(&gt911, GT_CFGS_REG, regdata, sizeof(regdata));  
    gt911_write_reg(&gt911,GT_CTRL_REG,2);

    msleep(100);
}
*/



/* i2C The probe function of the driver. This function will be executed after the driver matches the device */
static int gt911_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    u8 ret = 0; 
    gt911.client = client; 
    printk("gt911 driver and device has match!\r\n");  // Prompt information 

    /* 1. Gets the interrupt and reset pins in the device tree */
    gt911.irq_pin = of_get_named_gpio(client->dev.of_node, "irq-gpio", 0);
	gt911.reset_pin = of_get_named_gpio(client->dev.of_node, "rst-gpio", 0);

    printk("get gpios success!\r\n");

    /* 2. Reset gt911 request GPIO and reset */
    ret = gt911_ts_reset(client, &gt911);
    if(ret < 0){
        printk("gt911 reset failed!\r\n");
        goto fail;
    }

    /* 3. Initialize gt911 */
    gt911_write_reg(&gt911, GT_CTRL_REG, 2);  /* Soft reset */
    mdelay(100);
    gt911_write_reg(&gt911, GT_CTRL_REG, 0);  /* Stop soft reset */
    mdelay(100);

    /* 4. input Register device*/
    gt911.input  = devm_input_allocate_device(&client->dev);
    if(!gt911.input){
        return -ENOMEM;
    }

    /* Initialize input */
    gt911.input->name = client->name;
    gt911.input->id.bustype = BUS_I2C;
    gt911.input->dev.parent = &client->dev;
    /* To set the input device, you need to report the event type and key value*/
    __set_bit(EV_KEY, gt911.input->evbit);
	__set_bit(EV_ABS, gt911.input->evbit);
	__set_bit(BTN_TOUCH, gt911.input->keybit);
    /* Set the absolute coordinates to be reported by the input device */
    input_set_abs_params(gt911.input, ABS_X, 0, 800, 0, 0);
	input_set_abs_params(gt911.input, ABS_Y, 0, 480, 0, 0);
	input_set_abs_params(gt911.input, ABS_MT_POSITION_X,0, 800, 0, 0);
	input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,0, 480, 0, 0);	     
    /* Initialize slots for multi-point capacitive touch*/
	ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0);  // Number of input slots touch points for initializing MT
    if(ret != 0){
        printk("MT init failed!\r\n");
        goto fail;
    }

    /* Register input */
    ret = input_register_device(gt911.input);
    if(ret){
        printk("input register failed!\r\n");
        goto fail;
    }

    /* Last initialization interrupt */
    ret = gt911_ts_irq(client, &gt911);
    if(ret < 0){
        printk("init irq failed!\r\n");
        goto fail;
    }
    return 0;

fail:
    return ret;
}

/* i2c remove function after driving */
int gt911_remove(struct i2c_client *client)
{
    /* Release input device */
    input_unregister_device(gt911.input);

    printk("gt911 drive unregsister ok !\r\n");
    return 0;
}

/* Traditional matching method ID list */
static const struct i2c_device_id gt911_id[] = {
    {"goodix,gt911", 0},
    {}
};
/* Match list */
static const struct of_device_id gt911_of_match[] = {
    {.compatible = "goodix,gt911"},
    {/* Sentinel */}
};

/* i2c Drive structure */
struct i2c_driver gt911_i2c_driver = {
        .driver = {
            .owner = THIS_MODULE,
            .name = "gt911",   /* The driver name is used to match the device. It is applicable when there is no device tree*/
            .of_match_table =gt911_of_match,  /* Device tree matching list */
        },
        .probe =gt911_probe,
        .remove =gt911_remove,
        .id_table = gt911_id, /* id Configuration list */
};


module_i2c_driver(gt911_i2c_driver);

/* LICENSE And AUTHOR information*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luatao");

3, Run test

Here are some problems encountered during debugging:
If it is not commented out in the device tree

/*pinctrl-0 = <&ts_reset_pin>; 				*/	

Then the following error will be reported after loading

This means that the PB3 pin has been registered, but I searched the device tree and couldn't find where it was registered (I don't know at present). Just comment this out.

Load run

After loading, pressing the screen with your finger will print coordinate information.

4, Compile into kernel

1. Copy files

Copy gt911.c to the drivers/input/touchscreen directory

cp gt911.c /home/luatao/linux/zero/linux-zero-5.2.y/drivers/input/touchscreen/

2. Modify the corresponding Makefile

Modify the Makefile in the drivers/input/touchscreen directory and add the following line at the bottom:

obj-y += gt911.o

3. Compile and run

After the modification, recompile the linux kernel, and then start the development board with the new zImage.

If the driver is added successfully, the system will output as shown in the figure when starting up

4. Test

When we click on the screen, it will print

This means that there is no problem with the driver.

1. Comment out the coordinate information

However, this will print coordinates every time you press it. It is not easy to apply during operation. We need to comment it out in the driver,
Open the gt911.c file in the kernel

Comment out the information in the red box above, then compile the kernel and download the development board.

5, Porting tslib

1. Configure tslib for builderoot

Execute make menuconfig

Target packages —>
Graphic libraries and applications (graphic/text) —>
[] Qt5 —>
[] Enable Tslib support


Then save, exit, compile and download the development board

2. Configure tslib

After tslib is enabled in the builderoot configuration, add the following to / etc/profile.

export T_ROOT=/usr/tslib4arm
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/usr/lib/ts/
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0


Enable to take effect

source /etc/profile

3. Test

input

ts_test_mt

This command will open a touch test interface

There are three buttons "Draw", "Draw" and "Quit" on the figure. The functions of these three buttons are as follows:

Drag:: drag button, which is the default function. You can see a cross cursor in the middle of the screen. We can drag the cursor by touching the screen. One touch point is a cross cursor. For a 5-point capacitive touch screen, if all five fingers are placed on the screen, there are five cursors, one finger and one.
Draw:: draw button. Press this button to simply draw on the screen. You can use this function to detect whether multi touch works normally.
Quit: exit button, exit ts_test_mt test software.

We can test

Tags: Linux IoT

Posted on Wed, 01 Sep 2021 20:24:07 -0400 by JJohnsenDK