From: Kuo-Jung Su <dant...@faraday-tech.com> The FTTSC010 provides two operation modes to sample the analog input voltage. 1. The manual operation mode needs to program and control the panel drivers by software step-by-step for the x-y position measurement. 2. The auto-scan mode provides a periodic sampling method to convert the analog input.
This patch only implements the auto-scan mode. Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a369.c | 3 + hw/arm/fttsc010.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/arm/fttsc010.h | 23 +++++ 4 files changed, 287 insertions(+) create mode 100644 hw/arm/fttsc010.c create mode 100644 hw/arm/fttsc010.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 5175a9d..854c30a 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -50,3 +50,4 @@ obj-y += ftssp010.o obj-y += ftmac110.o obj-y += ftgmac100.o obj-y += ftlcdc200.o +obj-y += fttsc010.o diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c index 9100616..fd81e2b 100644 --- a/hw/arm/faraday_a369.c +++ b/hw/arm/faraday_a369.c @@ -193,6 +193,9 @@ a369_device_init(A369State *s) pic[23], /* FIFO Under-Run */ pic[22], /* AHB Bus Error */ NULL); + + /* fttsc010 */ + sysbus_create_simple("fttsc010", 0x92400000, pic[19]); } static void diff --git a/hw/arm/fttsc010.c b/hw/arm/fttsc010.c new file mode 100644 index 0000000..7e5bcb0 --- /dev/null +++ b/hw/arm/fttsc010.c @@ -0,0 +1,260 @@ +/* + * Faraday FTTSC010 emulator. + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su <dant...@faraday-tech.com> + * + * This code is licensed under GNU GPL v2+. + */ + +#include <hw/hw.h> +#include <hw/sysbus.h> +#include <hw/devices.h> +#include <ui/console.h> +#include <qemu/timer.h> +#include <sysemu/sysemu.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; +} Fttsc010State; + +#define FTTSC010(obj) \ + OBJECT_CHECK(Fttsc010State, obj, TYPE_FTTSC010) + +static void fttsc010_update_irq(Fttsc010State *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) +{ + Fttsc010State *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) +{ + Fttsc010State *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) +{ + Fttsc010State *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) +{ + Fttsc010State *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); + Fttsc010State *s = FTTSC010(FROM_SYSBUS(Fttsc010State, 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) +{ + Fttsc010State *s = FTTSC010(FROM_SYSBUS(Fttsc010State, 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, Fttsc010State), + VMSTATE_UINT32(isr, Fttsc010State), + VMSTATE_UINT32(imr, Fttsc010State), + VMSTATE_UINT32(csr, Fttsc010State), + VMSTATE_UINT32(pfr, Fttsc010State), + VMSTATE_UINT32(dcr, Fttsc010State), + 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(Fttsc010State), + .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/arm/fttsc010.h b/hw/arm/fttsc010.h new file mode 100644 index 0000000..10ae937 --- /dev/null +++ b/hw/arm/fttsc010.h @@ -0,0 +1,23 @@ +/* + * Faraday FTTSC010 touchscreen driver + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su <dant...@faraday-tech.com> + * + * This file is licensed under GNU GPL v2+. + */ + +#ifndef HW_ARM_FTTSC010_H +#define HW_ARM_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 -- 1.7.9.5