From patchwork Wed Mar 6 07:27:33 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: 225385 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 C61D92C035F for ; Wed, 6 Mar 2013 18:31:36 +1100 (EST) Received: from localhost ([::1]:39377 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UD8p4-00010r-JQ for incoming@patchwork.ozlabs.org; Wed, 06 Mar 2013 02:31:34 -0500 Received: from eggs.gnu.org ([208.118.235.92]:57883) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UD8mb-0006Bx-Ie for qemu-devel@nongnu.org; Wed, 06 Mar 2013 02:29:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UD8mX-00039o-Gc for qemu-devel@nongnu.org; Wed, 06 Mar 2013 02:29:01 -0500 Received: from mail-ia0-x22d.google.com ([2607:f8b0:4001:c02::22d]:52411) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UD8mX-00039i-BE for qemu-devel@nongnu.org; Wed, 06 Mar 2013 02:28:57 -0500 Received: by mail-ia0-f173.google.com with SMTP id h37so7156078iak.18 for ; Tue, 05 Mar 2013 23:28:56 -0800 (PST) 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=ZO/pZN8igHQWdJDeBr4SlZNI+Dky4wYeSBTP7m18LcQ=; b=kXdPz8W/7b6JZhmCUm64Lpn3PB+WDufHq/mS7E20S3frV1LYaLelZavsFBnzo+QI9f lLf9lJFuAhN0b7Q25JTYEwT8MmivwUg9Ua8PDtz0JgPNnVw3qp9ydatXJo0ELhrvZSSu wxJRu35tYqqLu/RlmLvpsEeSCgyhagoq/DFeLTeOf0hFkud1lVlRvsIQkeo56tu/4bW8 9SARCa7eU1WbggNS2zz5FdtVfwIOHyWFSmZkvc46/3Jo+1KR91qBEh0ZUqDrgZZzPEzy i0iopriMfH5gf+hTxbD1tplSIFOJjRXx1l3tA6NhY0LMUe62cbIXrQk/l8zzyBsCE4mz tKaA== X-Received: by 10.43.4.74 with SMTP id ob10mr30366292icb.28.1362554936843; Tue, 05 Mar 2013 23:28:56 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id ew5sm22823374igc.2.2013.03.05.23.28.53 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Tue, 05 Mar 2013 23:28:56 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Wed, 6 Mar 2013 15:27:33 +0800 Message-Id: <1362554857-3896-21-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1362554857-3896-1-git-send-email-dantesu@gmail.com> References: <1362554857-3896-1-git-send-email-dantesu@gmail.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:4001:c02::22d Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Kuo-Jung Su , Paul Brook , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v6 20/24] hw/arm: add Faraday 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 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 | 264 +++++++++++++++++++++++++++++++++++++++++++++ hw/arm/fttsc010.h | 39 +++++++ 4 files changed, 307 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 5d5e36c..8c12beb 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 2b2f31d..0265f4e 100644 --- a/hw/arm/faraday_a369_soc.c +++ b/hw/arm/faraday_a369_soc.c @@ -262,6 +262,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 int a369soc_init(SysBusDevice *dev) diff --git a/hw/arm/fttsc010.c b/hw/arm/fttsc010.c new file mode 100644 index 0000000..b0ae0b1 --- /dev/null +++ b/hw/arm/fttsc010.c @@ -0,0 +1,264 @@ +/* + * 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 "faraday.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(rt_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(rt_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 int fttsc010_init(SysBusDevice *dev) +{ + Fttsc010State *s = FTTSC010(dev); + + s->qtimer = qemu_new_timer_ms(rt_clock, fttsc010_timer_tick, s); + + memory_region_init_io(&s->iomem, + &mmio_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_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) +{ + 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->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