2 * Copyright (c) 2010 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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).
41 #include <io/console.h>
43 #include <ipc/mouseev.h>
50 #include "s3c24xx_ts.h"
52 #define NAME "s3c24xx_ts"
53 #define NAMESPACE "hid"
55 static irq_cmd_t ts_irq_cmds
[] = {
61 static irq_code_t ts_irq_code
= {
64 sizeof(ts_irq_cmds
) / sizeof(irq_cmd_t
),
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
,
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
);
89 printf("%s: Unable to register driver.\n", NAME
);
93 ts
= malloc(sizeof(s3c24xx_ts_t
));
97 if (s3c24xx_ts_init(ts
) != EOK
)
100 rc
= loc_service_register(NAMESPACE
"/mouse", &ts
->service_id
);
102 printf(NAME
": Unable to register device %s.\n",
107 printf(NAME
": Registered device %s.\n", NAMESPACE
"/mouse");
109 printf(NAME
": Accepting connections\n");
117 /** Initialize S3C24xx touchscreen interface. */
118 static int s3c24xx_ts_init(s3c24xx_ts_t
*ts
)
123 inr
= S3C24XX_TS_INR
;
124 ts
->paddr
= S3C24XX_TS_ADDR
;
126 if (pio_enable((void *) ts
->paddr
, sizeof(s3c24xx_adc_io_t
),
131 ts
->client_sess
= NULL
;
132 ts
->state
= ts_wait_pendown
;
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
,
142 s3c24xx_ts_wait_for_int_mode(ts
, updn_down
);
147 /** Switch interface to wait for interrupt mode.
149 * In this mode we receive an interrupt when pen goes up/down, depending
152 * @param ts Touchscreen instance
153 * @param updn @c updn_up to wait for pen up, @c updn_down to wait for pen
156 static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t
*ts
, ts_updn_t updn
)
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. */
185 tsc
|= ADCTSC_DSUD_UP
;
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
,
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
);
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");
236 if (ts
->state
!= ts_sample_pos
) {
238 * We got an extra interrupt ater switching to
239 * wait for interrupt mode.
244 /* End-of-conversion interrupt */
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
)
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
)
276 /* Pen-up interrupt */
278 ts
->state
= ts_wait_pendown
;
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
)
302 ts
->state
= ts_wait_penup
;
304 /* Read in sampled data. */
306 data
= pio_read_32(&ts
->io
->dat0
);
309 data
= pio_read_32(&ts
->io
->dat1
);
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
,
318 /* Get differences. */
319 dx
= x_pos
- ts
->last_x
;
320 dy
= y_pos
- ts
->last_y
;
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
);
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
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
)
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
,
380 async_answer_0(iid
, EOK
);
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
);
397 async_callback_receive_start(EXCHANGE_SERIALIZE
, &call
);
399 if (ts
->client_sess
== NULL
) {
400 ts
->client_sess
= sess
;
401 async_answer_0(callid
, EOK
);
403 async_answer_0(callid
, ELIMIT
);
405 async_answer_0(callid
, EINVAL
);