Patchwork [11/18] hw: add QEMU model for Faraday touchscreen controller

login
register
mail settings
Submitter Dante
Date Jan. 18, 2013, 6:31 a.m.
Message ID <1358490696-18553-1-git-send-email-dantesu@faraday-tech.com>
Download mbox | patch
Permalink /patch/213493/
State New
Headers show

Comments

Dante - Jan. 18, 2013, 6:31 a.m.
Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/fttsc010.c |  239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/fttsc010.h |   35 +++++++++
 2 files changed, 274 insertions(+)
 create mode 100644 hw/fttsc010.c
 create mode 100644 hw/fttsc010.h

Patch

diff --git a/hw/fttsc010.c b/hw/fttsc010.c
new file mode 100644
index 0000000..2f5552e
--- /dev/null
+++ b/hw/fttsc010.c
@@ -0,0 +1,239 @@ 
+/*
+ * Faraday FTTSC010 emulator.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ *
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysbus.h"
+#include "ui/console.h"
+#include "devices.h"
+
+#include "fttsc010.h"
+
+#define X_AXIS_DMAX    3470
+#define X_AXIS_MIN    290
+#define Y_AXIS_DMAX    3450
+#define Y_AXIS_MIN    200
+
+#define ADS_XPOS(x, y)    (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
+#define ADS_YPOS(x, y)    (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
+#define ADS_Z1POS(x, y)    (8)
+#define ADS_Z2POS(x, y)    ((1600 + ADS_XPOS(x, y)) * ADS_Z1POS(x, y) / ADS_XPOS(x, y))
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    uint64_t interval;
+    QEMUTimer *qtimer;
+
+    int x, y;
+    int z1, z2;
+
+    /* HW registers */
+    uint32_t cr;
+    uint32_t isr;
+    uint32_t imr;
+    uint32_t csr;
+    uint32_t pfr;
+    uint32_t dcr;
+} fttsc010_state;
+
+static inline void fttsc010_update_irq(void *opaque)
+{
+    fttsc010_state *s = (fttsc010_state *) opaque;
+    
+    if (s->imr & s->isr)
+        qemu_irq_raise(s->irq);
+    else
+        qemu_irq_lower(s->irq);
+}
+
+static uint64_t fttsc010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    fttsc010_state *s = (fttsc010_state *) opaque;
+
+    switch (addr) {
+    case REG_CR:
+        return s->cr;
+    case REG_ISR:
+        return s->isr;
+    case REG_IMR:
+        return s->imr;
+    case REG_VER:
+        return 0x00010000;
+    case REG_CSR:
+        return s->csr;
+    case REG_PFR:
+        return s->pfr;
+    case REG_DCR:
+        return s->dcr;
+    case REG_XYR:
+        return (s->x & 0x0fff) | ((s->y & 0x0fff) << 16);
+    case REG_ZR:
+        return (s->z1 & 0x0fff) | ((s->z2 & 0x0fff) << 16);
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static void fttsc010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    fttsc010_state *s = (fttsc010_state *) opaque;
+
+    switch (addr) {
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        if (s->cr & (3 << 30))
+            qemu_mod_timer(s->qtimer, s->interval + qemu_get_clock_ns(vm_clock));
+        else
+            qemu_del_timer(s->qtimer);
+        break;
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        fttsc010_update_irq(s);
+        break;
+    case REG_IMR:
+        s->imr = (uint32_t)val;
+        fttsc010_update_irq(s);
+        break;
+    case REG_CSR:
+        s->csr = (uint32_t)val;
+        break;
+    case REG_PFR:
+        s->pfr = (uint32_t)val;
+        break;
+    case REG_DCR:
+        s->dcr = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps fttsc010_mem_ops = {
+    .read  = fttsc010_mem_read,
+    .write = fttsc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void fttsc010_timer_tick(void *opaque)
+{
+    fttsc010_state *s = (fttsc010_state *) opaque;
+    
+    /* if auto-scan enabled */    
+    if (s->cr & (3 << 30)) {
+        s->isr |= (1 << 10);
+        /* turn it off, when it's under one-shot mode */
+        if (s->cr & (1 << 30))
+            s->cr &= ~(1 << 30);
+    }
+
+    fttsc010_update_irq(s);
+
+    qemu_mod_timer(s->qtimer, s->interval + qemu_get_clock_ns(vm_clock));
+}
+
+static void fttsc010_touchscreen_event(void *opaque,
+                int x, int y, int z, int buttons_state)
+{
+    fttsc010_state *s = (fttsc010_state *) opaque;
+
+    if (buttons_state) {
+        x = 0x7fff - x;
+        s->x  = ADS_XPOS(x, y);
+        s->y  = ADS_YPOS(x, y);
+        s->z1 = ADS_Z1POS(x, y);
+        s->z2 = ADS_Z2POS(x, y);
+    } else {
+        s->z1 = 0;
+        s->z2 = 0;
+    }
+}
+
+static void fttsc010_reset(DeviceState *d)
+{
+    fttsc010_state *s = DO_UPCAST(fttsc010_state, busdev.qdev, d);
+    
+    s->cr  = 0;
+    s->isr = 0;
+    s->imr = 0;
+    s->csr = 0;
+    s->pfr = 0;
+    s->dcr = 0;
+
+    s->x  = 0;
+    s->y  = 0;
+    s->z1 = 0;
+    s->z2 = 0;
+
+    qemu_irq_lower(s->irq);
+}
+
+static int fttsc010_init(SysBusDevice *dev)
+{
+    fttsc010_state *s = FROM_SYSBUS(fttsc010_state, dev);
+    
+    s->interval = (uint64_t)get_ticks_per_sec() >> 6;
+    s->qtimer   = qemu_new_timer_ns(vm_clock, fttsc010_timer_tick, s);
+
+    memory_region_init_io(&s->iomem, &fttsc010_mem_ops, s, "fttsc010", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    
+    qemu_add_mouse_event_handler(fttsc010_touchscreen_event, s, 1,
+                    "QEMU FTTSC010-driven Touchscreen");
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_fttsc010_regs = {
+    .name = "fttsc010",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr, fttsc010_state),
+        VMSTATE_UINT32(isr, fttsc010_state),
+        VMSTATE_UINT32(imr, fttsc010_state),
+        VMSTATE_UINT32(csr, fttsc010_state),
+        VMSTATE_UINT32(pfr, fttsc010_state),
+        VMSTATE_UINT32(dcr, fttsc010_state),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void fttsc010_dev_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);    
+
+    sdc->init  = fttsc010_init;
+    k->vmsd    = &vmstate_fttsc010_regs;
+    k->reset   = fttsc010_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo fttsc010_dev_info = {
+    .name          = "fttsc010",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(fttsc010_state),
+    .class_init    = fttsc010_dev_class_init,
+};
+
+static void fttsc010_register_types(void)
+{
+    type_register_static(&fttsc010_dev_info);
+}
+
+type_init(fttsc010_register_types)
diff --git a/hw/fttsc010.h b/hw/fttsc010.h
new file mode 100644
index 0000000..c3756eb
--- /dev/null
+++ b/hw/fttsc010.h
@@ -0,0 +1,35 @@ 
+/*
+ * Faraday FTTSC010 touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term 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.
+ *
+ * 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
+ *
+ * Copyright 2009 Dante Su <dantesu@gmail.com>
+ *
+ */
+
+#ifndef FTTSC010_H
+#define FTTSC010_H
+
+#define REG_CR            0x00  /* Control Register */
+#define REG_ISR            0x04  /* Interrupt Status Register */
+#define REG_IMR            0x08  /* Interrupt Mask Register */
+#define REG_VER            0x0C  /* Version Register */
+#define REG_CSR            0x30  /* Clock & Sample Rate Register */
+#define REG_PFR            0x34  /* Panel Function Register */
+#define REG_DCR            0x38  /* Delay Control Register */
+#define REG_XYR            0x3C  /* Touchscreen X,Y-Axis Register */
+#define REG_ZR            0x4C  /* Touchscreen Z-Axis (Pressure) Register */
+
+#endif