Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/input/touch.c
3694 views
1
/*
2
* Touch driver for Nintendo Switch's STM FingerTip S (FTM4CD60DA1BE/FTM4CD50TA1BE) touch controller
3
*
4
* Copyright (c) 2018 langerhans
5
* Copyright (c) 2018-2026 CTCaer
6
*
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms and conditions of the GNU General Public License,
9
* version 2, as published by the Free Software Foundation.
10
*
11
* This program is distributed in the hope it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
* more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include <string.h>
21
22
#include <soc/clock.h>
23
#include <soc/i2c.h>
24
#include <soc/pinmux.h>
25
#include <power/max7762x.h>
26
#include <soc/gpio.h>
27
#include <soc/timer.h>
28
#include <soc/t210.h>
29
#include <utils/btn.h>
30
#include "touch.h"
31
32
static touch_panel_info_t _panels[] =
33
{
34
{ 0, 1, 1, 1, "NISSHA NFT-K12D" },// 0.
35
{ 1, 0, 1, 1, "GiS GGM6 B2X" },// 1.
36
{ 2, 0, 0, 0, "NISSHA NBF-K9A" },// 3.
37
{ 3, 1, 0, 0, "GiS 5.5\"" },// 4.
38
{ 4, 0, 0, 1, "Samsung TSP" },// 5?
39
{ -1, 1, 0, 1, "GiS VA 6.2\"" } // 2.
40
};
41
42
static touch_info_t _touch_info = { 0 };
43
static touch_panel_info_t _touch_panel_info = { 0 };
44
45
static int _touch_command(u8 cmd, u8 *buf, u8 size)
46
{
47
return i2c_send_buf_small(I2C_3, FTS4_I2C_ADDR, cmd, buf, size);
48
}
49
50
static int _touch_read_reg(u8 *cmd, u32 csize, u8 *buf, u32 size)
51
{
52
return i2c_xfer_packet(I2C_3, FTS4_I2C_ADDR, cmd, csize, buf, size);
53
}
54
55
int touch_get_event_count()
56
{
57
u8 cmd[3] = { FTS4_CMD_HW_REG_READ, 0, FTS4_HW_REG_EVENT_COUNT };
58
u8 buf[2];
59
60
if (_touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf)))
61
return 0;
62
63
return (buf[1] >> 1);
64
}
65
66
static int _touch_wait_event(u8 event, u8 status, u32 timeout, u8 *buf)
67
{
68
u32 timer = get_tmr_ms() + timeout;
69
while (true)
70
{
71
if (!touch_get_event_count())
72
goto retry;
73
74
u8 tmp[FTS4_EVENT_SIZE];
75
int res = i2c_recv_buf_big(tmp, FTS4_EVENT_SIZE, I2C_3, FTS4_I2C_ADDR, FTS4_CMD_READ_ONE_EVENT);
76
77
// Check that event type and status match.
78
if (!res && tmp[0] == event && tmp[1] == status)
79
{
80
if (buf)
81
memcpy(buf, &tmp[2], 6);
82
return 0;
83
}
84
85
retry:
86
usleep(500);
87
88
if (get_tmr_ms() > timer)
89
return 1;
90
}
91
}
92
93
#define X_REAL_MAX 1264
94
#define Y_REAL_MAX 704
95
#define EDGE_OFFSET 15
96
97
static void _touch_compensate_limits(touch_event_t *event, bool touching)
98
{
99
event->x = MAX(event->x, EDGE_OFFSET);
100
event->x = MIN(event->x, X_REAL_MAX);
101
event->x -= EDGE_OFFSET;
102
u32 x_adj = (1280 * 1000) / (X_REAL_MAX - EDGE_OFFSET);
103
event->x = ((u32)event->x * x_adj) / 1000;
104
105
if (touching)
106
{
107
event->y = MAX(event->y, EDGE_OFFSET);
108
event->y = MIN(event->y, Y_REAL_MAX);
109
event->y -= EDGE_OFFSET;
110
u32 y_adj = (720 * 1000) / (Y_REAL_MAX - EDGE_OFFSET);
111
event->y = ((u32)event->y * y_adj) / 1000;
112
}
113
}
114
115
static void _touch_process_contact_event(touch_event_t *event, bool touching)
116
{
117
event->x = (event->raw[1] << 4) | ((event->raw[3] & FTS4_MASK_Y_LSB) >> 4);
118
119
// Normally, GUI elements have bigger horizontal estate.
120
// Avoid parsing y axis when finger is removed to minimize touch noise.
121
if (touching)
122
{
123
event->y = (event->raw[2] << 4) | (event->raw[3] & FTS4_MASK_X_MSB);
124
125
event->z = event->raw[4] | (event->raw[5] << 8);
126
event->z = event->z << 6;
127
128
u16 tmp = 0x40;
129
if ((event->raw[6] & 0x3F) != 1 && (event->raw[6] & 0x3F) != 0x3F)
130
tmp = event->raw[6] & 0x3F;
131
event->z /= tmp + 0x40;
132
133
event->finger = ((event->raw[0] & FTS4_MASK_TOUCH_ID) >> 4) + 1;
134
}
135
else
136
event->finger = 0;
137
138
_touch_compensate_limits(event, touching);
139
}
140
141
static int _touch_parse_input_event(touch_event_t *event)
142
{
143
switch (event->raw[0] & FTS4_MASK_EVENT_ID)
144
{
145
case FTS4_EV_MULTI_TOUCH_ENTER:
146
case FTS4_EV_MULTI_TOUCH_MOTION:
147
_touch_process_contact_event(event, true);
148
if (event->z < 500) // Reject palm rest.
149
event->touch = true;
150
else
151
event->touch = false;
152
return 0;
153
154
case FTS4_EV_MULTI_TOUCH_LEAVE:
155
event->touch = false;
156
_touch_process_contact_event(event, false);
157
return 0;
158
159
default:
160
return 1; // No event.
161
}
162
}
163
164
int touch_poll(touch_event_t *event)
165
{
166
u8 cmd = !_touch_info.clone ? FTS4_CMD_LATEST_EVENT : FTS4_CMD_READ_ONE_EVENT;
167
168
int res = i2c_recv_buf_big(event->raw, FTS4_EVENT_SIZE, I2C_3, FTS4_I2C_ADDR, cmd);
169
if (!res)
170
res = _touch_parse_input_event(event);
171
172
return res;
173
}
174
175
touch_info_t *touch_get_chip_info()
176
{
177
u8 buf[7] = { 0 };
178
179
// Get chip info.
180
u8 cmd[3] = { FTS4_CMD_HW_REG_READ, 0, FTS4_HW_REG_CHIP_ID_INFO };
181
if (_touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf)))
182
{
183
memset(&_touch_info, 0, sizeof(touch_info_t));
184
goto exit;
185
}
186
187
_touch_info.chip_id = buf[1] << 8 | buf[2];
188
_touch_info.fw_ver = buf[3] << 8 | buf[4];
189
_touch_info.config_id = buf[5];
190
_touch_info.config_ver = buf[6];
191
192
// Validate that device is genuine or proper.
193
cmd[2] = 2;
194
_touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf));
195
_touch_info.clone = _touch_info.chip_id != (buf[3] << 8 | buf[4]);
196
197
exit:
198
return &_touch_info;
199
}
200
201
touch_panel_info_t *touch_get_panel_vendor()
202
{
203
_touch_panel_info.idx = -2;
204
205
u8 cmd = FTS4_VENDOR_GPIO_STATE;
206
if (_touch_command(FTS4_CMD_VENDOR, &cmd, 1))
207
return NULL;
208
209
u8 buf[6] = { 0 };
210
if (_touch_wait_event(FTS4_EV_VENDOR, FTS4_VENDOR_GPIO_STATE, 2000, buf))
211
return NULL;
212
213
for (u32 i = 0; i < ARRAY_SIZE(_panels); i++)
214
{
215
touch_panel_info_t *panel = &_panels[i];
216
if (buf[0] == panel->gpio0 && buf[1] == panel->gpio1 && buf[2] == panel->gpio2)
217
return panel;
218
}
219
220
// Touch panel not found, return current gpios.
221
_touch_panel_info.gpio0 = buf[0];
222
_touch_panel_info.gpio1 = buf[1];
223
_touch_panel_info.gpio2 = buf[2];
224
225
return &_touch_panel_info;
226
}
227
228
int touch_get_fw_info(touch_fw_info_t *fw)
229
{
230
u8 buf[9] = { 0 };
231
232
memset(fw, 0, sizeof(touch_fw_info_t));
233
234
// Get fw address info.
235
u8 cmd[3] = { FTS4_CMD_FB_REG_READ, 0, 0x60 };
236
int res = _touch_read_reg(cmd, sizeof(cmd), buf, 3);
237
if (!res)
238
{
239
// Get fw info.
240
cmd[1] = buf[2]; cmd[2] = buf[1];
241
res = _touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf));
242
if (!res)
243
{
244
fw->fw_id = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
245
fw->ftb_ver = (buf[6] << 8) | buf[5];
246
fw->fw_rev = (buf[8] << 8) | buf[7];
247
}
248
}
249
250
return res;
251
}
252
253
int touch_sys_reset()
254
{
255
u8 cmd[3] = { 0, FTS4_HW_REG_SYS_RESET, 0x80 }; // System reset cmd.
256
for (u8 retries = 0; retries < 3; retries++)
257
{
258
if (_touch_command(FTS4_CMD_HW_REG_WRITE, cmd, 3))
259
{
260
msleep(10);
261
continue;
262
}
263
msleep(10);
264
265
if (_touch_wait_event(FTS4_EV_CONTROLLER_READY, 0, 20, NULL))
266
continue;
267
else
268
return 0;
269
}
270
271
return 1;
272
}
273
274
int touch_panel_ito_test(u8 *err)
275
{
276
// Check that touch IC is supported.
277
touch_info_t *info = touch_get_chip_info();
278
if (info->chip_id != FTS4_I2C_CHIP_ID || info->clone)
279
return 1;
280
281
// Reset touchscreen module.
282
if (touch_sys_reset())
283
return 1;
284
285
// Do ITO Production test.
286
if (_touch_command(FTS4_CMD_ITO_CHECK, NULL, 0))
287
return 1;
288
289
u8 buf[6] = { 0 };
290
int res = _touch_wait_event(FTS4_EV_ERROR, FTS4_EV_ERROR_ITO_TEST, 2000, buf);
291
if (!res && err)
292
{
293
err[0] = buf[0];
294
err[1] = buf[1];
295
}
296
297
// Reset touchscreen module.
298
touch_sys_reset();
299
300
return res;
301
}
302
303
int touch_get_fb_info(u8 *buf)
304
{
305
u8 cmd[3] = { FTS4_CMD_FB_REG_READ, 0, 0 };
306
int res = 0;
307
308
for (u32 i = 0; i < 0x10000; i += 4)
309
{
310
if (!res)
311
{
312
cmd[1] = (i >> 8) & 0xFF;
313
cmd[2] = i & 0xFF;
314
315
u8 tmp[5];
316
memset(tmp, 0xCC, sizeof(tmp));
317
res = _touch_read_reg(cmd, sizeof(cmd), tmp, sizeof(tmp));
318
memcpy(&buf[i], tmp + 1, 4);
319
}
320
}
321
322
return res;
323
}
324
325
int touch_switch_sense_mode(u8 mode, bool gis_6_2)
326
{
327
// Set detection config.
328
u8 cmd[3] = { 1, 0x64, 0 };
329
330
switch (mode)
331
{
332
case FTS4_STYLUS_MODE:
333
cmd[2] = !gis_6_2 ? 0xC8 : 0xAD;
334
break;
335
case FTS4_FINGER_MODE:
336
cmd[2] = !gis_6_2 ? 0x8C : 0x79;
337
break;
338
}
339
340
if (_touch_command(FTS4_CMD_DETECTION_CONFIG, cmd, 3))
341
return 1;
342
343
// Sense mode.
344
cmd[0] = mode;
345
346
return _touch_command(FTS4_CMD_SWITCH_SENSE_MODE, cmd, 1);
347
}
348
349
int touch_sense_enable()
350
{
351
// Switch sense mode and enable multi-touch sensing.
352
u8 cmd = FTS4_FINGER_MODE;
353
if (_touch_command(FTS4_CMD_SWITCH_SENSE_MODE, &cmd, 1))
354
return 1;
355
356
if (_touch_command(FTS4_CMD_MS_MT_SENSE_ON, NULL, 0))
357
return 1;
358
359
if (_touch_command(FTS4_CMD_CLEAR_EVENT_STACK, NULL, 0))
360
return 1;
361
362
return 0;
363
}
364
365
int touch_execute_autotune()
366
{
367
u8 buf[6] = { 0 };
368
369
// Reset touchscreen module.
370
if (touch_sys_reset())
371
return 1;
372
373
// Trim low power oscillator.
374
if (_touch_command(FTS4_CMD_LP_TIMER_CALIB, NULL, 0))
375
return 1;
376
377
msleep(200);
378
379
// Apply Mutual Sense Compensation tuning.
380
if (_touch_command(FTS4_CMD_MS_CX_TUNING, NULL, 0))
381
return 1;
382
if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_MS_CX_TUNING_DONE, 2000, buf) || buf[0] || buf[1])
383
return 1;
384
385
// Apply Self Sense Compensation tuning.
386
if (_touch_command(FTS4_CMD_SS_CX_TUNING, NULL, 0))
387
return 1;
388
if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_SS_CX_TUNING_DONE, 2000, buf) || buf[0] || buf[1])
389
return 1;
390
391
// Save Compensation data to EEPROM.
392
if (_touch_command(FTS4_CMD_SAVE_CX_TUNING, NULL, 0))
393
return 1;
394
if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_WRITE_CX_TUNE_DONE, 2000, buf) || buf[0] || buf[1])
395
return 1;
396
397
return touch_sense_enable();
398
}
399
400
static int touch_init()
401
{
402
// Check that touch IC is supported.
403
touch_info_t *info = touch_get_chip_info();
404
if (info->chip_id != FTS4_I2C_CHIP_ID)
405
return 1;
406
407
// Initialize touchscreen module.
408
if (touch_sys_reset())
409
return 1;
410
411
return touch_sense_enable();
412
}
413
414
int touch_power_on()
415
{
416
// Configure touchscreen Touch Reset pin.
417
PINMUX_AUX(PINMUX_AUX_DAP4_SCLK) = PINMUX_PULL_DOWN | 1;
418
gpio_direction_output(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
419
usleep(20);
420
421
// Enable LDO6 for touchscreen AVDD and DVDD supply.
422
max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
423
max7762x_regulator_enable(REGULATOR_LDO6, true);
424
425
// Initialize I2C3.
426
pinmux_config_i2c(I2C_3);
427
clock_enable_i2c(I2C_3);
428
i2c_init(I2C_3);
429
usleep(1000);
430
431
// Set Touch Reset pin.
432
gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_HIGH);
433
usleep(10000);
434
435
// Wait for the touchscreen module to get ready.
436
_touch_wait_event(FTS4_EV_CONTROLLER_READY, 0, 20, NULL);
437
438
// Check for forced boot time calibration.
439
if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
440
{
441
u8 err[2];
442
if (!touch_panel_ito_test(err))
443
if (!err[0] && !err[1])
444
return touch_execute_autotune();
445
}
446
447
// Initialize touchscreen.
448
u32 retries = 3;
449
while (retries)
450
{
451
if (!touch_init())
452
return 0;
453
retries--;
454
}
455
456
return 1;
457
}
458
459
void touch_power_off()
460
{
461
// Disable touchscreen power.
462
gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
463
464
// Disables LDO6 for touchscreen VDD, AVDD supply
465
max7762x_regulator_enable(REGULATOR_LDO6, false);
466
467
clock_disable_i2c(I2C_3);
468
}
469
470