Troubleshooting a Touchscreen Driver Issue

Troubleshooting a Touchscreen Driver Issue

Today, I worked with a colleague on a TP driver. After powering on, the logs were normal, but the touchscreen was unresponsive, so we began troubleshooting.

After powering on, we could read the chip’s chip ID normally, which indicates that the I2C communication is functioning correctly, and it also confirms that the power supply to the touchscreen chip is normal.

Based on this, I brought over an oscilloscope to measure the signal on the interrupt pin and observed two phenomena:

1. After transitioning from sleep to wake state, there was a waveform on the interrupt pin, and the waveform appeared normal.

2. The interrupt handler function did not execute.

—— The DTS file for the touchscreen is written as follows:

File: arch/arm/boot/dts/inxxx.dts
cap_touch@14 {
  compatible = "mediatek,cap_touch";
  reg = <0x14>;
  interrupt-parent = <&pio>;
  interrupts = <100 IRQ_TYPE_EDGE_FALLING>;
  int-gpio = <&pio 100 0>;
  rst-gpio = <&pio 101 0>;
};

The compatible field matches the name in the driver.

The reg field is the I2C chip address.

The interrupt-parent corresponds to the platform’s interrupt controller, where the pio used corresponds to the DTS description of the interrupt controller in the mt8167.dtsi file.

The first parameter of interrupts corresponds to the interrupt number, and the second parameter corresponds to the trigger type of the interrupt.

The int-gpio references the GPIO controller in pio, where the second parameter corresponds to the GPIO number, and the third corresponds to the GPIO level.

The rst-gpio is similar to the above.

Refer to the kernel documentation for the description of gpiodts:

File: Documentation/devicetree/binding/pinctrl/pinctrl-mt65xx.txt
Eg: <&pio 6 0>
<[phandle of the gpio controller node]
[line number within the gpio controller]
[flags]>
Values for gpio specifier:
- Line number: is a value between 0 to 202.
- Flags: bit field of flags, as defined in <dt-bindings/gpio/gpio.h>. Only the following flags are supported: 0 - GPIO_ACTIVE_HIGH 1 - GPIO_ACTIVE_LOW

Let’s see what is inside &pio:

pio: pinctrl@10005000 {
   compatible = "mediatek,mt8167-pinctrl";
   reg = <0 0x1000b000 0 0x1000>;
   mediatek,pctl-regmap = <&syscfg_pctl_a>;
   pins-are-numbered;
   gpio-controller;
   #gpio-cells = <2>;
   interrupt-controller;
   #interrupt-cells = <2>;
   interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
};

—— Let’s check the registered interrupt numbers:

