]> git.proxmox.com Git - mirror_qemu.git/blobdiff - hw/tsc210x.c
misc: move include files to include/qemu/
[mirror_qemu.git] / hw / tsc210x.c
index f04b19d334f1c2b529d26f84f1ca8706f2188e39..2076c355d2225b303b994185cfedf4571b2ada17 100644 (file)
@@ -1,29 +1,30 @@
 /*
  * TI TSC2102 (touchscreen/sensors/audio controller) emulator.
+ * TI TSC2301 (touchscreen/sensors/keypad).
  *
  * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "hw.h"
 #include "audio/audio.h"
-#include "qemu-timer.h"
-#include "console.h"
-#include "omap.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+#include "omap.h"      /* For I2SCodec and uWireSlave */
+#include "devices.h"
 
 #define TSC_DATA_REGISTERS_PAGE                0x0
 #define TSC_CONTROL_REGISTERS_PAGE     0x1
 
 #define TSC_CUT_RESOLUTION(value, p)   ((value) >> (16 - resolution[p]))
 
-struct tsc210x_state_s {
+typedef struct {
     qemu_irq pint;
+    qemu_irq kbint;
+    qemu_irq davint;
     QEMUTimer *timer;
     QEMUSoundCard card;
-    struct uwire_slave_s chip;
-    struct i2s_codec_s codec;
+    uWireSlave chip;
+    I2SCodec codec;
     uint8_t in_fifo[16384];
     uint8_t out_fifo[16384];
+    uint16_t model;
 
     int x, y;
     int pressure;
@@ -64,7 +68,7 @@ struct tsc210x_state_s {
     uint16_t audio_ctrl1;
     uint16_t audio_ctrl2;
     uint16_t audio_ctrl3;
-    uint16_t pll[2];
+    uint16_t pll[3];
     uint16_t volume;
     int64_t volume_change;
     int softstep;
@@ -77,8 +81,18 @@ struct tsc210x_state_s {
     SWVoiceOut *dac_voice[1];
     int i2s_rx_rate;
     int i2s_tx_rate;
-    AudioState *audio;
-};
+
+    int tr[8];
+
+    struct {
+        uint16_t down;
+        uint16_t mask;
+        int scan;
+        int debounce;
+        int mode;
+        int intr;
+    } kb;
+} TSC210xState;
 
 static const int resolution[4] = { 12, 8, 10, 12 };
 
@@ -118,21 +132,15 @@ static const uint16_t mode_regs[16] = {
     0x0000,    /* Y+, X- drivers */
 };
 
-/*
- * Convert screen coordinates to arbitrary values that the
- * touchscreen in my Palm Tungsten E device returns.
- * This shouldn't really matter (because the guest system
- * should calibrate the touchscreen anyway), but let's
- * imitate some real hardware.
- */
-#define X_TRANSFORM(value)             \
-    ((3850 - ((int) (value) * (3850 - 250) / 32768)) << 4)
-#define Y_TRANSFORM(value)             \
-    ((150 + ((int) (value) * (3037 - 150) / 32768)) << 4)
+#define X_TRANSFORM(s)                 \
+    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s)                 \
+    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
 #define Z1_TRANSFORM(s)                        \
-    ((400 - (s)->x + ((s)->pressure << 9)) << 4)
+    ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
 #define Z2_TRANSFORM(s)                        \
-    ((4000 + (s)->y - ((s)->pressure << 10)) << 4)
+    ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
 #define BAT1_VAL                       0x8660
 #define BAT2_VAL                       0x0000
 #define AUX1_VAL                       0x35c0
@@ -143,7 +151,7 @@ static const uint16_t mode_regs[16] = {
 #define TSC_POWEROFF_DELAY             50
 #define TSC_SOFTSTEP_DELAY             50
 
-static void tsc210x_reset(struct tsc210x_state_s *s)
+static void tsc210x_reset(TSC210xState *s)
 {
     s->state = 0;
     s->pin_func = 2;
@@ -160,6 +168,7 @@ static void tsc210x_reset(struct tsc210x_state_s *s)
     s->audio_ctrl3 = 0x0000;
     s->pll[0] = 0x1004;
     s->pll[1] = 0x0000;
+    s->pll[2] = 0x1fff;
     s->volume = 0xffff;
     s->dac_power = 0x8540;
     s->softstep = 1;
@@ -189,17 +198,25 @@ static void tsc210x_reset(struct tsc210x_state_s *s)
     s->i2s_tx_rate = 0;
     s->i2s_rx_rate = 0;
 
+    s->kb.scan = 1;
+    s->kb.debounce = 0;
+    s->kb.mask = 0x0000;
+    s->kb.mode = 3;
+    s->kb.intr = 0;
+
     qemu_set_irq(s->pint, !s->irq);
+    qemu_set_irq(s->davint, !s->dav);
+    qemu_irq_raise(s->kbint);
 }
 
-struct tsc210x_rate_info_s {
+typedef struct {
     int rate;
     int dsor;
     int fsref;
-};
+} TSC210xRateInfo;
 
 /*  { rate,  dsor,  fsref } */
-static const struct tsc210x_rate_info_s tsc2101_rates[] = {
+static const TSC210xRateInfo tsc2101_rates[] = {
     /* Fsref / 6.0 */
     { 7350,    7,      1 },
     { 8000,    7,      0 },
@@ -229,7 +246,7 @@ static const struct tsc210x_rate_info_s tsc2101_rates[] = {
 };
 
 /*  { rate,   dsor, fsref }    */
-static const struct tsc210x_rate_info_s tsc2102_rates[] = {
+static const TSC210xRateInfo tsc2102_rates[] = {
     /* Fsref / 6.0 */
     { 7350,    63,     1 },
     { 8000,    63,     0 },
@@ -258,7 +275,7 @@ static const struct tsc210x_rate_info_s tsc2102_rates[] = {
     { 0,       0,      0 },
 };
 
-static inline void tsc210x_out_flush(struct tsc210x_state_s *s, int len)
+static inline void tsc210x_out_flush(TSC210xState *s, int len)
 {
     uint8_t *data = s->codec.out.fifo + s->codec.out.start;
     uint8_t *end = data + len;
@@ -272,7 +289,7 @@ static inline void tsc210x_out_flush(struct tsc210x_state_s *s, int len)
     s->codec.out.start = 0;
 }
 
-static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b)
+static void tsc210x_audio_out_cb(TSC210xState *s, int free_b)
 {
     if (s->codec.out.len >= free_b) {
         tsc210x_out_flush(s, free_b);
@@ -283,59 +300,73 @@ static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b)
     qemu_irq_raise(s->codec.tx_start);
 }
 
-static void tsc2102_audio_set_format(struct tsc210x_state_s *s)
+static void tsc2102_audio_rate_update(TSC210xState *s)
+{
+    const TSC210xRateInfo *rate;
+
+    s->codec.tx_rate = 0;
+    s->codec.rx_rate = 0;
+    if (s->dac_power & (1 << 15))                              /* PWDNC */
+        return;
+
+    for (rate = tsc2102_rates; rate->rate; rate ++)
+        if (rate->dsor == (s->audio_ctrl1 & 0x3f) &&           /* DACFS */
+                        rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */
+            break;
+    if (!rate->rate) {
+        printf("%s: unknown sampling rate configured\n", __FUNCTION__);
+        return;
+    }
+
+    s->codec.tx_rate = rate->rate;
+}
+
+static void tsc2102_audio_output_update(TSC210xState *s)
 {
     int enable;
-    const struct tsc210x_rate_info_s *rate;
-    audsettings_t fmt;
+    struct audsettings fmt;
 
     if (s->dac_voice[0]) {
         tsc210x_out_flush(s, s->codec.out.len);
         s->codec.out.size = 0;
         AUD_set_active_out(s->dac_voice[0], 0);
         AUD_close_out(&s->card, s->dac_voice[0]);
-        s->dac_voice[0] = 0;
+        s->dac_voice[0] = NULL;
     }
+    s->codec.cts = 0;
 
     enable =
             (~s->dac_power & (1 << 15)) &&                     /* PWDNC */
             (~s->dac_power & (1 << 10));                       /* DAPWDN */
-    if (!enable)
+    if (!enable || !s->codec.tx_rate)
         return;
 
-    for (rate = tsc2102_rates; rate->rate; rate ++)
-        if (rate->dsor == (s->audio_ctrl1 & 0x3f) &&           /* DACFS */
-                        rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */
-            break;
-    if (!rate->rate) {
-        printf("%s: unknown sampling rate configured\n", __FUNCTION__);
-        return;
-    }
-
     /* Force our own sampling rate even in slave DAC mode */
     fmt.endianness = 0;
     fmt.nchannels = 2;
-    fmt.freq = rate->rate;
+    fmt.freq = s->codec.tx_rate;
     fmt.fmt = AUD_FMT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
                     "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
-    if (s->dac_voice[0])
+    if (s->dac_voice[0]) {
+        s->codec.cts = 1;
         AUD_set_active_out(s->dac_voice[0], 1);
+    }
 }
 
-static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
+static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg)
 {
     switch (reg) {
     case 0x00: /* X */
         s->dav &= 0xfbff;
-        return TSC_CUT_RESOLUTION(X_TRANSFORM(s->x), s->precision) +
+        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
                 (s->noise & 3);
 
     case 0x01: /* Y */
         s->noise ++;
         s->dav &= 0xfdff;
-        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s->y), s->precision) ^
+        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
                 (s->noise & 3);
 
     case 0x02: /* Z1 */
@@ -349,11 +380,20 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
                 (s->noise & 3);
 
     case 0x04: /* KPData */
+        if ((s->model & 0xff00) == 0x2300) {
+            if (s->kb.intr && (s->kb.mode & 2)) {
+                s->kb.intr = 0;
+                qemu_irq_raise(s->kbint);
+            }
+            return s->kb.down;
+        }
+
         return 0xffff;
 
     case 0x05: /* BAT1 */
         s->dav &= 0xffbf;
-        return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision);
+        return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) +
+                (s->noise & 6);
 
     case 0x06: /* BAT2 */
         s->dav &= 0xffdf;
@@ -369,11 +409,13 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
 
     case 0x09: /* TEMP1 */
         s->dav &= 0xfffb;
-        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision);
+        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+                (s->noise & 5);
 
     case 0x0a: /* TEMP2 */
         s->dav &= 0xfffd;
-        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision);
+        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+                (s->noise & 3);
 
     case 0x0b: /* DAC */
         s->dav &= 0xfffe;
@@ -389,16 +431,26 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
 }
 
 static uint16_t tsc2102_control_register_read(
-                struct tsc210x_state_s *s, int reg)
+                TSC210xState *s, int reg)
 {
     switch (reg) {
     case 0x00: /* TSC ADC */
         return (s->pressure << 15) | ((!s->busy) << 14) |
                 (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; 
 
-    case 0x01: /* Status */
-        return (s->pin_func << 14) | ((!s->enabled) << 13) |
-                (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
+    case 0x01: /* Status / Keypad Control */
+        if ((s->model & 0xff00) == 0x2100)
+            return (s->pin_func << 14) | ((!s->enabled) << 13) |
+                    (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
+        else
+            return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) |
+                    (s->kb.debounce << 11);
+
+    case 0x02: /* DAC Control */
+        if ((s->model & 0xff00) == 0x2300)
+            return s->dac_power & 0x8000;
+        else
+            goto bad_reg;
 
     case 0x03: /* Reference */
         return s->ref;
@@ -409,7 +461,18 @@ static uint16_t tsc2102_control_register_read(
     case 0x05: /* Configuration */
         return s->timing;
 
+    case 0x06: /* Secondary configuration */
+        if ((s->model & 0xff00) == 0x2100)
+            goto bad_reg;
+        return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2];
+
+    case 0x10: /* Keypad Mask */
+        if ((s->model & 0xff00) == 0x2100)
+            goto bad_reg;
+        return s->kb.mask;
+
     default:
+    bad_reg:
 #ifdef TSC_VERBOSE
         fprintf(stderr, "tsc2102_control_register_read: "
                         "no such register: 0x%02x\n", reg);
@@ -418,7 +481,7 @@ static uint16_t tsc2102_control_register_read(
     }
 }
 
-static uint16_t tsc2102_audio_register_read(struct tsc210x_state_s *s, int reg)
+static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg)
 {
     int l_ch, r_ch;
     uint16_t val;
@@ -440,9 +503,9 @@ static uint16_t tsc2102_audio_register_read(struct tsc210x_state_s *s, int reg)
         l_ch = 1;
         r_ch = 1;
         if (s->softstep && !(s->dac_power & (1 << 10))) {
-            l_ch = (qemu_get_clock(vm_clock) >
+            l_ch = (qemu_get_clock_ns(vm_clock) >
                             s->volume_change + TSC_SOFTSTEP_DELAY);
-            r_ch = (qemu_get_clock(vm_clock) >
+            r_ch = (qemu_get_clock_ns(vm_clock) >
                             s->volume_change + TSC_SOFTSTEP_DELAY);
         }
 
@@ -451,7 +514,7 @@ static uint16_t tsc2102_audio_register_read(struct tsc210x_state_s *s, int reg)
     case 0x05: /* Stereo DAC Power Control */
         return 0x2aa0 | s->dac_power |
                 (((s->dac_power & (1 << 10)) &&
-                  (qemu_get_clock(vm_clock) >
+                  (qemu_get_clock_ns(vm_clock) >
                    s->powerdown + TSC_POWEROFF_DELAY)) << 6);
 
     case 0x06: /* Audio Control 3 */
@@ -500,7 +563,7 @@ static uint16_t tsc2102_audio_register_read(struct tsc210x_state_s *s, int reg)
 }
 
 static void tsc2102_data_register_write(
-                struct tsc210x_state_s *s, int reg, uint16_t value)
+                TSC210xState *s, int reg, uint16_t value)
 {
     switch (reg) {
     case 0x00: /* X */
@@ -524,7 +587,7 @@ static void tsc2102_data_register_write(
 }
 
 static void tsc2102_control_register_write(
-                struct tsc210x_state_s *s, int reg, uint16_t value)
+                TSC210xState *s, int reg, uint16_t value)
 {
     switch (reg) {
     case 0x00: /* TSC ADC */
@@ -538,10 +601,27 @@ static void tsc2102_control_register_write(
         s->filter = value & 0xff;
         return;
 
-    case 0x01: /* Status */
-        s->pin_func = value >> 14;
+    case 0x01: /* Status / Keypad Control */
+        if ((s->model & 0xff00) == 0x2100)
+            s->pin_func = value >> 14;
+       else {
+            s->kb.scan = (value >> 14) & 1;
+            s->kb.debounce = (value >> 11) & 7;
+            if (s->kb.intr && s->kb.scan) {
+                s->kb.intr = 0;
+                qemu_irq_raise(s->kbint);
+            }
+        }
         return;
 
+    case 0x02: /* DAC Control */
+        if ((s->model & 0xff00) == 0x2300) {
+            s->dac_power &= 0x7fff;
+            s->dac_power |= 0x8000 & value;
+        } else
+            goto bad_reg;
+        break;
+
     case 0x03: /* Reference */
         s->ref = value & 0x1f;
         return;
@@ -568,7 +648,21 @@ static void tsc2102_control_register_write(
 #endif
         return;
 
+    case 0x06: /* Secondary configuration */
+        if ((s->model & 0xff00) == 0x2100)
+            goto bad_reg;
+        s->kb.mode = value >> 14;
+        s->pll[2] = value & 0x3ffff;
+        return;
+
+    case 0x10: /* Keypad Mask */
+        if ((s->model & 0xff00) == 0x2100)
+            goto bad_reg;
+        s->kb.mask = value;
+        return;
+
     default:
+    bad_reg:
 #ifdef TSC_VERBOSE
         fprintf(stderr, "tsc2102_control_register_write: "
                         "no such register: 0x%02x\n", reg);
@@ -577,7 +671,7 @@ static void tsc2102_control_register_write(
 }
 
 static void tsc2102_audio_register_write(
-                struct tsc210x_state_s *s, int reg, uint16_t value)
+                TSC210xState *s, int reg, uint16_t value)
 {
     switch (reg) {
     case 0x00: /* Audio Control 1 */
@@ -587,8 +681,8 @@ static void tsc2102_audio_register_write(
             fprintf(stderr, "tsc2102_audio_register_write: "
                             "wrong value written into Audio 1\n");
 #endif
-        if (s->audio)
-            tsc2102_audio_set_format(s);
+        tsc2102_audio_rate_update(s);
+        tsc2102_audio_output_update(s);
         return;
 
     case 0x01:
@@ -601,7 +695,7 @@ static void tsc2102_audio_register_write(
 
     case 0x02: /* DAC Volume Control */
         s->volume = value;
-        s->volume_change = qemu_get_clock(vm_clock);
+        s->volume_change = qemu_get_clock_ns(vm_clock);
         return;
 
     case 0x03:
@@ -623,7 +717,7 @@ static void tsc2102_audio_register_write(
 
     case 0x05: /* Stereo DAC Power Control */
         if ((value & ~s->dac_power) & (1 << 10))
-            s->powerdown = qemu_get_clock(vm_clock);
+            s->powerdown = qemu_get_clock_ns(vm_clock);
 
         s->dac_power = value & 0x9543;
 #ifdef TSC_VERBOSE
@@ -631,8 +725,8 @@ static void tsc2102_audio_register_write(
             fprintf(stderr, "tsc2102_audio_register_write: "
                             "wrong value written into Power\n");
 #endif
-        if (s->audio)
-            tsc2102_audio_set_format(s);
+        tsc2102_audio_rate_update(s);
+        tsc2102_audio_output_update(s);
         return;
 
     case 0x06: /* Audio Control 3 */
@@ -643,8 +737,7 @@ static void tsc2102_audio_register_write(
             fprintf(stderr, "tsc2102_audio_register_write: "
                             "wrong value written into Audio 3\n");
 #endif
-        if (s->audio)
-            tsc2102_audio_set_format(s);
+        tsc2102_audio_output_update(s);
         return;
 
     case 0x07: /* LCH_BASS_BOOST_N0 */
@@ -706,7 +799,7 @@ static void tsc2102_audio_register_write(
 }
 
 /* This handles most of the chip logic.  */
-static void tsc210x_pin_update(struct tsc210x_state_s *s)
+static void tsc210x_pin_update(TSC210xState *s)
 {
     int64_t expires;
     int pin_state;
@@ -765,17 +858,17 @@ static void tsc210x_pin_update(struct tsc210x_state_s *s)
         return;
     }
 
-    if (!s->enabled || s->busy)
+    if (!s->enabled || s->busy || s->dav)
         return;
 
     s->busy = 1;
     s->precision = s->nextprecision;
     s->function = s->nextfunction;
-    expires = qemu_get_clock(vm_clock) + (ticks_per_sec >> 10);
+    expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10);
     qemu_mod_timer(s->timer, expires);
 }
 
-static uint16_t tsc210x_read(struct tsc210x_state_s *s)
+static uint16_t tsc210x_read(TSC210xState *s)
 {
     uint16_t ret = 0x0000;
 
@@ -785,6 +878,8 @@ static uint16_t tsc210x_read(struct tsc210x_state_s *s)
     switch (s->page) {
     case TSC_DATA_REGISTERS_PAGE:
         ret = tsc2102_data_register_read(s, s->offset);
+        if (!s->dav)
+            qemu_irq_raise(s->davint);
         break;
     case TSC_CONTROL_REGISTERS_PAGE:
         ret = tsc2102_control_register_read(s, s->offset);
@@ -793,7 +888,7 @@ static uint16_t tsc210x_read(struct tsc210x_state_s *s)
         ret = tsc2102_audio_register_read(s, s->offset);
         break;
     default:
-        cpu_abort(cpu_single_env, "tsc210x_read: wrong memory page\n");
+        hw_error("tsc210x_read: wrong memory page\n");
     }
 
     tsc210x_pin_update(s);
@@ -804,7 +899,7 @@ static uint16_t tsc210x_read(struct tsc210x_state_s *s)
     return ret;
 }
 
-static void tsc210x_write(struct tsc210x_state_s *s, uint16_t value)
+static void tsc210x_write(TSC210xState *s, uint16_t value)
 {
     /*
      * This is a two-state state machine for reading
@@ -830,8 +925,7 @@ static void tsc210x_write(struct tsc210x_state_s *s, uint16_t value)
                 tsc2102_audio_register_write(s, s->offset, value);
                 break;
             default:
-                cpu_abort(cpu_single_env,
-                                "tsc210x_write: wrong memory page\n");
+                hw_error("tsc210x_write: wrong memory page\n");
             }
 
         tsc210x_pin_update(s);
@@ -839,9 +933,28 @@ static void tsc210x_write(struct tsc210x_state_s *s, uint16_t value)
     }
 }
 
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len)
+{
+    TSC210xState *s = opaque;
+    uint32_t ret = 0;
+
+    if (len != 16)
+        hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
+
+    /* TODO: sequential reads etc - how do we make sure the host doesn't
+     * unintentionally read out a conversion result from a register while
+     * transmitting the command word of the next command?  */
+    if (!value || (s->state && s->command))
+        ret = tsc210x_read(s);
+    if (value || (s->state && !s->command))
+        tsc210x_write(s, value);
+
+    return ret;
+}
+
 static void tsc210x_timer_tick(void *opaque)
 {
-    struct tsc210x_state_s *s = opaque;
+    TSC210xState *s = opaque;
 
     /* Timer ticked -- a set of conversions has been finished.  */
 
@@ -851,12 +964,13 @@ static void tsc210x_timer_tick(void *opaque)
     s->busy = 0;
     s->dav |= mode_regs[s->function];
     tsc210x_pin_update(s);
+    qemu_irq_lower(s->davint);
 }
 
 static void tsc210x_touchscreen_event(void *opaque,
                 int x, int y, int z, int buttons_state)
 {
-    struct tsc210x_state_s *s = opaque;
+    TSC210xState *s = opaque;
     int p = s->pressure;
 
     if (buttons_state) {
@@ -874,7 +988,7 @@ static void tsc210x_touchscreen_event(void *opaque,
         tsc210x_pin_update(s);
 }
 
-static void tsc210x_i2s_swallow(struct tsc210x_state_s *s)
+static void tsc210x_i2s_swallow(TSC210xState *s)
 {
     if (s->dac_voice[0])
         tsc210x_out_flush(s, s->codec.out.len);
@@ -882,7 +996,7 @@ static void tsc210x_i2s_swallow(struct tsc210x_state_s *s)
         s->codec.out.len = 0;
 }
 
-static void tsc210x_i2s_set_rate(struct tsc210x_state_s *s, int in, int out)
+static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out)
 {
     s->i2s_tx_rate = out;
     s->i2s_rx_rate = in;
@@ -890,8 +1004,8 @@ static void tsc210x_i2s_set_rate(struct tsc210x_state_s *s, int in, int out)
 
 static void tsc210x_save(QEMUFile *f, void *opaque)
 {
-    struct tsc210x_state_s *s = (struct tsc210x_state_s *) opaque;
-    int64_t now = qemu_get_clock(vm_clock);
+    TSC210xState *s = (TSC210xState *) opaque;
+    int64_t now = qemu_get_clock_ns(vm_clock);
     int i;
 
     qemu_put_be16(f, s->x);
@@ -925,8 +1039,8 @@ static void tsc210x_save(QEMUFile *f, void *opaque)
     qemu_put_be16s(f, &s->pll[0]);
     qemu_put_be16s(f, &s->pll[1]);
     qemu_put_be16s(f, &s->volume);
-    qemu_put_be64(f, (uint64_t) (s->volume_change - now));
-    qemu_put_be64(f, (uint64_t) (s->powerdown - now));
+    qemu_put_sbe64(f, (s->volume_change - now));
+    qemu_put_sbe64(f, (s->powerdown - now));
     qemu_put_byte(f, s->softstep);
     qemu_put_be16s(f, &s->dac_power);
 
@@ -936,8 +1050,8 @@ static void tsc210x_save(QEMUFile *f, void *opaque)
 
 static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
 {
-    struct tsc210x_state_s *s = (struct tsc210x_state_s *) opaque;
-    int64_t now = qemu_get_clock(vm_clock);
+    TSC210xState *s = (TSC210xState *) opaque;
+    int64_t now = qemu_get_clock_ns(vm_clock);
     int i;
 
     s->x = qemu_get_be16(f);
@@ -971,8 +1085,8 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be16s(f, &s->pll[0]);
     qemu_get_be16s(f, &s->pll[1]);
     qemu_get_be16s(f, &s->volume);
-    s->volume_change = (int64_t) qemu_get_be64(f) + now;
-    s->powerdown = (int64_t) qemu_get_be64(f) + now;
+    s->volume_change = qemu_get_sbe64(f) + now;
+    s->powerdown = qemu_get_sbe64(f) + now;
     s->softstep = qemu_get_byte(f);
     qemu_get_be16s(f, &s->dac_power);
 
@@ -981,27 +1095,35 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
 
     s->busy = qemu_timer_pending(s->timer);
     qemu_set_irq(s->pint, !s->irq);
+    qemu_set_irq(s->davint, !s->dav);
 
     return 0;
 }
 
-static int tsc2102_iid = 0;
-
-struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio)
+uWireSlave *tsc2102_init(qemu_irq pint)
 {
-    struct tsc210x_state_s *s;
+    TSC210xState *s;
 
-    s = (struct tsc210x_state_s *)
-            qemu_mallocz(sizeof(struct tsc210x_state_s));
-    memset(s, 0, sizeof(struct tsc210x_state_s));
+    s = (TSC210xState *)
+            g_malloc0(sizeof(TSC210xState));
+    memset(s, 0, sizeof(TSC210xState));
     s->x = 160;
     s->y = 160;
     s->pressure = 0;
     s->precision = s->nextprecision = 0;
-    s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s);
+    s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
     s->pint = pint;
+    s->model = 0x2102;
     s->name = "tsc2102";
-    s->audio = audio;
+
+    s->tr[0] = 0;
+    s->tr[1] = 1;
+    s->tr[2] = 1;
+    s->tr[3] = 0;
+    s->tr[4] = 1;
+    s->tr[5] = 0;
+    s->tr[6] = 1;
+    s->tr[7] = 0;
 
     s->chip.opaque = s;
     s->chip.send = (void *) tsc210x_write;
@@ -1018,19 +1140,154 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio)
     qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
                     "QEMU TSC2102-driven Touchscreen");
 
-    if (s->audio)
-        AUD_register_card(s->audio, s->name, &s->card);
+    AUD_register_card(s->name, &s->card);
 
     qemu_register_reset((void *) tsc210x_reset, s);
-    register_savevm(s->name, tsc2102_iid ++, 0,
+    register_savevm(NULL, s->name, -1, 0,
                     tsc210x_save, tsc210x_load, s);
 
     return &s->chip;
 }
 
-struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip)
+uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
+{
+    TSC210xState *s;
+
+    s = (TSC210xState *)
+            g_malloc0(sizeof(TSC210xState));
+    memset(s, 0, sizeof(TSC210xState));
+    s->x = 400;
+    s->y = 240;
+    s->pressure = 0;
+    s->precision = s->nextprecision = 0;
+    s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
+    s->pint = penirq;
+    s->kbint = kbirq;
+    s->davint = dav;
+    s->model = 0x2301;
+    s->name = "tsc2301";
+
+    s->tr[0] = 0;
+    s->tr[1] = 1;
+    s->tr[2] = 1;
+    s->tr[3] = 0;
+    s->tr[4] = 1;
+    s->tr[5] = 0;
+    s->tr[6] = 1;
+    s->tr[7] = 0;
+
+    s->chip.opaque = s;
+    s->chip.send = (void *) tsc210x_write;
+    s->chip.receive = (void *) tsc210x_read;
+
+    s->codec.opaque = s;
+    s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
+    s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
+    s->codec.in.fifo = s->in_fifo;
+    s->codec.out.fifo = s->out_fifo;
+
+    tsc210x_reset(s);
+
+    qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
+                    "QEMU TSC2301-driven Touchscreen");
+
+    AUD_register_card(s->name, &s->card);
+
+    qemu_register_reset((void *) tsc210x_reset, s);
+    register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s);
+
+    return &s->chip;
+}
+
+I2SCodec *tsc210x_codec(uWireSlave *chip)
 {
-    struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque;
+    TSC210xState *s = (TSC210xState *) chip->opaque;
 
     return &s->codec;
 }
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen.  Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc210x_set_transform(uWireSlave *chip,
+                MouseTransformInfo *info)
+{
+    TSC210xState *s = (TSC210xState *) chip->opaque;
+#if 0
+    int64_t ltr[8];
+
+    ltr[0] = (int64_t) info->a[1] * info->y;
+    ltr[1] = (int64_t) info->a[4] * info->x;
+    ltr[2] = (int64_t) info->a[1] * info->a[3] -
+            (int64_t) info->a[4] * info->a[0];
+    ltr[3] = (int64_t) info->a[2] * info->a[4] -
+            (int64_t) info->a[5] * info->a[1];
+    ltr[4] = (int64_t) info->a[0] * info->y;
+    ltr[5] = (int64_t) info->a[3] * info->x;
+    ltr[6] = (int64_t) info->a[4] * info->a[0] -
+            (int64_t) info->a[1] * info->a[3];
+    ltr[7] = (int64_t) info->a[2] * info->a[3] -
+            (int64_t) info->a[5] * info->a[0];
+
+    /* Avoid integer overflow */
+    s->tr[0] = ltr[0] >> 11;
+    s->tr[1] = ltr[1] >> 11;
+    s->tr[2] = muldiv64(ltr[2], 1, info->a[6]);
+    s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]);
+    s->tr[4] = ltr[4] >> 11;
+    s->tr[5] = ltr[5] >> 11;
+    s->tr[6] = muldiv64(ltr[6], 1, info->a[6]);
+    s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]);
+#else
+
+    /* This version assumes touchscreen X & Y axis are parallel or
+     * perpendicular to LCD's  X & Y axis in some way.  */
+    if (abs(info->a[0]) > abs(info->a[1])) {
+        s->tr[0] = 0;
+        s->tr[1] = -info->a[6] * info->x;
+        s->tr[2] = info->a[0];
+        s->tr[3] = -info->a[2] / info->a[0];
+        s->tr[4] = info->a[6] * info->y;
+        s->tr[5] = 0;
+        s->tr[6] = info->a[4];
+        s->tr[7] = -info->a[5] / info->a[4];
+    } else {
+        s->tr[0] = info->a[6] * info->y;
+        s->tr[1] = 0;
+        s->tr[2] = info->a[1];
+        s->tr[3] = -info->a[2] / info->a[1];
+        s->tr[4] = 0;
+        s->tr[5] = -info->a[6] * info->x;
+        s->tr[6] = info->a[3];
+        s->tr[7] = -info->a[5] / info->a[3];
+    }
+
+    s->tr[0] >>= 11;
+    s->tr[1] >>= 11;
+    s->tr[3] <<= 4;
+    s->tr[4] >>= 11;
+    s->tr[5] >>= 11;
+    s->tr[7] <<= 4;
+#endif
+}
+
+void tsc210x_key_event(uWireSlave *chip, int key, int down)
+{
+    TSC210xState *s = (TSC210xState *) chip->opaque;
+
+    if (down)
+        s->kb.down |= 1 << key;
+    else
+        s->kb.down &= ~(1 << key);
+
+    if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) {
+        s->kb.intr = 1;
+        qemu_irq_lower(s->kbint);
+    } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) &&
+                    !(s->kb.mode & 1)) {
+        s->kb.intr = 0;
+        qemu_irq_raise(s->kbint);
+    }
+}