Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/input/touch.c
1476 views
1
/*
2
* Touch driver for Nintendo Switch's STM FingerTip S (4CD60D) touch controller
3
*
4
* Copyright (c) 2018 langerhans
5
* Copyright (c) 2018-2023 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
33
#include <gfx_utils.h>
34
#define DPRINTF(...) gfx_printf(__VA_ARGS__)
35
36
static touch_panel_info_t _panels[] =
37
{
38
{ 0, 1, 1, 1, "NISSHA NFT-K12D" },// 0.
39
{ 1, 0, 1, 1, "GiS GGM6 B2X" },// 1.
40
{ 2, 0, 0, 0, "NISSHA NBF-K9A" },// 3.
41
{ 3, 1, 0, 0, "GiS 5.5\"" },// 4.
42
{ 4, 0, 0, 1, "Samsung TSP" },// 5?
43
{ -1, 1, 0, 1, "GiS VA 6.2\"" } // 2.
44
};
45
46
static int touch_command(u8 cmd, u8 *buf, u8 size)
47
{
48
int res = i2c_send_buf_small(I2C_3, STMFTS_I2C_ADDR, cmd, buf, size);
49
if (!res)
50
return 1;
51
return 0;
52
}
53
54
static int touch_read_reg(u8 *cmd, u32 csize, u8 *buf, u32 size)
55
{
56
int res = i2c_send_buf_small(I2C_3, STMFTS_I2C_ADDR, cmd[0], &cmd[1], csize - 1);
57
if (res)
58
res = i2c_recv_buf(buf, size, I2C_3, STMFTS_I2C_ADDR);
59
if (!res)
60
return 1;
61
62
return 0;
63
}
64
65
static int touch_wait_event(u8 event, u8 status, u32 timeout, u8 *buf)
66
{
67
u32 timer = get_tmr_ms() + timeout;
68
while (true)
69
{
70
u8 tmp[8] = {0};
71
i2c_recv_buf_small(tmp, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_ONE_EVENT);
72
if (tmp[1] == event && tmp[2] == status)
73
{
74
if (buf)
75
memcpy(buf, &tmp[3], 5);
76
return 0;
77
}
78
79
if (get_tmr_ms() > timer)
80
return 1;
81
}
82
}
83
84
#define X_REAL_MAX 1264
85
#define Y_REAL_MAX 704
86
#define EDGE_OFFSET 15
87
88
static void _touch_compensate_limits(touch_event *event, bool touching)
89
{
90
event->x = MAX(event->x, EDGE_OFFSET);
91
event->x = MIN(event->x, X_REAL_MAX);
92
event->x -= EDGE_OFFSET;
93
u32 x_adj = (1280 * 1000) / (X_REAL_MAX - EDGE_OFFSET);
94
event->x = ((u32)event->x * x_adj) / 1000;
95
96
if (touching)
97
{
98
event->y = MAX(event->y, EDGE_OFFSET);
99
event->y = MIN(event->y, Y_REAL_MAX);
100
event->y -= EDGE_OFFSET;
101
u32 y_adj = (720 * 1000) / (Y_REAL_MAX - EDGE_OFFSET);
102
event->y = ((u32)event->y * y_adj) / 1000;
103
}
104
}
105
106
static void _touch_process_contact_event(touch_event *event, bool touching)
107
{
108
event->x = (event->raw[2] << 4) | ((event->raw[4] & STMFTS_MASK_Y_LSB) >> 4);
109
110
// Normally, GUI elements have bigger horizontal estate.
111
// Avoid parsing y axis when finger is removed to minimize touch noise.
112
if (touching)
113
{
114
event->y = (event->raw[3] << 4) | (event->raw[4] & STMFTS_MASK_X_MSB);
115
116
event->z = event->raw[5] | (event->raw[6] << 8);
117
event->z = event->z << 6;
118
119
u16 tmp = 0x40;
120
if ((event->raw[7] & 0x3F) != 1 && (event->raw[7] & 0x3F) != 0x3F)
121
tmp = event->raw[7] & 0x3F;
122
event->z /= tmp + 0x40;
123
124
event->fingers = ((event->raw[1] & STMFTS_MASK_TOUCH_ID) >> 4) + 1;
125
}
126
else
127
event->fingers = 0;
128
129
_touch_compensate_limits(event, touching);
130
}
131
132
static void _touch_parse_event(touch_event *event)
133
{
134
event->type = event->raw[1] & STMFTS_MASK_EVENT_ID;
135
136
switch (event->type)
137
{
138
case STMFTS_EV_MULTI_TOUCH_ENTER:
139
case STMFTS_EV_MULTI_TOUCH_MOTION:
140
_touch_process_contact_event(event, true);
141
if (event->z < 255) // Reject palm rest.
142
event->touch = true;
143
else
144
{
145
event->touch = false;
146
event->type = STMFTS_EV_MULTI_TOUCH_LEAVE;
147
}
148
break;
149
case STMFTS_EV_MULTI_TOUCH_LEAVE:
150
event->touch = false;
151
_touch_process_contact_event(event, false);
152
break;
153
case STMFTS_EV_NO_EVENT:
154
if (event->touch)
155
event->type = STMFTS_EV_MULTI_TOUCH_MOTION;
156
break;
157
default:
158
if (event->touch && event->raw[0] == STMFTS_EV_MULTI_TOUCH_MOTION)
159
event->type = STMFTS_EV_MULTI_TOUCH_MOTION;
160
else
161
event->type = STMFTS_EV_MULTI_TOUCH_LEAVE;
162
}
163
164
// gfx_con_setpos(0, 300);
165
// DPRINTF("x = %d \ny = %d \nz = %d \n", event->x, event->y, event->z);
166
// DPRINTF("0 = %02X\n1 = %02X\n2 = %02X\n3 = %02X\n", event->raw[0], event->raw[1], event->raw[2], event->raw[3]);
167
// DPRINTF("4 = %02X\n5 = %02X\n6 = %02X\n7 = %02X\n", event->raw[4], event->raw[5], event->raw[6], event->raw[7]);
168
}
169
170
void touch_poll(touch_event *event)
171
{
172
i2c_recv_buf_small(event->raw, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_LATEST_EVENT);
173
174
_touch_parse_event(event);
175
}
176
177
touch_event touch_poll_wait()
178
{
179
touch_event event;
180
do
181
{
182
touch_poll(&event);
183
} while (event.type != STMFTS_EV_MULTI_TOUCH_LEAVE);
184
185
return event;
186
}
187
188
touch_info touch_get_info()
189
{
190
touch_info info;
191
u8 buf[8];
192
memset(&buf, 0, 8);
193
i2c_recv_buf_small(buf, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_INFO);
194
195
info.chip_id = buf[0] << 8 | buf[1];
196
info.fw_ver = buf[2] << 8 | buf[3];
197
info.config_id = buf[4];
198
info.config_ver = buf[5];
199
200
//DPRINTF("ID: %04X, FW Ver: %d.%02d\nCfg ID: %02X, Cfg Ver: %d\n",
201
// info.chip_id, info.fw_ver >> 8, info.fw_ver & 0xFF, info.config_id, info.config_ver);
202
203
return info;
204
}
205
206
touch_panel_info_t *touch_get_panel_vendor()
207
{
208
u8 buf[5] = {0};
209
u8 cmd = STMFTS_VENDOR_GPIO_STATE;
210
static touch_panel_info_t panel_info = { -2, 0, 0, 0, ""};
211
212
if (touch_command(STMFTS_VENDOR, &cmd, 1))
213
return NULL;
214
215
if (touch_wait_event(STMFTS_EV_VENDOR, STMFTS_VENDOR_GPIO_STATE, 2000, buf))
216
return NULL;
217
218
for (u32 i = 0; i < ARRAY_SIZE(_panels); i++)
219
{
220
touch_panel_info_t *panel = &_panels[i];
221
if (buf[0] == panel->gpio0 && buf[1] == panel->gpio1 && buf[2] == panel->gpio2)
222
return panel;
223
}
224
225
// Touch panel not found, return current gpios.
226
panel_info.gpio0 = buf[0];
227
panel_info.gpio1 = buf[1];
228
panel_info.gpio2 = buf[2];
229
230
return &panel_info;
231
}
232
233
int touch_get_fw_info(touch_fw_info_t *fw)
234
{
235
u8 buf[8] = {0};
236
237
memset(fw, 0, sizeof(touch_fw_info_t));
238
239
// Get fw address info.
240
u8 cmd[3] = { STMFTS_RW_FRAMEBUFFER_REG, 0, 0x60 };
241
int res = touch_read_reg(cmd, 3, buf, 3);
242
if (!res)
243
{
244
// Get fw info.
245
cmd[1] = buf[2]; cmd[2] = buf[1];
246
res = touch_read_reg(cmd, 3, buf, 8);
247
if (!res)
248
{
249
fw->fw_id = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
250
fw->ftb_ver = (buf[6] << 8) | buf[5];
251
}
252
253
cmd[2]++;
254
res = touch_read_reg(cmd, 3, buf, 8);
255
if (!res)
256
fw->fw_rev = (buf[7] << 8) | buf[6];
257
}
258
259
return res;
260
}
261
262
int touch_sys_reset()
263
{
264
u8 cmd[3] = { 0, 0x28, 0x80 }; // System reset cmd.
265
for (u8 retries = 0; retries < 3; retries++)
266
{
267
if (touch_command(STMFTS_WRITE_REG, cmd, 3))
268
{
269
msleep(10);
270
continue;
271
}
272
msleep(10);
273
if (touch_wait_event(STMFTS_EV_CONTROLLER_READY, 0, 20, NULL))
274
continue;
275
else
276
return 0;
277
}
278
279
return 1;
280
}
281
282
int touch_panel_ito_test(u8 *err)
283
{
284
int res = 0;
285
286
// Reset touchscreen module.
287
if (touch_sys_reset())
288
return res;
289
290
// Do ITO Production test.
291
u8 cmd[2] = { 1, 0 };
292
if (touch_command(STMFTS_ITO_CHECK, cmd, 2))
293
return res;
294
295
u32 timer = get_tmr_ms() + 2000;
296
while (true)
297
{
298
u8 tmp[8] = {0};
299
i2c_recv_buf_small(tmp, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_ONE_EVENT);
300
if (tmp[1] == 0xF && tmp[2] == 0x5)
301
{
302
if (err)
303
{
304
err[0] = tmp[3];
305
err[1] = tmp[4];
306
}
307
308
res = 1;
309
break;
310
}
311
312
if (get_tmr_ms() > timer)
313
break;
314
}
315
316
// Reset touchscreen module.
317
touch_sys_reset();
318
319
return res;
320
}
321
322
int touch_get_fb_info(u8 *buf)
323
{
324
u8 tmp[5];
325
326
u8 cmd[3] = { STMFTS_RW_FRAMEBUFFER_REG, 0, 0 };
327
int res = 0;
328
329
330
for (u32 i = 0; i < 0x10000; i += 4)
331
{
332
if (!res)
333
{
334
cmd[1] = (i >> 8) & 0xFF;
335
cmd[2] = i & 0xFF;
336
memset(tmp, 0xCC, 5);
337
res = touch_read_reg(cmd, 3, tmp, 5);
338
memcpy(&buf[i], tmp + 1, 4);
339
}
340
}
341
342
return res;
343
}
344
345
int touch_sense_enable()
346
{
347
// Switch sense mode and enable multi-touch sensing.
348
u8 cmd = STMFTS_FINGER_MODE;
349
if (touch_command(STMFTS_SWITCH_SENSE_MODE, &cmd, 1))
350
return 0;
351
352
if (touch_command(STMFTS_MS_MT_SENSE_ON, NULL, 0))
353
return 0;
354
355
if (touch_command(STMFTS_CLEAR_EVENT_STACK, NULL, 0))
356
return 0;
357
358
return 1;
359
}
360
361
int touch_execute_autotune()
362
{
363
// Reset touchscreen module.
364
if (touch_sys_reset())
365
return 0;
366
367
// Trim low power oscillator.
368
if (touch_command(STMFTS_LP_TIMER_CALIB, NULL, 0))
369
return 0;
370
msleep(200);
371
372
// Apply Mutual Sense Compensation tuning.
373
if (touch_command(STMFTS_MS_CX_TUNING, NULL, 0))
374
return 0;
375
if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_MS_CX_TUNING_DONE, 2000, NULL))
376
return 0;
377
378
// Apply Self Sense Compensation tuning.
379
if (touch_command(STMFTS_SS_CX_TUNING, NULL, 0))
380
return 0;
381
if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_SS_CX_TUNING_DONE, 2000, NULL))
382
return 0;
383
384
// Save Compensation data to EEPROM.
385
if (touch_command(STMFTS_SAVE_CX_TUNING, NULL, 0))
386
return 0;
387
if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_WRITE_CX_TUNE_DONE, 2000, NULL))
388
return 0;
389
390
return touch_sense_enable();
391
}
392
393
static int touch_init()
394
{
395
// Initialize touchscreen module.
396
if (touch_sys_reset())
397
return 0;
398
399
return touch_sense_enable();
400
}
401
402
int touch_power_on()
403
{
404
// Configure Touscreen and GCAsic shared GPIO.
405
PINMUX_AUX(PINMUX_AUX_CAM_I2C_SDA) = PINMUX_LPDR | PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 2;
406
PINMUX_AUX(PINMUX_AUX_CAM_I2C_SCL) = PINMUX_IO_HV | PINMUX_LPDR | PINMUX_TRISTATE | PINMUX_PULL_DOWN | 2; // Unused.
407
gpio_config(GPIO_PORT_S, GPIO_PIN_3, GPIO_MODE_GPIO); // GC detect.
408
409
// Configure touchscreen Touch Reset pin.
410
PINMUX_AUX(PINMUX_AUX_DAP4_SCLK) = PINMUX_PULL_DOWN | 1;
411
gpio_direction_output(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
412
usleep(20);
413
414
// Enable LDO6 for touchscreen AVDD and DVDD supply.
415
max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
416
max7762x_regulator_enable(REGULATOR_LDO6, true);
417
418
// Initialize I2C3.
419
pinmux_config_i2c(I2C_3);
420
clock_enable_i2c(I2C_3);
421
i2c_init(I2C_3);
422
usleep(1000);
423
424
// Set Touch Reset pin.
425
gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_HIGH);
426
usleep(10000);
427
428
// Wait for the touchscreen module to get ready.
429
touch_wait_event(STMFTS_EV_CONTROLLER_READY, 0, 20, NULL);
430
431
// Check for forced boot time calibration.
432
if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
433
{
434
u8 err[2];
435
if (touch_panel_ito_test(err))
436
if (!err[0] && !err[1])
437
return touch_execute_autotune();
438
}
439
440
// Initialize touchscreen.
441
u32 retries = 3;
442
while (retries)
443
{
444
if (touch_init())
445
return 1;
446
retries--;
447
}
448
449
return 0;
450
}
451
452
void touch_power_off()
453
{
454
// Disable touchscreen power.
455
gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
456
457
// Disables LDO6 for touchscreen VDD, AVDD supply
458
max7762x_regulator_enable(REGULATOR_LDO6, false);
459
460
clock_disable_i2c(I2C_3);
461
}
462