1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2010-01-01 Yi.Qiu first version
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <s3c24x0.h>
14
15 #ifdef PKG_USING_GUIENGINE
16 #include <rtgui/rtgui_system.h>
17 #include <rtgui/rtgui_server.h>
18 #include <rtgui/event.h>
19 #endif
20
21 #define TOUCH_SWAP_XY
22
23 #include "touch.h"
24
25 /* ADCCON Register Bits */
26 #define S3C2410_ADCCON_ECFLG (1<<15)
27 #define S3C2410_ADCCON_PRSCEN (1<<14)
28 #define S3C2410_ADCCON_PRSCVL(x) (((x)&0xFF)<<6)
29 #define S3C2410_ADCCON_PRSCVLMASK (0xFF<<6)
30 #define S3C2410_ADCCON_SELMUX(x) (((x)&0x7)<<3)
31 #define S3C2410_ADCCON_MUXMASK (0x7<<3)
32 #define S3C2410_ADCCON_STDBM (1<<2)
33 #define S3C2410_ADCCON_READ_START (1<<1)
34 #define S3C2410_ADCCON_ENABLE_START (1<<0)
35 #define S3C2410_ADCCON_STARTMASK (0x3<<0)
36
37 /* ADCTSC Register Bits */
38 #define S3C2410_ADCTSC_UD_SEN (1<<8) /* ghcstop add for s3c2440a */
39 #define S3C2410_ADCTSC_YM_SEN (1<<7)
40 #define S3C2410_ADCTSC_YP_SEN (1<<6)
41 #define S3C2410_ADCTSC_XM_SEN (1<<5)
42 #define S3C2410_ADCTSC_XP_SEN (1<<4)
43 #define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
44 #define S3C2410_ADCTSC_AUTO_PST (1<<2)
45 #define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
46
47 /* ADCDAT0 Bits */
48 #define S3C2410_ADCDAT0_UPDOWN (1<<15)
49 #define S3C2410_ADCDAT0_AUTO_PST (1<<14)
50 #define S3C2410_ADCDAT0_XY_PST (0x3<<12)
51 #define S3C2410_ADCDAT0_XPDATA_MASK (0x03FF)
52
53 /* ADCDAT1 Bits */
54 #define S3C2410_ADCDAT1_UPDOWN (1<<15)
55 #define S3C2410_ADCDAT1_AUTO_PST (1<<14)
56 #define S3C2410_ADCDAT1_XY_PST (0x3<<12)
57 #define S3C2410_ADCDAT1_YPDATA_MASK (0x03FF)
58
59 #define WAIT4INT(x) (((x)<<8) | \
60 S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
61 S3C2410_ADCTSC_XY_PST(3))
62
63 #define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
64 S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
65
66 #define X_MIN 74
67 #define X_MAX 934
68 #define Y_MIN 920
69 #define Y_MAX 89
70
71 struct s3c2410ts
72 {
73 long xp;
74 long yp;
75 int count;
76 int shift;
77
78 int delay;
79 int presc;
80
81 char phys[32];
82 };
83 static struct s3c2410ts ts;
84
85 struct rtgui_touch_device
86 {
87 struct rt_device parent;
88
89 rt_timer_t poll_timer;
90 rt_uint16_t x, y;
91
92 rt_bool_t calibrating;
93 rt_touch_calibration_func_t calibration_func;
94
95 rt_touch_eventpost_func_t eventpost_func;
96 void *eventpost_param;
97
98 rt_uint16_t min_x, max_x;
99 rt_uint16_t min_y, max_y;
100
101 rt_uint16_t width;
102 rt_uint16_t height;
103
104 rt_bool_t first_down_report;
105 };
106 static struct rtgui_touch_device *touch = RT_NULL;
107
108 #ifdef PKG_USING_GUIENGINE
report_touch_input(int updown)109 static void report_touch_input(int updown)
110 {
111 struct rtgui_event_mouse emouse;
112
113 RTGUI_EVENT_MOUSE_BUTTON_INIT(&emouse);
114 emouse.wid = RT_NULL;
115
116 /* set emouse button */
117 emouse.button = RTGUI_MOUSE_BUTTON_LEFT;
118 emouse.parent.sender = RT_NULL;
119
120 if (updown)
121 {
122 ts.xp = ts.xp / ts.count;
123 ts.yp = ts.yp / ts.count;;
124
125 #ifdef TOUCH_SWAP_XY
126 ts.xp = ts.xp + ts.yp;
127 ts.yp = ts.xp - ts.yp;
128 ts.xp = ts.xp - ts.yp;
129 #endif
130
131 if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))
132 {
133 touch->x = ts.xp;
134 touch->y = ts.yp;
135 }
136 else
137 {
138 if (touch->max_x > touch->min_x)
139 {
140 touch->x = touch->width * (ts.xp-touch->min_x)/(touch->max_x-touch->min_x);
141 }
142 else
143 {
144 touch->x = touch->width * ( touch->min_x - ts.xp ) / (touch->min_x-touch->max_x);
145 }
146
147 if (touch->max_y > touch->min_y)
148 {
149 touch->y = touch->height * ( ts.yp - touch->min_y ) / (touch->max_y-touch->min_y);
150 }
151 else
152 {
153 touch->y = touch->height * ( touch->min_y - ts.yp ) / (touch->min_y-touch->max_y);
154 }
155 }
156
157 emouse.x = touch->x;
158 emouse.y = touch->y;
159 if (touch->first_down_report == RT_TRUE)
160 {
161 emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;
162 emouse.button |= RTGUI_MOUSE_BUTTON_DOWN;
163 }
164 else
165 {
166 emouse.parent.type = RTGUI_EVENT_MOUSE_MOTION;
167 emouse.button = 0;
168 }
169 }
170 else
171 {
172 emouse.x = touch->x;
173 emouse.y = touch->y;
174 emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;
175 emouse.button |= RTGUI_MOUSE_BUTTON_UP;
176 if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))
177 {
178 /* callback function */
179 touch->calibration_func(emouse.x, emouse.y);
180 }
181 }
182
183 /* rt_kprintf("touch %s: ts.x: %d, ts.y: %d\n", updown? "down" : "up",
184 touch->x, touch->y); */
185
186 /* send event to server */
187 if (touch->calibrating != RT_TRUE)
188 {
189 rtgui_server_post_event((&emouse.parent), sizeof(emouse));
190 }
191 }
192 #else
report_touch_input(int updown)193 static void report_touch_input(int updown)
194 {
195 struct rt_touch_event touch_event;
196
197 if (updown)
198 {
199 ts.xp = ts.xp / ts.count;
200 ts.yp = ts.yp / ts.count;
201
202 if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))
203 {
204 touch->x = ts.xp;
205 touch->y = ts.yp;
206 }
207 else
208 {
209 if (touch->max_x > touch->min_x)
210 {
211 touch->x = touch->width * ( ts.xp - touch->min_x ) / (touch->max_x-touch->min_x);
212 }
213 else
214 {
215 touch->x = touch->width * ( touch->min_x - ts.xp ) / (touch->min_x-touch->max_x);
216 }
217
218 if (touch->max_y > touch->min_y)
219 {
220 touch->y = touch->height * ( ts.yp - touch->min_y ) / (touch->max_y-touch->min_y);
221 }
222 else
223 {
224 touch->y = touch->height * ( touch->min_y - ts.yp ) / (touch->min_y-touch->max_y);
225 }
226 }
227
228 touch_event.x = touch->x;
229 touch_event.y = touch->y;
230 touch_event.pressed = 1;
231
232 if (touch->first_down_report == RT_TRUE)
233 {
234 if (touch->calibrating != RT_TRUE && touch->eventpost_func)
235 {
236 touch->eventpost_func(touch->eventpost_param, &touch_event);
237 }
238 }
239 }
240 else
241 {
242 touch_event.x = touch->x;
243 touch_event.y = touch->y;
244 touch_event.pressed = 0;
245
246 if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))
247 {
248 /* callback function */
249 touch->calibration_func(touch_event.x, touch_event.y);
250 }
251
252 if (touch->calibrating != RT_TRUE && touch->eventpost_func)
253 {
254 touch->eventpost_func(touch->eventpost_param, &touch_event);
255 }
256 }
257 }
258 #endif
259
touch_timer_fire(void * parameter)260 static void touch_timer_fire(void *parameter)
261 {
262 rt_uint32_t data0;
263 rt_uint32_t data1;
264 int updown;
265
266 data0 = ADCDAT0;
267 data1 = ADCDAT1;
268
269 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
270
271 if (updown)
272 {
273 if (ts.count != 0)
274 {
275 report_touch_input(updown);
276 }
277
278 ts.xp = 0;
279 ts.yp = 0;
280 ts.count = 0;
281
282 ADCTSC = S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST;
283 ADCCON |= S3C2410_ADCCON_ENABLE_START;
284 }
285 }
286
s3c2410_adc_stylus_action(void)287 static void s3c2410_adc_stylus_action(void)
288 {
289 rt_uint32_t data0;
290 rt_uint32_t data1;
291
292 data0 = ADCDAT0;
293 data1 = ADCDAT1;
294
295 ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
296 ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
297 ts.count ++;
298
299 if (ts.count < (1<<ts.shift))
300 {
301 ADCTSC = S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST;
302 ADCCON |= S3C2410_ADCCON_ENABLE_START;
303 }
304 else
305 {
306 if (touch->first_down_report)
307 {
308 report_touch_input(1);
309 ts.xp = 0;
310 ts.yp = 0;
311 ts.count = 0;
312 touch->first_down_report = 0;
313 }
314 /* start timer */
315 rt_timer_start(touch->poll_timer);
316 ADCTSC = WAIT4INT(1);
317 }
318
319 SUBSRCPND |= BIT_SUB_ADC;
320 }
321
s3c2410_intc_stylus_updown(void)322 static void s3c2410_intc_stylus_updown(void)
323 {
324 rt_uint32_t data0;
325 rt_uint32_t data1;
326 int updown;
327
328 data0 = ADCDAT0;
329 data1 = ADCDAT1;
330
331 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
332
333 /* rt_kprintf("stylus: %s\n", updown? "down" : "up"); */
334
335 if (updown)
336 {
337 touch_timer_fire(0);
338 }
339 else
340 {
341 /* stop timer */
342 rt_timer_stop(touch->poll_timer);
343 touch->first_down_report = RT_TRUE;
344 if (ts.xp >= 0 && ts.yp >= 0)
345 {
346 report_touch_input(updown);
347 }
348 ts.count = 0;
349 ADCTSC = WAIT4INT(0);
350 }
351
352 SUBSRCPND |= BIT_SUB_TC;
353 }
354
rt_touch_handler(int irqno,void * param)355 static void rt_touch_handler(int irqno, void *param)
356 {
357 if (SUBSRCPND & BIT_SUB_ADC)
358 {
359 /* INT_SUB_ADC */
360 s3c2410_adc_stylus_action();
361 }
362
363 if (SUBSRCPND & BIT_SUB_TC)
364 {
365 /* INT_SUB_TC */
366 s3c2410_intc_stylus_updown();
367 }
368
369 /* clear interrupt */
370 INTPND |= (1ul << INTADC);
371 }
372
373 /* RT-Thread Device Interface */
rtgui_touch_init(rt_device_t dev)374 static rt_err_t rtgui_touch_init(rt_device_t dev)
375 {
376 /* init touch screen structure */
377 rt_memset(&ts, 0, sizeof(struct s3c2410ts));
378
379 ts.delay = 50000;
380 ts.presc = 9;
381 ts.shift = 2;
382 ts.count = 0;
383 ts.xp = ts.yp = 0;
384
385 ADCCON = S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(ts.presc);
386 ADCDLY = ts.delay;
387
388 ADCTSC = WAIT4INT(0);
389
390 rt_hw_interrupt_install(INTADC, rt_touch_handler, RT_NULL , "INTADC");
391 rt_hw_interrupt_umask(INTADC);
392
393 /* clear interrupt */
394 INTPND |= (1ul << INTADC);
395
396 SUBSRCPND |= BIT_SUB_TC;
397 SUBSRCPND |= BIT_SUB_ADC;
398
399 /* install interrupt handler */
400 INTSUBMSK &= ~BIT_SUB_ADC;
401 INTSUBMSK &= ~BIT_SUB_TC;
402
403 touch->first_down_report = RT_TRUE;
404
405 return RT_EOK;
406 }
407
rtgui_touch_control(rt_device_t dev,int cmd,void * args)408 static rt_err_t rtgui_touch_control(rt_device_t dev, int cmd, void *args)
409 {
410 switch (cmd)
411 {
412 case RT_TOUCH_CALIBRATION:
413 touch->calibrating = RT_TRUE;
414 touch->calibration_func = (rt_touch_calibration_func_t)args;
415 break;
416
417 case RT_TOUCH_NORMAL:
418 touch->calibrating = RT_FALSE;
419 break;
420
421 case RT_TOUCH_CALIBRATION_DATA:
422 {
423 struct calibration_data *data;
424
425 data = (struct calibration_data *)args;
426
427 /* update */
428 touch->min_x = data->min_x;
429 touch->max_x = data->max_x;
430 touch->min_y = data->min_y;
431 touch->max_y = data->max_y;
432
433 /*
434 rt_kprintf("min_x = %d, max_x = %d, min_y = %d, max_y = %d\n",
435 touch->min_x, touch->max_x, touch->min_y, touch->max_y);
436 */
437 }
438 break;
439
440 case RT_TOUCH_EVENTPOST:
441 touch->eventpost_func = (rt_touch_eventpost_func_t)args;
442 break;
443
444 case RT_TOUCH_EVENTPOST_PARAM:
445 touch->eventpost_param = args;
446 break;
447 }
448
449 return RT_EOK;
450 }
451
rtgui_touch_hw_init(void)452 int rtgui_touch_hw_init(void)
453 {
454 rt_err_t result = RT_FALSE;
455 rt_device_t device = RT_NULL;
456 struct rt_device_graphic_info info;
457
458 touch = (struct rtgui_touch_device *)rt_malloc(sizeof(struct rtgui_touch_device));
459 if (touch == RT_NULL)
460 return -RT_ERROR; /* no memory yet */
461
462 /* clear device structure */
463 rt_memset(&(touch->parent), 0, sizeof(struct rt_device));
464 touch->calibrating = RT_FALSE;
465 touch->min_x = X_MIN;
466 touch->max_x = X_MAX;
467 touch->min_y = Y_MIN;
468 touch->max_y = Y_MAX;
469 touch->eventpost_func = RT_NULL;
470 touch->eventpost_param = RT_NULL;
471
472 /* init device structure */
473 touch->parent.type = RT_Device_Class_Unknown;
474 touch->parent.init = rtgui_touch_init;
475 touch->parent.control = rtgui_touch_control;
476 touch->parent.user_data = RT_NULL;
477
478 device = rt_device_find("lcd");
479 if (device == RT_NULL)
480 {
481 rt_kprintf("No lcd found\n");
482 return -RT_ERROR; /* no this device */
483 }
484
485 /* get graphic device info */
486 result = rt_device_control(device, RTGRAPHIC_CTRL_GET_INFO, &info);
487 if (result != RT_EOK)
488 {
489 /* get device information failed */
490 rt_kprintf("Get graphic device info failed\n");
491 return -RT_ERROR;
492 }
493
494 touch->width = info.width;
495 touch->height = info.height;
496
497 /* create 1/8 second timer */
498 touch->poll_timer = rt_timer_create("touch", touch_timer_fire, RT_NULL,
499 RT_TICK_PER_SECOND/8, RT_TIMER_FLAG_PERIODIC);
500
501 /* register touch device to RT-Thread */
502 rt_device_register(&(touch->parent), "touch", RT_DEVICE_FLAG_RDWR);
503
504 return RT_EOK;
505 }
506
507 INIT_PREV_EXPORT(rtgui_touch_hw_init);
508