Checksum Errors and False touch input on Hycon 4635 and AM62

We are using the current yocto build from the official toradex homepage.

I managed to enable included hycon hy46xx driver on the AM62 using instructions on kernel module compilation and device trees.
the device is correctly loaded and reachable via i2c but I still encounter multiple issues:
The Checksum is always wrong and when corrected it only performs a click on the upper left corner.

When performing a touch the standard driver always returns a checksum error.
after some investigation I found that it always differs exactly 0x18 (24 in decimal).
After correcting the checksum with this value no checksum errors accure anymore.
When the checksum is corrected the touches can be performed with correct X/Y values.
But there is a second issue:

When touching the screen it always performs a touch at the upper left corner (at the multimedia build it always pushes the “new terminal” button).

Does anybody encounter similar issues or has an idea on where to check for errors?
I have attached the dtbo and driver files

Example debug info: compare checksum with calculated:
[80739.422083] hycon_hy46xx 2-0038: CH_CODE: 249 - 0xf9 //read buf[1]
[80739.426917] hycon_hy46xx 2-0038: CH_BUF: 0-249-1-130-70-1-71-0-0 //read buf[i] in dec
[80739.432930] hycon_hy46xx 2-0038: CH_CALC: 17 - 0x11 //add buf[2]-buf[i]

Example output of “evtest” on touch input (when checksum is corrected):
1711452718.796787, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 444
Event: time 1711452718.796787, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 252
Event: time 1711452718.796787, type 3 (EV_ABS), code 0 (ABS_X), value 444
Event: time 1711452718.796787, type 3 (EV_ABS), code 1 (ABS_Y), value 252
Event: time 1711452718.796787, -------------- SYN_REPORT ------------
Event: time 1711452718.845007, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 418
Event: time 1711452718.845007, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 242
Event: time 1711452718.845007, type 3 (EV_ABS), code 0 (ABS_X), value 418
Event: time 1711452718.845007, type 3 (EV_ABS), code 1 (ABS_Y), value 242
Event: time 1711452718.845007, -------------- SYN_REPORT ------------
Event: time 1711452718.893179, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value -1
Event: time 1711452718.893179, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1711452718.893179, -------------- SYN_REPORT ------------

verdin-am62_dtv_mezzanine_touch_lvds_overlay.dts (2.4 KB)
hycon-hy46xx.c (15.9 KB)

Hello @cillex,
Could you share more details about the display you’re using? How are you connecting it?
Are you using one of Toradex’s carrier boards?

I looked through your investigation, but I found something odd:
on number 1 you say that when you correct the checksums you get correct reads of X/Y, while on number 2 you say that touches always go to position 0.
I couldn’t really understand that, to me if the position is correct then the event should happen on that position.

Also, looking at the changes you made, I noticed you replaced the read of HY46XX_CHKSUM_LEN field (on buffer index 7) with a static 7, meaning that you always calculate the checksum from indexes 2 to 6.
From the original code, it seems that index 7 should contain the length of the returned buffer, which could in theory vary between messages.

We found on Hytec website some example drivers:

On that linux driver, we can also see that they use the index 7 for the length of the buffer, and the checksum calculation is exactly the same as the one on the upstream kernel.

It looks like the problem you’re having could be caused by some hardware issue, or maybe some timing violations on the I2C bus, which could create invalid reads of the data coming from the touch controller.

Could you maybe monitor the I2C bus with a logic analyzer and see if the data being sent on the bus matches what you see on the software side?

Best regards,
Rafael

Hi Rafael,

I am using a verdin development board with a mezzanine adapter to connect a 5inch lvds display.
The display works fine with the included dtbo and the touch panel can be read with an i2cdump on given address.
The dump shows all registers that are mentioned in the hycon datasheet holding correct values, therefore it looks like the i2c is also correctly implemented.

The kernel module driver is the one found within linux-toradex (linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules)

It differs from the official one found on hycons homepage. That one is in an old-fashioned style and includes header files not supported by the official toradex kernel.

I changed the toradex driver to read the fixed length of buf, because the buf[7] was always 0.
By changing this checksum approach I discovered, that the checksum can be calculated by adding up the remaining entries of buf and subtracting a fixed value.

When doing this I can avoid the checksum error and the driver returns correct X and Y values, what indicates that read values should be correct (no shift in the read registers and also no data loss?)

Is it possible, that the i2c is somehow false configured, while returning correct values on the registers when reading them directly?

Best Regards,
Clemens

When you mention that de device is connected to a mezzanine, do you mean the DSI->LVDS adapter?

The dump shows all registers that are mentioned in the hycon datasheet holding correct values, therefore it looks like the i2c is also correctly implemented.

That’s weird. It looks like the packets that the controller is sending don’t match what the driver is expecting (the 0 on the position where we should see the checksum confirms that). What I don’t see is why this would happen if the communication between the controller and the SoC is working well.
Maybe this touch controller has another mode where it doesn’t send the packets with checksum the way the driver expects?

Could you check if the values being read on the regmap agree with the values that you see using i2cdump?

