Patchwork [v2,11/20] arm: add Faraday FTTSC010 touchscreen controller support

login
register
mail settings
Submitter Kuo-Jung Su
Date Jan. 25, 2013, 8:19 a.m.
Message ID <1359101996-11667-12-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/215601/
State New
Headers show

Comments

Kuo-Jung Su - Jan. 25, 2013, 8:19 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTTSC010 provides controlling signals to handle ADC conversion
and panel driver. The control signal is base on Faraday 12-bit ADC
IP of FXADC140HC0H_TPANEL, which is a 12-bit SAR ADC with 8-to-1
multiplexer, IPGA and panel driver. The ADC provides 8-channel analog
input via a multiplexer. There are 4 channels, including the panel
drivers, are used to measure the x-y position. One of the channels
embeds an IPGA, inputs the programmable gain amplifier to record voice.
The other three channels are auxiliary analog inputs, and can be used
to measure the system environment parameter, for example battery, chip
temperature and so on.

The FTTSC010 provides two operation modes to sample the analog input
voltage. The manual operation mode needs to program and control the
panel drivers by software step-by-step for the x-y position measurement.
The auto-scan mode provides a periodic sampling method to convert the
analog input. It also enables the function of auto-position measurement
to automatically control the panel drivers of ADC for the x-y position
measurement. It will auto-store these ADC conversion values that are
X+, X-, Y+, Y- references and the x-y position values from the four
channels of ADC. Users can easily read these values from six registers.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/fttsc010.c |  260 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/fttsc010.h |   23 +++++
 2 files changed, 283 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..40e2c38
--- /dev/null
+++ b/hw/fttsc010.c
@@ -0,0 +1,260 @@ 
+/*
+ * Faraday FTTSC010 emulator.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2.
+ */
+
+#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))
+
+#define TYPE_FTTSC010   "fttsc010"
+
+typedef struct Fttsc010State {
+    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;
+
+#define FTTSC010(obj) \
+    OBJECT_CHECK(fttsc010_state, obj, TYPE_FTTSC010)
+
+static void fttsc010_update_irq(fttsc010_state *s)
+{
+    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(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(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_ops = {
+    .read  = fttsc010_mem_read,
+    .write = fttsc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void fttsc010_timer_tick(void *opaque)
+{
+    fttsc010_state *s = FTTSC010(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(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 *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    fttsc010_state *s = FTTSC010(FROM_SYSBUS(fttsc010_state, busdev));
+
+    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 = FTTSC010(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_ops,
+                          s,
+                          TYPE_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 = {
+    .name = TYPE_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_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init     = fttsc010_init;
+    dc->reset   = fttsc010_reset;
+    dc->vmsd    = &vmstate_fttsc010;
+    dc->no_user = 1;
+}
+
+static const TypeInfo fttsc010_info = {
+    .name          = TYPE_FTTSC010,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(fttsc010_state),
+    .class_init    = fttsc010_class_init,
+};
+
+static void fttsc010_register_types(void)
+{
+    type_register_static(&fttsc010_info);
+}
+
+type_init(fttsc010_register_types)
diff --git a/hw/fttsc010.h b/hw/fttsc010.h
new file mode 100644
index 0000000..8619de3
--- /dev/null
+++ b/hw/fttsc010.h
@@ -0,0 +1,23 @@ 
+/*
+ * Faraday FTTSC010 touchscreen driver
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#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