s3c24xx_ts.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00039 #include <ddi.h>
00040 #include <libarch/ddi.h>
00041 #include <devmap.h>
00042 #include <io/console.h>
00043 #include <vfs/vfs.h>
00044 #include <ipc/mouse.h>
00045 #include <async.h>
00046 #include <unistd.h>
00047 #include <stdio.h>
00048 #include <stdlib.h>
00049 #include <sysinfo.h>
00050 #include <errno.h>
00051 #include <inttypes.h>
00052 
00053 #include "s3c24xx_ts.h"
00054 
00055 #define NAME "s3c24ser"
00056 #define NAMESPACE "hid_in"
00057 
00058 static irq_cmd_t ts_irq_cmds[] = {
00059         {
00060                 .cmd = CMD_ACCEPT
00061         }
00062 };
00063 
00064 static irq_code_t ts_irq_code = {
00065         sizeof(ts_irq_cmds) / sizeof(irq_cmd_t),
00066         ts_irq_cmds
00067 };
00068 
00070 static s3c24xx_ts_t *ts;
00071 
00072 static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall);
00073 static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call);
00074 static void s3c24xx_ts_pen_down(s3c24xx_ts_t *ts);
00075 static void s3c24xx_ts_pen_up(s3c24xx_ts_t *ts);
00076 static void s3c24xx_ts_eoc(s3c24xx_ts_t *ts);
00077 static int s3c24xx_ts_init(s3c24xx_ts_t *ts);
00078 static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *ts, ts_updn_t updn);
00079 static void s3c24xx_ts_convert_samples(int smp0, int smp1, int *x, int *y);
00080 static int lin_map_range(int v, int i0, int i1, int o0, int o1);
00081 
00082 int main(int argc, char *argv[])
00083 {
00084         int rc;
00085 
00086         printf(NAME ": S3C24xx touchscreen driver\n");
00087 
00088         rc = devmap_driver_register(NAME, s3c24xx_ts_connection);
00089         if (rc < 0) {
00090                 printf(NAME ": Unable to register driver.\n");
00091                 return -1;
00092         }
00093 
00094         ts = malloc(sizeof(s3c24xx_ts_t));
00095         if (ts == NULL)
00096                 return -1;
00097 
00098         if (s3c24xx_ts_init(ts) != EOK)
00099                 return -1;
00100 
00101         rc = devmap_device_register(NAMESPACE "/mouse", &ts->devmap_handle);
00102         if (rc != EOK) {
00103                 printf(NAME ": Unable to register device %s.\n",
00104                     NAMESPACE "/mouse");
00105                 return -1;
00106         }
00107 
00108         printf(NAME ": Registered device %s.\n", NAMESPACE "/mouse");
00109 
00110         printf(NAME ": Accepting connections\n");
00111         task_retval(0);
00112         async_manager();
00113 
00114         /* Not reached */
00115         return 0;
00116 }
00117 
00119 static int s3c24xx_ts_init(s3c24xx_ts_t *ts)
00120 {
00121         void *vaddr;
00122         sysarg_t inr;
00123 
00124         inr = S3C24XX_TS_INR;
00125         ts->paddr = S3C24XX_TS_ADDR;
00126 
00127         if (pio_enable((void *) ts->paddr, sizeof(s3c24xx_adc_io_t),
00128             &vaddr) != 0)
00129                 return -1;
00130 
00131         ts->io = vaddr;
00132         ts->client_phone = -1;
00133         ts->state = ts_wait_pendown;
00134         ts->last_x = 0;
00135         ts->last_y = 0;
00136 
00137         printf(NAME ": device at physical address %p, inr %" PRIun ".\n",
00138             (void *) ts->paddr, inr);
00139 
00140         async_set_interrupt_received(s3c24xx_ts_irq_handler);
00141         register_irq(inr, device_assign_devno(), 0, &ts_irq_code);
00142 
00143         s3c24xx_ts_wait_for_int_mode(ts, updn_down);
00144 
00145         return EOK;
00146 }
00147 
00157 static void s3c24xx_ts_wait_for_int_mode(s3c24xx_ts_t *ts, ts_updn_t updn)
00158 {
00159         uint32_t con, tsc;
00160 
00161         /*
00162          * Configure ADCCON register
00163          */
00164 
00165         con = pio_read_32(&ts->io->con);
00166 
00167         /* Disable standby, disable start-by-read, clear manual start bit */
00168         con = con & ~(ADCCON_STDBM | ADCCON_READ_START | ADCCON_ENABLE_START);
00169 
00170         /* Set prescaler value 0xff, XP for input. */
00171         con = con | (ADCCON_PRSCVL(0xff) << 6) | ADCCON_SEL_MUX(SMUX_XP);
00172 
00173         /* Enable prescaler. */
00174         con = con | ADCCON_PRSCEN;
00175 
00176         pio_write_32(&ts->io->con, con);
00177 
00178         /*
00179          * Configure ADCTSC register
00180          */
00181 
00182         tsc = pio_read_32(&ts->io->tsc);
00183 
00184         /* Select whether waiting for pen up or pen down. */
00185         if (updn == updn_up)
00186                 tsc |= ADCTSC_DSUD_UP;
00187         else
00188                 tsc &= ~ADCTSC_DSUD_UP;
00189 
00190         /*
00191          * Enable XP pull-up and disable all drivers except YM. This is
00192          * according to the manual. This gives us L on XP input when touching
00193          * and (pulled up to) H when not touching.
00194          */
00195         tsc = tsc & ~(ADCTSC_XM_ENABLE | ADCTSC_AUTO_PST |
00196             ADCTSC_PULLUP_DISABLE);
00197         tsc = tsc | ADCTSC_YP_DISABLE | ADCTSC_XP_DISABLE | ADCTSC_YM_ENABLE;
00198 
00199         /* Select wait-for-interrupt mode. */
00200         tsc = (tsc & ~ADCTSC_XY_PST_MASK) | ADCTSC_XY_PST_WAITINT;
00201 
00202         pio_write_32(&ts->io->tsc, tsc);
00203 }
00204 
00206 static void s3c24xx_ts_irq_handler(ipc_callid_t iid, ipc_call_t *call)
00207 {
00208         ts_updn_t updn;
00209 
00210         (void) iid; (void) call;
00211 
00212         /* Read up/down interrupt flags. */
00213         updn = pio_read_32(&ts->io->updn);
00214 
00215         if (updn & (ADCUPDN_TSC_DN | ADCUPDN_TSC_UP)) {
00216                 /* Clear up/down interrupt flags. */
00217                 pio_write_32(&ts->io->updn, updn &
00218                     ~(ADCUPDN_TSC_DN | ADCUPDN_TSC_UP));
00219         }
00220 
00221         if (updn & ADCUPDN_TSC_DN) {
00222                 /* Pen-down interrupt */
00223                 s3c24xx_ts_pen_down(ts);
00224         } else if (updn & ADCUPDN_TSC_UP) {
00225                 /* Pen-up interrupt */
00226                 s3c24xx_ts_pen_up(ts);
00227         } else {
00228                 /* Presumably end-of-conversion interrupt */
00229 
00230                 /* Check end-of-conversion flag. */
00231                 if ((pio_read_32(&ts->io->con) & ADCCON_ECFLG) == 0) {
00232                         printf(NAME ": Unrecognized ts int.\n");
00233                         return;
00234                 }
00235 
00236                 if (ts->state != ts_sample_pos) {
00237                         /*
00238                          * We got an extra interrupt ater switching to
00239                          * wait for interrupt mode.
00240                          */
00241                         return;
00242                 }
00243 
00244                 /* End-of-conversion interrupt */
00245                 s3c24xx_ts_eoc(ts);
00246         }
00247 }
00248 
00253 static void s3c24xx_ts_pen_down(s3c24xx_ts_t *ts)
00254 {
00255         /* Pen-down interrupt */
00256 
00257         ts->state = ts_sample_pos;
00258 
00259         /* Enable auto xy-conversion mode */
00260         pio_write_32(&ts->io->tsc, (pio_read_32(&ts->io->tsc)
00261             & ~3) | 4);
00262 
00263         /* Start the conversion. */
00264         pio_write_32(&ts->io->con, pio_read_32(&ts->io->con)
00265             | ADCCON_ENABLE_START);
00266 }
00267 
00272 static void s3c24xx_ts_pen_up(s3c24xx_ts_t *ts)
00273 {
00274         int button, press;
00275 
00276         /* Pen-up interrupt */
00277 
00278         ts->state = ts_wait_pendown;
00279 
00280         button = 1;
00281         press = 0;
00282         async_msg_2(ts->client_phone, MEVENT_BUTTON, button, press);
00283 
00284         s3c24xx_ts_wait_for_int_mode(ts, updn_down);
00285 }
00286 
00291 static void s3c24xx_ts_eoc(s3c24xx_ts_t *ts)
00292 {
00293         uint32_t data;
00294         int button, press;
00295         int smp0, smp1;
00296         int x_pos, y_pos;
00297         int dx, dy;
00298 
00299         ts->state = ts_wait_penup;
00300 
00301         /* Read in sampled data. */
00302 
00303         data = pio_read_32(&ts->io->dat0);
00304         smp0 = data & 0x3ff;
00305 
00306         data = pio_read_32(&ts->io->dat1);
00307         smp1 = data & 0x3ff;
00308 
00309         /* Convert to screen coordinates. */
00310         s3c24xx_ts_convert_samples(smp0, smp1, &x_pos, &y_pos);
00311 
00312         printf("s0: 0x%03x, s1:0x%03x -> x:%d,y:%d\n", smp0, smp1,
00313             x_pos, y_pos);
00314 
00315         /* Get differences. */
00316         dx = x_pos - ts->last_x;
00317         dy = y_pos - ts->last_y;
00318 
00319         button = 1;
00320         press = 1;
00321 
00322         /* Send notifications to client. */
00323         async_msg_2(ts->client_phone, MEVENT_MOVE, dx, dy);
00324         async_msg_2(ts->client_phone, MEVENT_BUTTON, button, press);
00325 
00326         ts->last_x = x_pos;
00327         ts->last_y = y_pos;
00328 
00329         s3c24xx_ts_wait_for_int_mode(ts, updn_up);
00330 }
00331 
00333 static void s3c24xx_ts_convert_samples(int smp0, int smp1, int *x, int *y)
00334 {
00335         /*
00336          * The orientation and display dimensions are GTA02-specific and the
00337          * calibration values might even specific to the individual piece
00338          * of hardware.
00339          *
00340          * The calibration values can be obtained by touching corners
00341          * of the screen with the stylus and noting the sampled values.
00342          */
00343         *x = lin_map_range(smp1, 0xa1, 0x396, 0, 479);
00344         *y = lin_map_range(smp0, 0x69, 0x38a, 639, 0);
00345 }
00346 
00360 static int lin_map_range(int v, int i0, int i1, int o0, int o1)
00361 {
00362         if (v < i0)
00363                 v = i0;
00364 
00365         if (v > i1)
00366                 v = i1;
00367 
00368         return o0 + (o1 - o0) * (v - i0) / (i1 - i0);
00369 }
00370 
00372 static void s3c24xx_ts_connection(ipc_callid_t iid, ipc_call_t *icall)
00373 {
00374         ipc_callid_t callid;
00375         ipc_call_t call;
00376         int retval;
00377 
00378         async_answer_0(iid, EOK);
00379 
00380         while (1) {
00381                 callid = async_get_call(&call);
00382                 switch (IPC_GET_IMETHOD(call)) {
00383                 case IPC_M_PHONE_HUNGUP:
00384                         if (ts->client_phone != -1) {
00385                                 async_hangup(ts->client_phone);
00386                                 ts->client_phone = -1;
00387                         }
00388 
00389                         async_answer_0(callid, EOK);
00390                         return;
00391                 case IPC_M_CONNECT_TO_ME:
00392                         if (ts->client_phone != -1) {
00393                                 retval = ELIMIT;
00394                                 break;
00395                         }
00396                         ts->client_phone = IPC_GET_ARG5(call);
00397                         retval = 0;
00398                         break;
00399                 default:
00400                         retval = EINVAL;
00401                 }
00402                 async_answer_0(callid, retval);
00403         }
00404 }
00405 

Generated on Thu Jun 2 07:45:50 2011 for HelenOS/USB by  doxygen 1.4.7