There should be a file under /sys/debug/regmap/* for every regmap that has been created. It seems they use the address on the filename, so I imagine the name for this device could be found with:

find /sys/debug/regmap|grep 38

I am using the Mezzanine adapter board, since we are planning to use the integrated lvds.

I used the i2c-2 on the lvds port, but had to move the two touch lanes INT and RESET directly to GPIO2 and 3, because of an error in our cable design.
Regardless the changes this setup seems to work fine, display is working and touch interrupt and i2c are all running as expected.

cat /sys/kernel/debug/regmap/2-0038/* returns:
registers: 0: 00
access: 0: y y y n
range: 0-0
name: hycon-hy46xx

The checksum problem is not that bad in my opinion, since the returned values seem to be correct.
x, y, id and type are all return correct values when ignoring checksum errors
like:
finger 0 at 700 300 DOWN
finger 0 at 700 300 UP

Is it possible, that the returned touch position is false interpreted on a plain multimedia image?

It looks like the device is not behaving the way the driver expects it to behave. That regmap with 0 registers doesn’t look right.

Because nothing on this touch controller seems to be behaving the way the linux driver expects, I wouldn’t be surprised if the information being passed on to the input device layer is also wrong as a result.

This could be causing the issues that you’re seeing and could be worth a review, but again, this driver shouldn’t require this kind of modification to work, since it’s on the upstream kernel and supposedly created for this specific device.

I checked all outputs of the driver and it looks like it is reading from the regmap and the entries match the i2c registers, but when checking /sys/kernel/debug/regmap/2-0038/* it alwys shows 0 (as mentioned before).

Is it possible, that the regmap needs to be initialized / configured manually?
Either in the dts /driver or an additional module?

From what I could see on the driver, the only interface it has to the device is through the regmap. It abstracts the i2c bus operations that need to be executed to read the registers and creates the illusion that the driver can just read the device’s registers like they were mem mapped.
The regmap is being initialized on the probe function of the driver, and to my knowledge it should just work:

503         tsdata->regmap = devm_regmap_init_i2c(client,                                                                                   
504                                               &hycon_hy46xx_i2c_regmap_config);                                                         
505         if (IS_ERR(tsdata->regmap)) {                                                                                                   
506                 dev_err(&client->dev, "regmap allocation failed\n");                                                                    
507                 return PTR_ERR(tsdata->regmap);                                                                                         
508         }

When I mentioned checking the driver’s output, I meant to check what the driver is feeding into the linux input system. Maybe you could add some debug here:

100 static irqreturn_t hycon_hy46xx_isr(int irq, void *dev_id)                                                                              
101 {                                                                                                                                       
102         struct hycon_hy46xx_data *tsdata = dev_id;                                                                                      
103         struct device *dev = &tsdata->client->dev;                                                                                      
104         u8 rdbuf[HY46XX_REPORT_PKT_LEN];                                                                                                
105         int i, x, y, id;                                                                                                                
106         int error;                                                                                                                      
107                                                                                                                                         
108         memset(rdbuf, 0, sizeof(rdbuf));                                                                                                
109                                                                                                                                         
110         error = regmap_bulk_read(tsdata->regmap, 0, rdbuf, sizeof(rdbuf));                                                              
111         if (error) {                                                                                                                    
112                 dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",                                                           
113                                     error);                                                                                             
114                 goto out;                                                                                                               
115         }                                                                                                                               
116                                                                                                                                         
117         if (!hycon_hy46xx_check_checksum(tsdata, rdbuf))                                                                                
118                 goto out;                                                                                                               
119                                                                                                                                         
120         for (i = 0; i < HY46XX_MAX_SUPPORTED_POINTS; i++) {                                                                             
121                 u8 *buf = &rdbuf[3 + (HY46XX_TPLEN * i)];                                                                               
122                 int type = buf[0] >> 6;                                                                                                 
123                                                                                                                                         
124                 if (type == TOUCH_EVENT_RESERVED)                                                                                       
125                         continue;                                                                                                       
126                                                                                                                                         
127                 x = get_unaligned_be16(buf) & 0x0fff;                                                                                   
128                 y = get_unaligned_be16(buf + 2) & 0x0fff;                                                                               
129                                                                                                                                         
130                 id = buf[2] >> 4;                                                                                                       
131                                                                                                                                         
132                 input_mt_slot(tsdata->input, id);                                                                                       
133                 if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,                                                           
134                                                type != TOUCH_EVENT_UP))                                                                 
135                         touchscreen_report_pos(tsdata->input, &tsdata->prop,                                                            
136                                                x, y, true);                                                                             
137         }

For instance, the driver is looping through the rdbuf using HY46XX_MAX_SUPPORTED_POINTS, but it seems from your previous output that most of the data at the end of the buffer is just 0’s. I don’t know if this could affect the results, but it would be interesting to dig a little more around that.

This is exactly where I got most of the information from.

When printing rdbuf it directly represents the i2c registers from 00 to HY46XX_REPORT_PKT_LEN
where the first entry is always 0, the second is the checksum and after that it holds the x/y coordinates of the fingers and their state (up/Down/…).
(this datasheet shows the register information of the controller, it is identical to the model 4635:
HY463x_Application Notes_EN_Preliminary V3.0.pdf (1.4 MB)

This also explains why using the position buf[HY46XX_CHKSUM_LEN] doesn’t make sense, since it is not used to hold the checksum length.

However, the data in rdbuf is fine and therefore interpreted correctly by the driver.
EVTEST also reads all values correctly, when the Checksum is ignored, including the touch down/release information of all fingers.
But when releasing the last finger EVTEST shows the XY coordinates and finger UP state last but the multimedia image clicks on the “New terminal” button in the upper left corner anyway.

When checking the entries in “/sys/kerneld/debug/regmap/2-0038/” it is all empty, but the device is listed at:
root@verdin-am62-15133540:~# cat /sys/class/i2c-dev/i2c-2/device/2-0038/uevent
OF_NAME=touchscreen
OF_FULLNAME=/bus@f0000/i2c@20020000/touchscreen@38
OF_COMPATIBLE_0=hycon,hy4635
OF_COMPATIBLE_N=1
MODALIAS=of:NtouchscreenT(null)Chycon,hy4635

I looked at the datasheet you sent. What I find weird is that there’s no mention at all of this buffer format that the driver expects. The driver expects to see a checksum in a certain position of the buffer that’s being read, but apparently, it’s not there. The datasheet seems to indicate that the chip only provides a memory-mapped view of the registers, without any other formatting.

Where’s the missing link? Is there some other documentation from the manufacturer that explains the protocol to communicate with the device?

I find it hard to believe that the touch event would somehow be changed inside the input handling framework (like you get the correct event from the driver but it gets changed and instead of the correct coordinate, it clicks on the top of the screen) because the same input handling presumably works for different touch input devices. At least for the moment, I would try to focus on what the touch controller driver can do that would cause the behavior you see.

Hi Rafael,

I got some new information from Hycon directly:
To increase performance they skipped the checksum check.
That’s why it is necessary to also skip the check on the driver side.
I adjusted the driver accordingly (for now I just commented the checksum parts out).

After checking the result with evtest I can see clean and correct values, but no correct touch command is accomplished:

Event: time 1712237181.441785, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 368
Event: time 1712237181.441785, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 194
Event: time 1712237181.441785, type 3 (EV_ABS), code 0 (ABS_X), value 368
Event: time 1712237181.441785, type 3 (EV_ABS), code 1 (ABS_Y), value 194
Event: time 1712237181.441785, -------------- SYN_REPORT ------------
Event: time 1712237181.463225, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 376
Event: time 1712237181.463225, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 219
Event: time 1712237181.463225, type 3 (EV_ABS), code 0 (ABS_X), value 376
Event: time 1712237181.463225, type 3 (EV_ABS), code 1 (ABS_Y), value 219
Event: time 1712237181.463225, -------------- SYN_REPORT ------------
Event: time 1712237181.467619, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value -1
Event: time 1712237181.467619, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1712237181.467619, -------------- SYN_REPORT ------------

On the multimedia image it still performs a “click” on the “new terminal” icon in the upper left corner.

Do you maybe have a suggestion on where to search for a fix? Other drivers or devices in the devicetree that could lead to this behavior?

Best Regards,
Clemens

To increase performance they skipped the checksum check.

With this, I see that they made the open-source driver completely unusable. Are there other changes they made to the data format that’s returned by the chip?

Do you maybe have a suggestion on where to search for a fix? Other drivers or devices in the devicetree that could lead to this behavior?

I haven’t checked yet but I would assume the touch controller drivers that come with the displays we sell in our store are properly working on the multimedia image. Maybe doing a test like this could indicate what might be wrong.

I connected our 10" capacitive touch display to a Verdin Development Board with a Verdin AM62 1GB IT. I used the mezzanine overlay:

root@verdin-am62-15133562:/home/weston# cat /boot/overlays.txt 
fdt_overlays=verdin-am62_mezzanine_panel-cap-touch-10inch-lvds_overlay.dtbo

With this, I got a working display and the touch controller works. There are no touch events out of place, when I touch a window it gets selected. When I touch the “new terminal” icon, it gets clicked. The only weird thing I noticed is that the touch threshold seems to be very short, so sometimes pressing the icon once would open 3 windows. But I guess this threshold is adjustable somehow.

Here’s a log from evtest of this test:
log_events.txt (181.1 KB)

I hope this helps,
Rafael

Hello Rafael,

I tested my setup with a minimal image and lvgl as UI framework and with this it is possible to correctly read all touch inputs from the hycon touch driver found at (in this case) event9.

There must be some kind of layer within linux that installs event devices as input devices that doesn’t recognize the touch controller as it is set up at the moment.

I will try finding this issue but at the moment the current setup works for me.

Best Regards,
Clemens

That’s interesting.
Did you implement the touch controller input handling yourself within lvgl, or are you using a lvgl driver that’s written to handle linux input events?