source: mainline/uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c@ 16dc887

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 16dc887 was 16dc887, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Merge Location service.

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