source: mainline/uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c

Last change on this file was ca48672, checked in by Jiri Svoboda <jiri@…>, 5 weeks ago

loc_service_register() needs to take a port ID argument.

  • Property mode set to 100644
File size: 10.1 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup uspace_srv_s3c24xx_ts
30 * @{
31 */
32/**
33 * @file
34 * @brief Samsung S3C24xx on-chip ADC and touch-screen interface driver.
35 *
36 * This interface is present on the Samsung S3C24xx CPU (on the gta02 platform).
37 */
38
39#include <ddi.h>
40#include <loc.h>
41#include <vfs/vfs.h>
42#include <ipc/mouseev.h>
43#include <async.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <sysinfo.h>
47#include <errno.h>
48#include <inttypes.h>
49#include "s3c24xx_ts.h"
50
51#define NAME "s3c24xx_ts"
52#define NAMESPACE "hid"
53
54static irq_cmd_t ts_irq_cmds[] = {
55 {
56 .cmd = CMD_ACCEPT
57 }
58};
59
60static irq_code_t ts_irq_code = {
61 0,
62 NULL,
63 sizeof(ts_irq_cmds) / sizeof(irq_cmd_t),
64 ts_irq_cmds
65};
66
67/** S3C24xx touchscreen instance structure */
68static s3c24xx_ts_t *ts;
69
70static void s3c24xx_ts_connection(ipc_call_t *, void *);
71static void s3c24xx_ts_irq_handler(ipc_call_t *, void *);
72static void s3c24xx_ts_pen_down(s3c24xx_ts_t *);
73static void s3c24xx_ts_pen_up(s3c24xx_ts_t *);
74static void s3c24xx_ts_eoc(s3c24xx_ts_t *);
75static int s3c24xx_ts_init(s3c24xx_ts_t *);
76static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *, ts_updn_t);
77static void s3c24xx_ts_convert_samples(int, int, int *, int *);
78static int lin_map_range(int, int, int, int, int);
79
80int main(int argc, char *argv[])
81{
82 loc_srv_t *srv;
83
84 printf("%s: S3C24xx touchscreen driver\n", NAME);
85
86 async_set_fallback_port_handler(s3c24xx_ts_connection, NULL);
87 errno_t rc = loc_server_register(NAME, &srv);
88 if (rc != EOK) {
89 printf("%s: Unable to register driver.\n", NAME);
90 return rc;
91 }
92
93 ts = malloc(sizeof(s3c24xx_ts_t));
94 if (ts == NULL) {
95 loc_server_unregister(srv);
96 return -1;
97 }
98
99 if (s3c24xx_ts_init(ts) != EOK) {
100 free(ts);
101 loc_server_unregister(srv);
102 return -1;
103 }
104
105 rc = loc_service_register(srv, NAMESPACE "/mouse", fallback_port_id,
106 &ts->service_id);
107 if (rc != EOK) {
108 // XXX s3c24xx_ts_fini();
109 free(ts);
110 loc_server_unregister(srv);
111 printf(NAME ": Unable to register device %s.\n",
112 NAMESPACE "/mouse");
113 return -1;
114 }
115
116 printf(NAME ": Registered device %s.\n", NAMESPACE "/mouse");
117
118 printf(NAME ": Accepting connections\n");
119 task_retval(0);
120 async_manager();
121
122 /* Not reached */
123 return 0;
124}
125
126/** Initialize S3C24xx touchscreen interface. */
127static int s3c24xx_ts_init(s3c24xx_ts_t *ts)
128{
129 void *vaddr;
130 sysarg_t inr;
131
132 inr = S3C24XX_TS_INR;
133 ts->paddr = S3C24XX_TS_ADDR;
134
135 if (pio_enable((void *) ts->paddr, sizeof(s3c24xx_adc_io_t),
136 &vaddr) != 0)
137 return -1;
138
139 ts->io = vaddr;
140 ts->client_sess = NULL;
141 ts->state = ts_wait_pendown;
142 ts->last_x = 0;
143 ts->last_y = 0;
144
145 printf(NAME ": device at physical address %p, inr %" PRIun ".\n",
146 (void *) ts->paddr, inr);
147
148 async_irq_subscribe(inr, s3c24xx_ts_irq_handler, NULL, &ts_irq_code, NULL);
149
150 s3c24xx_ts_wait_for_int_mode(ts, updn_down);
151
152 return EOK;
153}
154
155/** Switch interface to wait for interrupt mode.
156 *
157 * In this mode we receive an interrupt when pen goes up/down, depending
158 * on @a updn.
159 *
160 * @param ts Touchscreen instance
161 * @param updn @c updn_up to wait for pen up, @c updn_down to wait for pen
162 * down.
163 */
164static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *ts, ts_updn_t updn)
165{
166 uint32_t con, tsc;
167
168 /*
169 * Configure ADCCON register
170 */
171
172 con = pio_read_32(&ts->io->con);
173
174 /* Disable standby, disable start-by-read, clear manual start bit */
175 con = con & ~(ADCCON_STDBM | ADCCON_READ_START | ADCCON_ENABLE_START);
176
177 /* Set prescaler value 0xff, XP for input. */
178 con = con | (ADCCON_PRSCVL(0xff) << 6) | ADCCON_SEL_MUX(SMUX_XP);
179
180 /* Enable prescaler. */
181 con = con | ADCCON_PRSCEN;
182
183 pio_write_32(&ts->io->con, con);
184
185 /*
186 * Configure ADCTSC register
187 */
188
189 tsc = pio_read_32(&ts->io->tsc);
190
191 /* Select whether waiting for pen up or pen down. */
192 if (updn == updn_up)
193 tsc |= ADCTSC_DSUD_UP;
194 else
195 tsc &= ~ADCTSC_DSUD_UP;
196
197 /*
198 * Enable XP pull-up and disable all drivers except YM. This is
199 * according to the manual. This gives us L on XP input when touching
200 * and (pulled up to) H when not touching.
201 */
202 tsc = tsc & ~(ADCTSC_XM_ENABLE | ADCTSC_AUTO_PST |
203 ADCTSC_PULLUP_DISABLE);
204 tsc = tsc | ADCTSC_YP_DISABLE | ADCTSC_XP_DISABLE | ADCTSC_YM_ENABLE;
205
206 /* Select wait-for-interrupt mode. */
207 tsc = (tsc & ~ADCTSC_XY_PST_MASK) | ADCTSC_XY_PST_WAITINT;
208
209 pio_write_32(&ts->io->tsc, tsc);
210}
211
212/** Handle touchscreen interrupt */
213static void s3c24xx_ts_irq_handler(ipc_call_t *call, void *arg)
214{
215 ts_updn_t updn;
216
217 (void) call;
218
219 /* Read up/down interrupt flags. */
220 updn = pio_read_32(&ts->io->updn);
221
222 if (updn & (ADCUPDN_TSC_DN | ADCUPDN_TSC_UP)) {
223 /* Clear up/down interrupt flags. */
224 pio_write_32(&ts->io->updn, updn &
225 ~(ADCUPDN_TSC_DN | ADCUPDN_TSC_UP));
226 }
227
228 if (updn & ADCUPDN_TSC_DN) {
229 /* Pen-down interrupt */
230 s3c24xx_ts_pen_down(ts);
231 } else if (updn & ADCUPDN_TSC_UP) {
232 /* Pen-up interrupt */
233 s3c24xx_ts_pen_up(ts);
234 } else {
235 /* Presumably end-of-conversion interrupt */
236
237 /* Check end-of-conversion flag. */
238 if ((pio_read_32(&ts->io->con) & ADCCON_ECFLG) == 0) {
239 printf(NAME ": Unrecognized ts int.\n");
240 return;
241 }
242
243 if (ts->state != ts_sample_pos) {
244 /*
245 * We got an extra interrupt ater switching to
246 * wait for interrupt mode.
247 */
248 return;
249 }
250
251 /* End-of-conversion interrupt */
252 s3c24xx_ts_eoc(ts);
253 }
254}
255
256/** Handle pen-down interrupt.
257 *
258 * @param ts Touchscreen instance
259 */
260static void s3c24xx_ts_pen_down(s3c24xx_ts_t *ts)
261{
262 /* Pen-down interrupt */
263
264 ts->state = ts_sample_pos;
265
266 /* Enable auto xy-conversion mode */
267 pio_write_32(&ts->io->tsc, (pio_read_32(&ts->io->tsc) & ~3) | 4);
268
269 /* Start the conversion. */
270 pio_write_32(&ts->io->con, pio_read_32(&ts->io->con) | ADCCON_ENABLE_START);
271}
272
273/** Handle pen-up interrupt.
274 *
275 * @param ts Touchscreen instance
276 */
277static void s3c24xx_ts_pen_up(s3c24xx_ts_t *ts)
278{
279 int button, press;
280
281 /* Pen-up interrupt */
282
283 ts->state = ts_wait_pendown;
284
285 button = 1;
286 press = 0;
287
288 async_exch_t *exch = async_exchange_begin(ts->client_sess);
289 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, button, press);
290 async_exchange_end(exch);
291
292 s3c24xx_ts_wait_for_int_mode(ts, updn_down);
293}
294
295/** Handle end-of-conversion interrupt.
296 *
297 * @param ts Touchscreen instance
298 */
299static void s3c24xx_ts_eoc(s3c24xx_ts_t *ts)
300{
301 uint32_t data;
302 int button, press;
303 int smp0, smp1;
304 int x_pos, y_pos;
305 int dx, dy;
306
307 ts->state = ts_wait_penup;
308
309 /* Read in sampled data. */
310
311 data = pio_read_32(&ts->io->dat0);
312 smp0 = data & 0x3ff;
313
314 data = pio_read_32(&ts->io->dat1);
315 smp1 = data & 0x3ff;
316
317 /* Convert to screen coordinates. */
318 s3c24xx_ts_convert_samples(smp0, smp1, &x_pos, &y_pos);
319
320 printf("s0: 0x%03x, s1:0x%03x -> x:%d,y:%d\n", smp0, smp1,
321 x_pos, y_pos);
322
323 /* Get differences. */
324 dx = x_pos - ts->last_x;
325 dy = y_pos - ts->last_y;
326
327 button = 1;
328 press = 1;
329
330 /* Send notifications to client. */
331 async_exch_t *exch = async_exchange_begin(ts->client_sess);
332 async_msg_2(exch, MOUSEEV_MOVE_EVENT, dx, dy);
333 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, button, press);
334 async_exchange_end(exch);
335
336 ts->last_x = x_pos;
337 ts->last_y = y_pos;
338
339 s3c24xx_ts_wait_for_int_mode(ts, updn_up);
340}
341
342/** Convert sampled data to screen coordinates. */
343static void s3c24xx_ts_convert_samples(int smp0, int smp1, int *x, int *y)
344{
345 /*
346 * The orientation and display dimensions are GTA02-specific and the
347 * calibration values might even specific to the individual piece
348 * of hardware.
349 *
350 * The calibration values can be obtained by touching corners
351 * of the screen with the stylus and noting the sampled values.
352 */
353 *x = lin_map_range(smp1, 0xa1, 0x396, 0, 479);
354 *y = lin_map_range(smp0, 0x69, 0x38a, 639, 0);
355}
356
357/** Map integer from one range to another range in a linear fashion.
358 *
359 * i0 < i1 is required. i0 is mapped to o0, i1 to o1. If o1 < o0, then the
360 * mapping will be descending. If v is outside of [i0, i1], it is clamped.
361 *
362 * @param v Value to map.
363 * @param i0 Lower bound of input range.
364 * @param i1 Upper bound of input range.
365 * @param o0 First bound of output range.
366 * @param o1 Second bound of output range.
367 *
368 * @return Mapped value ov, o0 <= ov <= o1.
369 */
370static int lin_map_range(int v, int i0, int i1, int o0, int o1)
371{
372 if (v < i0)
373 v = i0;
374
375 if (v > i1)
376 v = i1;
377
378 return o0 + (o1 - o0) * (v - i0) / (i1 - i0);
379}
380
381/** Handle mouse client connection. */
382static void s3c24xx_ts_connection(ipc_call_t *icall, void *arg)
383{
384 async_accept_0(icall);
385
386 while (true) {
387 ipc_call_t call;
388 async_get_call(&call);
389
390 if (!ipc_get_imethod(&call)) {
391 if (ts->client_sess != NULL) {
392 async_hangup(ts->client_sess);
393 ts->client_sess = NULL;
394 }
395
396 async_answer_0(&call, EOK);
397 return;
398 }
399
400 async_sess_t *sess =
401 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
402 if (sess != NULL) {
403 if (ts->client_sess == NULL) {
404 ts->client_sess = sess;
405 async_answer_0(&call, EOK);
406 } else
407 async_answer_0(&call, ELIMIT);
408 } else
409 async_answer_0(&call, EINVAL);
410 }
411}
412
413/** @}
414 */
Note: See TracBrowser for help on using the repository browser.