source: mainline/uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c@ 43523b1

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

support for kernel notification multiplexing in the async framework

  • rename SYS_EVENT_* and SYS_IRQ_* syscalls to unify the terminology
  • add SYS_IPC_EVENT_UNSUBSCRIBE
  • remove IRQ handler multiplexing from DDF, the generic mechanism replaces it (unfortunatelly the order of arguments used by interrupt_handler_t needs to be permutated to align with the async framework conventions)
  • Property mode set to 100644
File size: 10.2 KB
RevLine 
[527298a]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>
[15f3c3f]40#include <loc.h>
[527298a]41#include <io/console.h>
42#include <vfs/vfs.h>
[022d9f67]43#include <ipc/mouseev.h>
[527298a]44#include <async.h>
45#include <unistd.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <sysinfo.h>
49#include <errno.h>
[7e752b2]50#include <inttypes.h>
[527298a]51#include "s3c24xx_ts.h"
52
[f7cbc6f]53#define NAME "s3c24xx_ts"
[5da7199]54#define NAMESPACE "hid"
[527298a]55
56static irq_cmd_t ts_irq_cmds[] = {
57 {
58 .cmd = CMD_ACCEPT
59 }
60};
61
62static irq_code_t ts_irq_code = {
[a996ae31]63 0,
64 NULL,
[527298a]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
[9934f7d]72static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall,
73 void *arg);
[8820544]74static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *);
[527298a]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{
[3123d2a]85 printf("%s: S3C24xx touchscreen driver\n", NAME);
[f302586]86
87 async_set_client_connection(s3c24xx_ts_connection);
[3123d2a]88 int rc = loc_server_register(NAME);
89 if (rc != EOK) {
90 printf("%s: Unable to register driver.\n", NAME);
91 return rc;
[527298a]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
[15f3c3f]101 rc = loc_service_register(NAMESPACE "/mouse", &ts->service_id);
[527298a]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;
[5da7199]132 ts->client_sess = NULL;
[527298a]133 ts->state = ts_wait_pendown;
134 ts->last_x = 0;
135 ts->last_y = 0;
136
[7e752b2]137 printf(NAME ": device at physical address %p, inr %" PRIun ".\n",
138 (void *) ts->paddr, inr);
[527298a]139
[8820544]140 async_irq_subscribe(inr, device_assign_devno(), s3c24xx_ts_irq_handler,
141 NULL, &ts_irq_code);
[527298a]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 */
[8820544]206static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call,
207 void *arg)
[527298a]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;
[5da7199]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);
[527298a]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. */
[5da7199]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);
[527298a]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. */
[9934f7d]378static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall,
379 void *arg)
[527298a]380{
[ffa2c8ef]381 async_answer_0(iid, EOK);
[5da7199]382
383 while (true) {
384 ipc_call_t call;
385 ipc_callid_t callid = async_get_call(&call);
[79ae36dd]386
387 if (!IPC_GET_IMETHOD(call)) {
[5da7199]388 if (ts->client_sess != NULL) {
389 async_hangup(ts->client_sess);
390 ts->client_sess = NULL;
[527298a]391 }
[5da7199]392
[ffa2c8ef]393 async_answer_0(callid, EOK);
[527298a]394 return;
[79ae36dd]395 }
396
[5da7199]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);
[527298a]407 }
408}
409
410/** @}
411 */
Note: See TracBrowser for help on using the repository browser.