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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b688fd8 was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

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