xxxx:/ # cat proc/interrupts
           CPU0
 18:      19578       GIC  27 Edge      arch_timer
 20:          0  MT_SYSIRQ 132 Level     mtk_timer
 21:         12  MT_SYSIRQ  84 Level     mtk-uart
 30:          0  MT_SYSIRQ 128 Level     SPM
 31:          0  MT_SYSIRQ 129 Level     SPM
 32:          0  MT_SYSIRQ 130 Level     SPM
 33:          0  MT_SYSIRQ 131 Level     SPM
 34:          0  MT_SYSIRQ 221 Edge      ATF_irq
 39:          0  MT_SYSIRQ 194 Level     BTCVSD_ISR_Handle
 40:          0  MT_SYSIRQ 198 Edge      mtk-wdt
 42:          0  MT_SYSIRQ 204 Level     mt-pmic-pwrap
 43:          0  MT_SYSIRQ 149 Edge      mtk-kpd
 45:          0  MT_SYSIRQ 121 Level     10203000.m4u
 46:          0  MT_SYSIRQ 218 Level     CIRQ
 47:          0  MT_SYSIRQ 114 Level     TEE IRQ
 49:       2942  MT_SYSIRQ 125 Level     mtk_cmdq
 50:          0  MT_SYSIRQ 126 Level     TEE IRQ
 53:          0  MT_SYSIRQ  76 Level     mt-pwm
 54:         30  MT_SYSIRQ  80 Level     i2c-mt65xx
 55:         16  MT_SYSIRQ  81 Level     i2c-mt65xx
 56:         64  MT_SYSIRQ  82 Level     i2c-mt65xx
 57:          0  MT_SYSIRQ  77 Level     mt8167-thermal
 58:         16  MT_SYSIRQ  83 Level     ptp
 62:      20967  MT_SYSIRQ  72 Level     musb-hdrc
 63:          0  MT_SYSIRQ 120 Level     Afe_ISR_Handle
 64:       2950  MT_SYSIRQ 185 Level     pvrsrvkm
 65:     113181  MT_SYSIRQ  78 Level     11120000.mmc
 67:          0  MT_SYSIRQ 210 Level     musbfsh-hdrc.0
 74:        347  MT_SYSIRQ 160 Level     DISPSYS
 75:      12980  MT_SYSIRQ 162 Level     DISPSYS
 76:          0  MT_SYSIRQ 163 Level     DISPSYS
 80:          0  MT_SYSIRQ 167 Level     DISPSYS
 83:          0  MT_SYSIRQ 171 Level     DISPSYS
 85:       1466  MT_SYSIRQ 153 Level     DISPSYS
 88:          0  MT_SYSIRQ 180 Level     ISP
123:          0  mtk-eint  28 Level     mt6397-pmic
136:          0  mtk-eint  41 Level     USB_IDDIG
195:          6  mtk-eint 100 Edge      mtk-tpd
264:          0  mt6397-irq   6 Edge      mt6397-thr_l
265:          0  mt6397-irq   7 Edge      mt6397-thr_h
266:          0  mt6397-irq   5 Edge      mtk-pmic-keys
267:          0  mt6397-irq  17 Edge      mtk-pmic-keys
268:          0  mt6397-irq  18 Edge      mtk-pmic-keys
269:          0  mt6397-irq  19 Edge      mtk-pmic-keys
270:          0  mt6397-irq  20 Edge      mt6397-rtc
IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:      31856  Rescheduling interrupts
IPI3:         35  Function call interrupts
IPI4:        121  Single function call interrupts
IPI5:          0  CPU stop interrupts
IPI6:          0  IRQ work interrupts
IPI7:          0  completion interrupts
Err:          0

# Let’s see how the driver handles these DTS configurations

First, use the function to obtain the content from the DTS:

tpd_rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpio", 0);
tpd_int_gpio = of_get_named_gpio(dev->of_node, "int-gpio", 0);

Then, request the GPIO:

static int gtp_get_gpio_res(void)
{
#if defined(CONFIG_OF) && !defined(CONFIG_GTP_USE_PINCTRL)
 int ret;
 /* configure the gpio pins */
 ret = gpio_request_one(tpd_rst_gpio, GPIOF_OUT_INIT_LOW,
     "touchp_reset");
 if (ret < 0) {
  GTP_ERROR("Unable to request gpio reset_pin\n");
  return -1;
 }
 ret = gpio_request_one(tpd_int_gpio, GPIOF_IN,
     "tpd_int");
 if (ret < 0) {
  GTP_ERROR("Unable to request gpio int_pin\n");
  gpio_free(tpd_rst_gpio);
  return -1;
 }
#endif
 return 0;
}

Next, when looking at the interrupt registration function, there is something strange:

static int tpd_irq_registration(void)
{
 struct device_node *node = NULL;
 unsigned long irqf_val = 0;
 int ret = 0;

// node = of_find_compatible_node(NULL, NULL, "mediatek,cap_touch");//0704
 node = of_find_matching_node(NULL, touch_of_match);

 if (node) {
//  touch_irq = gpio_to_irq(tpd_int_gpio);
  touch_irq = irq_of_parse_and_map(node, 0);
  GTP_ERROR("###### touch_irq = %d\n",(int)touch_irq);
  irqf_val = !int_type ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
   
  irq_enabled = true; //
  ret = request_irq(touch_irq, (irq_handler_t) tpd_interrupt_handler,
    irqf_val, TPD_DEVICE, NULL); //jason test here
  GTP_ERROR("###### ret = %d\n",(int)ret);
  if (ret < 0)
   GTP_ERROR("tpd request_irq IRQ LINE NOT AVAILABLE!.");
}
}

