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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a5c82bb9 was a996ae31, checked in by Jakub Jermar <jakub@…>, 13 years ago

Userspace IRQ pseudocode is expected to use physical addresses from now.
The kernel creates kernel mappings for those and adjusts the pseudocode
accordingly.

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