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

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

Categorize the remaining orphan doxygroups

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