Remove unistd.h
[helenos.git] / uspace / srv / hid / s3c24xx_ts / s3c24xx_ts.c
blob340fd4bf29baefae8102264994bed0fbe0958e45
1 /*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup mouse
30 * @{
32 /**
33 * @file
34 * @brief Samsung Samsung S3C24xx on-chip ADC and touch-screen interface driver.
36 * This interface is present on the Samsung S3C24xx CPU (on the gta02 platform).
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 <stdio.h>
46 #include <stdlib.h>
47 #include <sysinfo.h>
48 #include <errno.h>
49 #include <inttypes.h>
50 #include "s3c24xx_ts.h"
52 #define NAME "s3c24xx_ts"
53 #define NAMESPACE "hid"
55 static irq_cmd_t ts_irq_cmds[] = {
57 .cmd = CMD_ACCEPT
61 static irq_code_t ts_irq_code = {
63 NULL,
64 sizeof(ts_irq_cmds) / sizeof(irq_cmd_t),
65 ts_irq_cmds
68 /** S3C24xx touchscreen instance structure */
69 static s3c24xx_ts_t *ts;
71 static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall,
72 void *arg);
73 static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *);
74 static void s3c24xx_ts_pen_down(s3c24xx_ts_t *ts);
75 static void s3c24xx_ts_pen_up(s3c24xx_ts_t *ts);
76 static void s3c24xx_ts_eoc(s3c24xx_ts_t *ts);
77 static int s3c24xx_ts_init(s3c24xx_ts_t *ts);
78 static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *ts, ts_updn_t updn);
79 static void s3c24xx_ts_convert_samples(int smp0, int smp1, int *x, int *y);
80 static int lin_map_range(int v, int i0, int i1, int o0, int o1);
82 int main(int argc, char *argv[])
84 printf("%s: S3C24xx touchscreen driver\n", NAME);
86 async_set_fallback_port_handler(s3c24xx_ts_connection, NULL);
87 int rc = loc_server_register(NAME);
88 if (rc != EOK) {
89 printf("%s: Unable to register driver.\n", NAME);
90 return rc;
93 ts = malloc(sizeof(s3c24xx_ts_t));
94 if (ts == NULL)
95 return -1;
97 if (s3c24xx_ts_init(ts) != EOK)
98 return -1;
100 rc = loc_service_register(NAMESPACE "/mouse", &ts->service_id);
101 if (rc != EOK) {
102 printf(NAME ": Unable to register device %s.\n",
103 NAMESPACE "/mouse");
104 return -1;
107 printf(NAME ": Registered device %s.\n", NAMESPACE "/mouse");
109 printf(NAME ": Accepting connections\n");
110 task_retval(0);
111 async_manager();
113 /* Not reached */
114 return 0;
117 /** Initialize S3C24xx touchscreen interface. */
118 static int s3c24xx_ts_init(s3c24xx_ts_t *ts)
120 void *vaddr;
121 sysarg_t inr;
123 inr = S3C24XX_TS_INR;
124 ts->paddr = S3C24XX_TS_ADDR;
126 if (pio_enable((void *) ts->paddr, sizeof(s3c24xx_adc_io_t),
127 &vaddr) != 0)
128 return -1;
130 ts->io = vaddr;
131 ts->client_sess = NULL;
132 ts->state = ts_wait_pendown;
133 ts->last_x = 0;
134 ts->last_y = 0;
136 printf(NAME ": device at physical address %p, inr %" PRIun ".\n",
137 (void *) ts->paddr, inr);
139 async_irq_subscribe(inr, device_assign_devno(), s3c24xx_ts_irq_handler,
140 NULL, &ts_irq_code);
142 s3c24xx_ts_wait_for_int_mode(ts, updn_down);
144 return EOK;
147 /** Switch interface to wait for interrupt mode.
149 * In this mode we receive an interrupt when pen goes up/down, depending
150 * on @a updn.
152 * @param ts Touchscreen instance
153 * @param updn @c updn_up to wait for pen up, @c updn_down to wait for pen
154 * down.
156 static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *ts, ts_updn_t updn)
158 uint32_t con, tsc;
161 * Configure ADCCON register
164 con = pio_read_32(&ts->io->con);
166 /* Disable standby, disable start-by-read, clear manual start bit */
167 con = con & ~(ADCCON_STDBM | ADCCON_READ_START | ADCCON_ENABLE_START);
169 /* Set prescaler value 0xff, XP for input. */
170 con = con | (ADCCON_PRSCVL(0xff) << 6) | ADCCON_SEL_MUX(SMUX_XP);
172 /* Enable prescaler. */
173 con = con | ADCCON_PRSCEN;
175 pio_write_32(&ts->io->con, con);
178 * Configure ADCTSC register
181 tsc = pio_read_32(&ts->io->tsc);
183 /* Select whether waiting for pen up or pen down. */
184 if (updn == updn_up)
185 tsc |= ADCTSC_DSUD_UP;
186 else
187 tsc &= ~ADCTSC_DSUD_UP;
190 * Enable XP pull-up and disable all drivers except YM. This is
191 * according to the manual. This gives us L on XP input when touching
192 * and (pulled up to) H when not touching.
194 tsc = tsc & ~(ADCTSC_XM_ENABLE | ADCTSC_AUTO_PST |
195 ADCTSC_PULLUP_DISABLE);
196 tsc = tsc | ADCTSC_YP_DISABLE | ADCTSC_XP_DISABLE | ADCTSC_YM_ENABLE;
198 /* Select wait-for-interrupt mode. */
199 tsc = (tsc & ~ADCTSC_XY_PST_MASK) | ADCTSC_XY_PST_WAITINT;
201 pio_write_32(&ts->io->tsc, tsc);
204 /** Handle touchscreen interrupt */
205 static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call,
206 void *arg)
208 ts_updn_t updn;
210 (void) iid; (void) call;
212 /* Read up/down interrupt flags. */
213 updn = pio_read_32(&ts->io->updn);
215 if (updn & (ADCUPDN_TSC_DN | ADCUPDN_TSC_UP)) {
216 /* Clear up/down interrupt flags. */
217 pio_write_32(&ts->io->updn, updn &
218 ~(ADCUPDN_TSC_DN | ADCUPDN_TSC_UP));
221 if (updn & ADCUPDN_TSC_DN) {
222 /* Pen-down interrupt */
223 s3c24xx_ts_pen_down(ts);
224 } else if (updn & ADCUPDN_TSC_UP) {
225 /* Pen-up interrupt */
226 s3c24xx_ts_pen_up(ts);
227 } else {
228 /* Presumably end-of-conversion interrupt */
230 /* Check end-of-conversion flag. */
231 if ((pio_read_32(&ts->io->con) & ADCCON_ECFLG) == 0) {
232 printf(NAME ": Unrecognized ts int.\n");
233 return;
236 if (ts->state != ts_sample_pos) {
238 * We got an extra interrupt ater switching to
239 * wait for interrupt mode.
241 return;
244 /* End-of-conversion interrupt */
245 s3c24xx_ts_eoc(ts);
249 /** Handle pen-down interrupt.
251 * @param ts Touchscreen instance
253 static void s3c24xx_ts_pen_down(s3c24xx_ts_t *ts)
255 /* Pen-down interrupt */
257 ts->state = ts_sample_pos;
259 /* Enable auto xy-conversion mode */
260 pio_write_32(&ts->io->tsc, (pio_read_32(&ts->io->tsc)
261 & ~3) | 4);
263 /* Start the conversion. */
264 pio_write_32(&ts->io->con, pio_read_32(&ts->io->con)
265 | ADCCON_ENABLE_START);
268 /** Handle pen-up interrupt.
270 * @param ts Touchscreen instance
272 static void s3c24xx_ts_pen_up(s3c24xx_ts_t *ts)
274 int button, press;
276 /* Pen-up interrupt */
278 ts->state = ts_wait_pendown;
280 button = 1;
281 press = 0;
283 async_exch_t *exch = async_exchange_begin(ts->client_sess);
284 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, button, press);
285 async_exchange_end(exch);
287 s3c24xx_ts_wait_for_int_mode(ts, updn_down);
290 /** Handle end-of-conversion interrupt.
292 * @param ts Touchscreen instance
294 static void s3c24xx_ts_eoc(s3c24xx_ts_t *ts)
296 uint32_t data;
297 int button, press;
298 int smp0, smp1;
299 int x_pos, y_pos;
300 int dx, dy;
302 ts->state = ts_wait_penup;
304 /* Read in sampled data. */
306 data = pio_read_32(&ts->io->dat0);
307 smp0 = data & 0x3ff;
309 data = pio_read_32(&ts->io->dat1);
310 smp1 = data & 0x3ff;
312 /* Convert to screen coordinates. */
313 s3c24xx_ts_convert_samples(smp0, smp1, &x_pos, &y_pos);
315 printf("s0: 0x%03x, s1:0x%03x -> x:%d,y:%d\n", smp0, smp1,
316 x_pos, y_pos);
318 /* Get differences. */
319 dx = x_pos - ts->last_x;
320 dy = y_pos - ts->last_y;
322 button = 1;
323 press = 1;
325 /* Send notifications to client. */
326 async_exch_t *exch = async_exchange_begin(ts->client_sess);
327 async_msg_2(exch, MOUSEEV_MOVE_EVENT, dx, dy);
328 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, button, press);
329 async_exchange_end(exch);
331 ts->last_x = x_pos;
332 ts->last_y = y_pos;
334 s3c24xx_ts_wait_for_int_mode(ts, updn_up);
337 /** Convert sampled data to screen coordinates. */
338 static void s3c24xx_ts_convert_samples(int smp0, int smp1, int *x, int *y)
341 * The orientation and display dimensions are GTA02-specific and the
342 * calibration values might even specific to the individual piece
343 * of hardware.
345 * The calibration values can be obtained by touching corners
346 * of the screen with the stylus and noting the sampled values.
348 *x = lin_map_range(smp1, 0xa1, 0x396, 0, 479);
349 *y = lin_map_range(smp0, 0x69, 0x38a, 639, 0);
352 /** Map integer from one range to another range in a linear fashion.
354 * i0 < i1 is required. i0 is mapped to o0, i1 to o1. If o1 < o0, then the
355 * mapping will be descending. If v is outside of [i0, i1], it is clamped.
357 * @param v Value to map.
358 * @param i0 Lower bound of input range.
359 * @param i1 Upper bound of input range.
360 * @param o0 First bound of output range.
361 * @param o1 Second bound of output range.
363 * @return Mapped value ov, o0 <= ov <= o1.
365 static int lin_map_range(int v, int i0, int i1, int o0, int o1)
367 if (v < i0)
368 v = i0;
370 if (v > i1)
371 v = i1;
373 return o0 + (o1 - o0) * (v - i0) / (i1 - i0);
376 /** Handle mouse client connection. */
377 static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall,
378 void *arg)
380 async_answer_0(iid, EOK);
382 while (true) {
383 ipc_call_t call;
384 ipc_callid_t callid = async_get_call(&call);
386 if (!IPC_GET_IMETHOD(call)) {
387 if (ts->client_sess != NULL) {
388 async_hangup(ts->client_sess);
389 ts->client_sess = NULL;
392 async_answer_0(callid, EOK);
393 return;
396 async_sess_t *sess =
397 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
398 if (sess != NULL) {
399 if (ts->client_sess == NULL) {
400 ts->client_sess = sess;
401 async_answer_0(callid, EOK);
402 } else
403 async_answer_0(callid, ELIMIT);
404 } else
405 async_answer_0(callid, EINVAL);
409 /** @}