From patchwork Fri Mar 15 13:15:32 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 227992 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9A37D2C00A1 for ; Sat, 16 Mar 2013 00:29:20 +1100 (EST) Received: from localhost ([::1]:47596 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UGUhC-000307-L0 for incoming@patchwork.ozlabs.org; Fri, 15 Mar 2013 09:29:18 -0400 Received: from eggs.gnu.org ([208.118.235.92]:34497) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UGUUx-0001Ke-37 for qemu-devel@nongnu.org; Fri, 15 Mar 2013 09:16:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UGUUt-0005CT-9O for qemu-devel@nongnu.org; Fri, 15 Mar 2013 09:16:39 -0400 Received: from mail-pd0-f169.google.com ([209.85.192.169]:34964) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UGUUt-0005CM-0t for qemu-devel@nongnu.org; Fri, 15 Mar 2013 09:16:35 -0400 Received: by mail-pd0-f169.google.com with SMTP id 3so232000pdj.0 for ; Fri, 15 Mar 2013 06:16:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=gsdt8C5OmB1le80M9UgYkuO9b0vxLndSomXrqTHN5bs=; b=iUV6V7PMDD/YplZJfSz11UaMfYrJRYAffOvJlqxoBwVylM/5F8848v+nMrQZ3CbObU EuTg9tU2NGbxygiFmbYqo1av39MV+g0J4AKmbqc2EFMxR4mhY8uRdgNYHyuIPodDUTVV 9aPfb8/j1tkmndXyf6Yh47su300Qdh2YFG3rO/+ZTtDU7xkFiBUNH9MvMWM2cUrDFDf1 wOqPCGG5ZM7BALQ8DFHQcwvTvTB9zrG/l+GtaYbB8GYdjqAWBOKDOIEaDs3/ajibUOOr 60TEL33TD7me9I1or5/q0cistIB7jBqXv4IEfmFpHQXuXFxopVIR7zvnJ4Pbrz0rjoxl GnrQ== X-Received: by 10.68.242.132 with SMTP id wq4mr1597614pbc.160.1363353394406; Fri, 15 Mar 2013 06:16:34 -0700 (PDT) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id xr3sm8729551pbc.46.2013.03.15.06.16.31 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 15 Mar 2013 06:16:33 -0700 (PDT) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 15 Mar 2013 21:15:32 +0800 Message-Id: <1363353336-11237-21-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1363353336-11237-1-git-send-email-dantesu@gmail.com> References: <1363353336-11237-1-git-send-email-dantesu@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.192.169 Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v8 20/24] hw/arm: add FTTSC010 touchscreen controller support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Kuo-Jung Su 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 --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a369_soc.c | 3 + hw/arm/fttsc010.c | 260 +++++++++++++++++++++++++++++++++++++++++++++ hw/arm/fttsc010.h | 39 +++++++ 4 files changed, 303 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 6f3bf2d..9fdefc8 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -50,3 +50,4 @@ obj-y += fti2c010.o obj-y += ftssp010.o obj-y += ftgmac100.o obj-y += ftlcdc200.o +obj-y += fttsc010.o diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c index a6a65da..2177616 100644 --- a/hw/arm/faraday_a369_soc.c +++ b/hw/arm/faraday_a369_soc.c @@ -279,6 +279,9 @@ a369soc_device_init(FaradaySoCState *s) s->pic[23], /* FIFO Under-Run */ s->pic[22], /* AHB Bus Error */ NULL); + + /* fttsc010 */ + sysbus_create_simple("fttsc010", 0x92400000, s->pic[19]); } static void a369soc_realize(DeviceState *dev, Error **errp) diff --git a/hw/arm/fttsc010.c b/hw/arm/fttsc010.c new file mode 100644 index 0000000..fd12d9c --- /dev/null +++ b/hw/arm/fttsc010.c @@ -0,0 +1,260 @@ +/* + * Faraday FTTSC010 emulator. + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * 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" + +#define CFG_REGSIZE (0x3c / 4) + +typedef struct Fttsc010State { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + + uint64_t interval; + QEMUTimer *qtimer; + + int x, y; + int z1, z2; + uint32_t freq; + + /* HW registers */ + uint32_t regs[CFG_REGSIZE]; +} Fttsc010State; + +#define FTTSC010(obj) \ + OBJECT_CHECK(Fttsc010State, obj, TYPE_FTTSC010) + +#define TSC_REG32(s, off) \ + ((s)->regs[(off) / 4]) + +static void fttsc010_update_irq(Fttsc010State *s) +{ + qemu_set_irq(s->irq, !!(TSC_REG32(s, REG_IMR) & TSC_REG32(s, REG_ISR))); +} + +static uint64_t +fttsc010_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t ret = 0; + Fttsc010State *s = FTTSC010(opaque); + + switch (addr) { + case REG_CR ... REG_DCR: + ret = s->regs[addr / 4]; + break; + case REG_XYR: + ret = deposit32(ret, 0, 12, s->x); + ret = deposit32(ret, 16, 12, s->y); + break; + case REG_ZR: + ret = deposit32(ret, 0, 12, s->z1); + ret = deposit32(ret, 16, 12, s->z2); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "fttsc010: undefined memory access@%#" HWADDR_PRIx "\n", addr); + break; + } + + return ret; +} + +static void +fttsc010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + uint32_t dly, sdiv, mdiv; + Fttsc010State *s = FTTSC010(opaque); + + switch (addr) { + case REG_CR: + TSC_REG32(s, REG_CR) = (uint32_t)val; + if (TSC_REG32(s, REG_CR) & (CR_AS | CR_RD1)) { + /* ADC conversion delay with frame number */ + dly = extract32(TSC_REG32(s, REG_DCR), 0, 16); + /* ADC sample clock divider */ + sdiv = extract32(TSC_REG32(s, REG_CSR), 8, 8); + /* ADC main clock divider */ + mdiv = extract32(TSC_REG32(s, REG_CSR), 0, 8); + /* Calculate sample rate/timer interval */ + s->interval = s->freq / ((mdiv + 1) * (sdiv + 1) * (dly + 1) * 64); + s->interval = MAX(1ULL, s->interval); + qemu_mod_timer(s->qtimer, + s->interval + qemu_get_clock_ms(vm_clock)); + } else { + qemu_del_timer(s->qtimer); + } + break; + case REG_ISR: + TSC_REG32(s, REG_ISR) &= ~((uint32_t)val); + fttsc010_update_irq(s); + break; + case REG_IMR: + TSC_REG32(s, REG_IMR) = (uint32_t)val; + fttsc010_update_irq(s); + break; + case REG_CSR ... REG_DCR: + s->regs[addr / 4] = (uint32_t)val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "fttsc010: undefined memory access@%#" HWADDR_PRIx "\n", addr); + break; + } +} + +static const MemoryRegionOps mmio_ops = { + .read = fttsc010_mem_read, + .write = fttsc010_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void fttsc010_timer_tick(void *opaque) +{ + Fttsc010State *s = FTTSC010(opaque); + + /* update isr for auto-scan */ + if (TSC_REG32(s, REG_CR) & CR_AS) { + TSC_REG32(s, REG_ISR) |= ISR_AS; + } + + /* turn it off, when it's under one-shot mode */ + TSC_REG32(s, REG_CR) &= ~CR_RD1; + + /* update irq signal */ + fttsc010_update_irq(s); + + /* reschedule for next auto-scan */ + if (TSC_REG32(s, REG_CR) & CR_AS) { + qemu_mod_timer(s->qtimer, s->interval + qemu_get_clock_ms(vm_clock)); + } +} + +static void +fttsc010_touchscreen_event(void *opaque, int x, int y, int z, int bt) +{ + Fttsc010State *s = FTTSC010(opaque); + + if (bt) { + /* button pressed */ + 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 { + /* button released */ + s->z1 = 0; + s->z2 = 0; + } +} + +static void fttsc010_reset(DeviceState *ds) +{ + Fttsc010State *s = FTTSC010(SYS_BUS_DEVICE(ds)); + + memset(s->regs, 0, sizeof(s->regs)); + TSC_REG32(s, REG_REVR) = 0x00010000; /* rev. 1.0.0 */ + + /* initialize touch sample interval to 10 ms */ + TSC_REG32(s, REG_CSR) = CSR_SDIV(16) | CSR_MDIV(5); + TSC_REG32(s, REG_DCR) = DCR_DLY(100); + + s->x = 0; + s->y = 0; + s->z1 = 0; + s->z2 = 0; + + qemu_set_irq(s->irq, 0); +} + +static void fttsc010_realize(DeviceState *dev, Error **errp) +{ + Fttsc010State *s = FTTSC010(dev); + + s->qtimer = qemu_new_timer_ms(vm_clock, fttsc010_timer_tick, s); + + memory_region_init_io(&s->iomem, + &mmio_ops, + s, + TYPE_FTTSC010, + 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + qemu_add_mouse_event_handler(fttsc010_touchscreen_event, s, 1, + "QEMU FTTSC010-driven Touchscreen"); +} + +static const VMStateDescription vmstate_fttsc010 = { + .name = TYPE_FTTSC010, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, Fttsc010State, CFG_REGSIZE), + VMSTATE_END_OF_LIST(), + } +}; + +static Property properties_fttsc010[] = { + DEFINE_PROP_UINT32("freq", Fttsc010State, freq, 66000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void fttsc010_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = fttsc010_reset; + dc->realize = fttsc010_realize; + dc->vmsd = &vmstate_fttsc010; + dc->props = properties_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..b600645 --- /dev/null +++ b/hw/arm/fttsc010.h @@ -0,0 +1,39 @@ +/* + * Faraday FTTSC010 touchscreen driver + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL v2+. + */ + +#ifndef HW_ARM_FTTSC010_H +#define HW_ARM_FTTSC010_H + +#include "qemu/bitops.h" + +#define REG_CR 0x00 /* Control Register */ +#define REG_ISR 0x04 /* Interrupt Status Register */ +#define REG_IMR 0x08 /* Interrupt Mask Register */ +#define REG_REVR 0x0C /* Revision 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 */ + +#define CR_AS BIT(31) /* Auto-scan enable */ +#define CR_RD1 BIT(30) /* Single read enabled */ +#define CR_CHDLY(x) BIT(16 + (x % 7)) /* ADC channel x delay enabled */ +#define CR_APWRDN BIT(9) /* ADC auto power down mode */ +#define CR_PWRDN BIT(8) /* ADC and IPGA power down */ +#define CR_ICS(x) ((x) & 0xf) /* ADC analog input channel select */ + +#define CSR_SDIV(x) (((x) & 0xff) << 8) /* ADC sample clock divider */ +#define CSR_MDIV(x) (((x) & 0xff) << 0) /* ADC main clock divider */ + +#define DCR_DLY(x) ((x) & 0xffff) /* ADC conversion delay */ + +#define ISR_AS BIT(10) /* Auto-scan cpmplete */ + +#endif