// touch_irq = gpio_to_irq(tpd_int_gpio); This line of code is commented out, meaning that gpio_to_irq is not used to handle the interrupt.

The current driver uses the irq_of_parse_and_map function to parse the DTS content for the driver.

The reason for using irq_of_parse_and_map is to reduce the workload for the driver engineer, as the DTS has already clearly described the interrupt information, including the interrupts and interrupt-parent properties. This function will parse these two properties and implement the corresponding mapping relationship.

—— Let’s look at the kernel code’s explanation of this function:

/**
 * irq_of_parse_and_map - Parse and map an interrupt into linux virq space
 * @dev: Device node of the device whose interrupt is to be mapped
 * @index: Index of the interrupt to map
 *
 * This function is a wrapper that chains of_irq_parse_one() and
 * irq_create_of_mapping() to make things easier to callers
 */
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
 struct of_phandle_args oirq;

 if (of_irq_parse_one(dev, index, &oirq))
  return 0;

 return irq_create_of_mapping(&oirq);
}
EXPORT_SYMBOL_GPL(irq_of_parse_and_map);

The above explanation means that this function parses and maps the interrupt to the Linux virq space, where dev corresponds to the device pointer, and index is the index of the interrupt to be mapped.

This function is a wrapper for of_irq_parse_one() and irq_create_of_mapping(), making it easier for developers to write code.

Now let’s look at:

touch_irq = irq_of_parse_and_map(node, 0);

The 0 here indicates the offset index. If we write it like this in the DTS, we can use the index to select our configuration:

interrupts = <0 0 4>, <0 1 4>;

The number “195” we see in proc/interrupts comes from here:

195:         69          0          0          0  mtk-eint 100 Edge      mtk-tpd

# So why didn’t the interrupt function execute?

After checking everything, it was found that the interrupts property in the DTS description was not written correctly. The first parameter of interrupts needs to correspond to the GPIO pin number, but we reused the code from the previous project and should have modified this GPIO pin, which we did not update in the software in time.

Of course, once the problem was identified, it was quickly resolved.

Seeing the touchscreen report points correctly after touching is a very satisfying outcome.

# How the driver obtains interrupts from the DTS

This part is very well summarized by Teacher Wei Dongshan, and I recommend everyone to read this article:

Obtaining interrupts described in DTS in Linux drivers

For different devices, the method of obtaining interrupts varies. A common approach is to describe the interrupt as a GPIO pin in the DTS, then in the driver, first obtain the GPIO pin, and then convert it to an interrupt.

# Summary

This part is somewhat repetitive; the touchscreen driver is a common peripheral, and the debugging difficulty is not very high. The normal debugging sequence is:

— First, ensure power supply— then ensure I2C functionality, which generally requires pull-ups, as well as I2C speed and levels.

— Next, check the address; in Linux, we generally use a 7-bit address, and it is important to note that the address may also be related to hardware design.

— Once everything above is normal, we can start troubleshooting the interrupt issue. Some GPIOs may not have interrupts enabled by default and may require software configuration.

— Then observe whether the software can enter the interrupt handler function. Some drivers support polling, but the code locations are generally similar.

Once everything is normal, we can perform a power-on test to check if the touch functionality is working correctly.

Recommended Reading: Collection | Summary of Linux Articles Collection | Programming Life Collection | C Language My Knowledge Circle

Troubleshooting a Touchscreen Driver Issue

Embedded Linux Scan the QR code to follow my public account

Leave a Comment