From patchwork Wed Feb 6 09:45:21 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: 218519 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 607242C02B7 for ; Wed, 6 Feb 2013 20:53:59 +1100 (EST) Received: from localhost ([::1]:47229 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31hV-0007Em-Hv for incoming@patchwork.ozlabs.org; Wed, 06 Feb 2013 04:53:57 -0500 Received: from eggs.gnu.org ([208.118.235.92]:36977) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31h8-00075I-7V for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:39 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U31h3-0000VH-FV for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:34 -0500 Received: from mail-ia0-x22d.google.com ([2607:f8b0:4001:c02::22d]:45529) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31h3-0000VC-7R for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:29 -0500 Received: by mail-ia0-f173.google.com with SMTP id h37so1337573iak.4 for ; Wed, 06 Feb 2013 01:53:28 -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=I7CoX2IjIWY6UlAnHVjOfTvE9n57cgptcNtUn4zg8kA=; b=dA9TlaY8wGSy1ouqKwjPZTle3okOCo4CBdFpUMzAqrCgZ7WTHXItood4vVyxfB1h0E 3ViZQVq+MxMXnYyTlOFg8/Y6cmJ+KsekbrHRZlvbLbs9RafdjMPMxuOSsSsr+LCfwyfR 7FKEXyw/MjWLlKd1iiJSXhFWofDpvT/G15RpHTRo86rd16pYk9UlQaDpZgtisIDG/O6E pXLdtWD3S+IM6hXkzj3KZpp/C8M6Q3RWEWM7Dyuah21trbcxc1/m4aRnomSMVCYJm2N6 gQ/FyHBO81scc2u8/sHDZDKetwIv8QQwp8Q1kwSENFmgXS/hJxNqkvc3xiPXqes5adOn 5/Zg== X-Received: by 10.50.91.168 with SMTP id cf8mr4881776igb.20.1360144054810; Wed, 06 Feb 2013 01:47:34 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id hg2sm2671279igc.3.2013.02.06.01.47.31 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Wed, 06 Feb 2013 01:47:34 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Wed, 6 Feb 2013 17:45:21 +0800 Message-Id: <1360143925-10800-18-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1360143925-10800-1-git-send-email-dantesu@gmail.com> References: <1360143925-10800-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 , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v3 17/20] arm: add Faraday FTLCDC200 LCD 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 FTLCDC200 Color LCD controller performs translation of pixel-coded data into the required formats and timings to drive a variety of single/dual mono and color LCDs. Depending on the LCD type and mode, the unpacked data can represent: 1. an actual true display gray or color value 2. an address to a 256 x 16 bit wide palette RAM gray or color value. The FTLCDC200 generates 4 individual interrupts for: 1. DMA FIFO underflow 2. base address update 3. vertical status 4. bus error. There is also a single combined interrupt that is raised when any of the individual interrupts become active. Signed-off-by: Kuo-Jung Su --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a360.c | 3 + hw/arm/faraday_a369.c | 10 + hw/arm/ftlcdc200.c | 505 +++++++++++++++++++++++++++++++++++++++++++ hw/arm/ftlcdc200.h | 110 ++++++++++ hw/arm/ftlcdc200_template.h | 439 +++++++++++++++++++++++++++++++++++++ 6 files changed, 1068 insertions(+) create mode 100644 hw/arm/ftlcdc200.c create mode 100644 hw/arm/ftlcdc200.h create mode 100644 hw/arm/ftlcdc200_template.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 8498116..5175a9d 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -49,3 +49,4 @@ obj-y += fti2c010.o obj-y += ftssp010.o obj-y += ftmac110.o obj-y += ftgmac100.o +obj-y += ftlcdc200.o diff --git a/hw/arm/faraday_a360.c b/hw/arm/faraday_a360.c index 51e8649..d3df30e 100644 --- a/hw/arm/faraday_a360.c +++ b/hw/arm/faraday_a360.c @@ -132,6 +132,9 @@ a360_device_init(A360State *s) done_nic = 1; } } + + /* ftlcdc200 */ + sysbus_create_simple("ftlcdc200", 0x90600000, pic[27]); } static void diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c index 65dda61..9100616 100644 --- a/hw/arm/faraday_a369.c +++ b/hw/arm/faraday_a369.c @@ -183,6 +183,16 @@ a369_device_init(A369State *s) done_nic = 1; } } + + /* ftlcdc200 */ + sysbus_create_varargs("ftlcdc200", + 0x94a00000, + pic[0], /* ALL (NC in A369) */ + pic[25], /* VSTATUS */ + pic[24], /* Base Address Update */ + pic[23], /* FIFO Under-Run */ + pic[22], /* AHB Bus Error */ + NULL); } static void diff --git a/hw/arm/ftlcdc200.c b/hw/arm/ftlcdc200.c new file mode 100644 index 0000000..95a596d --- /dev/null +++ b/hw/arm/ftlcdc200.c @@ -0,0 +1,505 @@ +/* + * Faraday FTLCDC200 Color LCD Controller + * + * base is pl110.c + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under the GNU LGPL + */ + +#include +#include +#include +#include + +#include "ftlcdc200.h" + +enum ftlcdc200_irqpin { + IRQ_ALL = 0, + IRQ_VSTATUS, + IRQ_BASEUPT, + IRQ_FIFOUR, + IRQ_BUSERR, +}; + +enum ftlcdc200_bppmode { + BPP_1 = 0, + BPP_2, + BPP_4, + BPP_8, + BPP_16, + BPP_32, + BPP_16_565, + BPP_12, +}; + +#define TYPE_FTLCDC200 "ftlcdc200" + +typedef struct Ftlcdc200State { + SysBusDevice busdev; + MemoryRegion iomem; + DisplayState *ds; + qemu_irq irq[5]; + int cols; + int rows; + enum ftlcdc200_bppmode bpp; + int invalidate; + uint32_t palette[256]; + uint32_t raw_palette[128]; + + /* hw register caches */ + + uint32_t fer; /* function enable register */ + uint32_t ppr; /* panel pixel register */ + uint32_t ier; /* interrupt enable register */ + uint32_t isr; /* interrupt status register */ + uint32_t sppr; /* serail panel pixel register */ + + uint32_t fb[4]; /* frame buffer base address register */ + uint32_t ht; /* horizontal timing control register */ + uint32_t vt0; /* vertital timing control register 0 */ + uint32_t vt1; /* vertital timing control register 1 */ + uint32_t pol; /* polarity */ + +} Ftlcdc200State; + +#define FTLCDC200(obj) \ + OBJECT_CHECK(Ftlcdc200State, obj, TYPE_FTLCDC200) + +static const VMStateDescription vmstate_ftlcdc200 = { + .name = TYPE_FTLCDC200, + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(cols, Ftlcdc200State), + VMSTATE_INT32(rows, Ftlcdc200State), + VMSTATE_UINT32(bpp, Ftlcdc200State), + VMSTATE_INT32(invalidate, Ftlcdc200State), + VMSTATE_UINT32_ARRAY(palette, Ftlcdc200State, 256), + VMSTATE_UINT32_ARRAY(raw_palette, Ftlcdc200State, 128), + VMSTATE_UINT32(fer, Ftlcdc200State), + VMSTATE_UINT32(ppr, Ftlcdc200State), + VMSTATE_UINT32(ier, Ftlcdc200State), + VMSTATE_UINT32(isr, Ftlcdc200State), + VMSTATE_UINT32(sppr, Ftlcdc200State), + VMSTATE_UINT32_ARRAY(fb, Ftlcdc200State, 4), + VMSTATE_UINT32(ht, Ftlcdc200State), + VMSTATE_UINT32(vt0, Ftlcdc200State), + VMSTATE_UINT32(vt1, Ftlcdc200State), + VMSTATE_UINT32(pol, Ftlcdc200State), + VMSTATE_END_OF_LIST() + } +}; + +#define BITS 8 +#include "ftlcdc200_template.h" +#define BITS 15 +#include "ftlcdc200_template.h" +#define BITS 16 +#include "ftlcdc200_template.h" +#define BITS 24 +#include "ftlcdc200_template.h" +#define BITS 32 +#include "ftlcdc200_template.h" + +static int ftlcdc200_enabled(Ftlcdc200State *s) +{ + uint32_t mask = FER_EN | FER_ON; + return ((s->fer & mask) == mask) + && s->bpp && s->cols && s->rows && s->fb[0]; +} + +/* Update interrupts. */ +static void ftlcdc200_update_irq(Ftlcdc200State *s) +{ + uint32_t mask = s->ier & s->isr; + + if (mask) { + qemu_irq_raise(s->irq[IRQ_ALL]); + qemu_set_irq(s->irq[IRQ_FIFOUR], (mask & 0x01) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_BASEUPT], (mask & 0x02) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_VSTATUS], (mask & 0x04) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_BUSERR], (mask & 0x08) ? 1 : 0); + } else { + qemu_irq_lower(s->irq[IRQ_ALL]); + qemu_irq_lower(s->irq[IRQ_VSTATUS]); + qemu_irq_lower(s->irq[IRQ_BASEUPT]); + qemu_irq_lower(s->irq[IRQ_FIFOUR]); + qemu_irq_lower(s->irq[IRQ_BUSERR]); + } +} + +static void ftlcdc200_update_display(void *opaque) +{ + Ftlcdc200State *s = FTLCDC200(opaque); + drawfn *fntable; + drawfn fn; + int dest_width; + int src_width; + int bpp_offset; + int first; + int last; + + if (!ftlcdc200_enabled(s)) { + return; + } + + switch (ds_get_bits_per_pixel(s->ds)) { + case 0: + return; + case 8: + fntable = ftlcdc200_draw_fn_8; + dest_width = 1; + break; + case 15: + fntable = ftlcdc200_draw_fn_15; + dest_width = 2; + break; + case 16: + fntable = ftlcdc200_draw_fn_16; + dest_width = 2; + break; + case 24: + fntable = ftlcdc200_draw_fn_24; + dest_width = 3; + break; + case 32: + fntable = ftlcdc200_draw_fn_32; + dest_width = 4; + break; + default: + hw_error("ftlcdc200: Bad color depth\n"); + exit(1); + } + +#if 1 + bpp_offset = 0; + fn = fntable[s->bpp + bpp_offset]; +#else + if (s->ppr & PPR_BGR) { + bpp_offset = 0; + } else { + bpp_offset = 24; + } + if ((s->ppr & PPR_ENDIAN_MASK) == PPR_ENDIAN_BBBP) { + fn = fntable[s->bpp + 8 + bpp_offset]; + } else { + fn = fntable[s->bpp + bpp_offset]; + } +#endif + + src_width = s->cols; + switch (s->bpp) { + case BPP_1: + src_width >>= 3; + break; + case BPP_2: + src_width >>= 2; + break; + case BPP_4: + src_width >>= 1; + break; + case BPP_8: + break; + case BPP_16: + case BPP_16_565: + case BPP_12: + src_width <<= 1; + break; + case BPP_32: + src_width <<= 2; + break; + } + dest_width *= s->cols; + first = 0; + framebuffer_update_display(s->ds, sysbus_address_space(&s->busdev), + s->fb[0], s->cols, s->rows, + src_width, dest_width, 0, + s->invalidate, + fn, s->palette, + &first, &last); + if (s->ier & (IER_VCOMP | IER_NEXTFB)) { + s->isr |= (IER_VCOMP | IER_NEXTFB); + ftlcdc200_update_irq(s); + } + if (first >= 0) { + dpy_gfx_update(s->ds, 0, first, s->cols, last - first + 1); + } + s->invalidate = 0; +} + +static void ftlcdc200_invalidate_display(void *opaque) +{ + Ftlcdc200State *s = FTLCDC200(opaque); + s->invalidate = 1; + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->ds, s->cols, s->rows); + } +} + +static void ftlcdc200_update_palette(Ftlcdc200State *s, int n) +{ + int i; + uint32_t raw; + unsigned int r, g, b; + + raw = s->raw_palette[n]; + n <<= 1; + for (i = 0; i < 2; i++) { + r = (raw & 0x1f) << 3; + raw >>= 5; + g = (raw & 0x1f) << 3; + raw >>= 5; + b = (raw & 0x1f) << 3; + /* The I bit is ignored. */ + raw >>= 6; + switch (ds_get_bits_per_pixel(s->ds)) { + case 8: + s->palette[n] = rgb_to_pixel8(r, g, b); + break; + case 15: + s->palette[n] = rgb_to_pixel15(r, g, b); + break; + case 16: + s->palette[n] = rgb_to_pixel16(r, g, b); + break; + case 24: + case 32: + s->palette[n] = rgb_to_pixel32(r, g, b); + break; + } + n++; + } +} + +static void ftlcdc200_resize(Ftlcdc200State *s, int width, int height) +{ + if (width != s->cols || height != s->rows) { + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->ds, width, height); + } + } + s->cols = width; + s->rows = height; +} + +static uint64_t ftlcdc200_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + Ftlcdc200State *s = FTLCDC200(opaque); + + switch (addr) { + case REG_FER: + return s->fer; + case REG_PPR: + return s->ppr; + case REG_IER: + return s->ier; + case REG_ISR: + return s->isr; + case REG_FB0: + return s->fb[0]; + case REG_FB1: + return s->fb[1]; + case REG_FB2: + return s->fb[2]; + case REG_FB3: + return s->fb[3]; + case REG_HT: + return s->ht; + case REG_VT0: + return s->vt0; + case REG_VT1: + return s->vt1; + case REG_POL: + return s->pol; + case REG_SPPR: + return s->sppr; + default: + return 0; + } +} + +static void ftlcdc200_mem_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + Ftlcdc200State *s = FTLCDC200(opaque); + int n; + + /* For simplicity invalidate the display whenever a control register + is written to. */ + s->invalidate = 1; + + if (addr >= 0xA00 && addr < 0xC00) { + /* Palette. */ + n = (addr - 0xA00) >> 2; + s->raw_palette[(addr - 0xA00) >> 2] = val; + ftlcdc200_update_palette(s, n); + return; + } + + switch (addr) { + case REG_FER: + s->fer = (uint32_t)val; + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->ds, s->cols, s->rows); + } + break; + case REG_PPR: + s->ppr = (uint32_t)val; + switch (s->ppr & PPR_RGB_MASK) { + case PPR_RGB1: + s->bpp = BPP_1; + break; + case PPR_RGB2: + s->bpp = BPP_2; + break; + case PPR_RGB4: + s->bpp = BPP_4; + break; + case PPR_RGB8: + s->bpp = BPP_8; + break; + case PPR_RGB12: + s->bpp = BPP_12; + break; + case PPR_RGB16_555: + s->bpp = BPP_16; + break; + case PPR_RGB16_565: + s->bpp = BPP_16_565; + break; + case PPR_RGB24: + default: + s->bpp = BPP_32; + break; + } + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->ds, s->cols, s->rows); + } + break; + case REG_IER: + s->ier = (uint32_t)val; + ftlcdc200_update_irq(s); + break; + case REG_ISCR: + s->isr &= ~((uint32_t)val); + ftlcdc200_update_irq(s); + break; + case REG_FB0: + s->fb[0] = (uint32_t)val; + break; + case REG_FB1: + s->fb[1] = (uint32_t)val; + break; + case REG_FB2: + s->fb[2] = (uint32_t)val; + break; + case REG_FB3: + s->fb[3] = (uint32_t)val; + break; + case REG_HT: + s->ht = (uint32_t)val; + n = ((s->ht & 0xff) + 1) << 4; + ftlcdc200_resize(s, n, s->rows); + break; + case REG_VT0: + s->vt0 = (uint32_t)val; + n = (s->vt0 & 0xfff) + 1; + ftlcdc200_resize(s, s->cols, n); + break; + case REG_VT1: + s->vt1 = (uint32_t)val; + break; + case REG_POL: + s->pol = (uint32_t)val; + break; + case REG_SPPR: + s->sppr = (uint32_t)val; + break; + default: + break; + } +} + +static const MemoryRegionOps ftlcdc200_ops = { + .read = ftlcdc200_mem_read, + .write = ftlcdc200_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ftlcdc200_reset(DeviceState *ds) +{ + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + Ftlcdc200State *s = FTLCDC200(FROM_SYSBUS(Ftlcdc200State, busdev)); + + s->fer = 0; + s->ppr = 0; + s->ier = 0; + s->isr = 0; + s->sppr = 0; + s->fb[0] = 0; + s->fb[1] = 0; + s->fb[2] = 0; + s->fb[3] = 0; + s->ht = 0; + s->vt0 = 0; + s->vt1 = 0; + s->pol = 0; + s->cols = 0; + s->rows = 0; + s->bpp = 0; + s->invalidate = 1; + + /* Make sure we redraw, and at the right size */ + ftlcdc200_invalidate_display(s); +} + +static int ftlcdc200_init(SysBusDevice *dev) +{ + Ftlcdc200State *s = FTLCDC200(FROM_SYSBUS(Ftlcdc200State, dev)); + + memory_region_init_io(&s->iomem, + &ftlcdc200_ops, + s, + TYPE_FTLCDC200, + 0x10000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq[IRQ_ALL]); + sysbus_init_irq(dev, &s->irq[IRQ_VSTATUS]); + sysbus_init_irq(dev, &s->irq[IRQ_BASEUPT]); + sysbus_init_irq(dev, &s->irq[IRQ_FIFOUR]); + sysbus_init_irq(dev, &s->irq[IRQ_BUSERR]); + s->ds = graphic_console_init(ftlcdc200_update_display, + ftlcdc200_invalidate_display, + NULL, NULL, s); + return 0; +} + +static void ftlcdc200_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = ftlcdc200_init; + dc->reset = ftlcdc200_reset; + dc->vmsd = &vmstate_ftlcdc200; + dc->no_user = 1; +} + +static const TypeInfo ftlcdc200_info = { + .name = TYPE_FTLCDC200, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ftlcdc200State), + .class_init = ftlcdc200_class_init, +}; + +static void ftlcdc200_register_types(void) +{ + type_register_static(&ftlcdc200_info); +} + +type_init(ftlcdc200_register_types) diff --git a/hw/arm/ftlcdc200.h b/hw/arm/ftlcdc200.h new file mode 100644 index 0000000..46d0df8 --- /dev/null +++ b/hw/arm/ftlcdc200.h @@ -0,0 +1,110 @@ +/* + * Faraday FTLCDC200 Color LCD Controller + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under the GNU LGPL + */ + +#ifndef HW_ARM_FTLCDC2XX_H +#define HW_ARM_FTLCDC2XX_H + +/* HW Registers */ + +#define REG_FER 0x00000 +#define REG_PPR 0x00004 +#define REG_IER 0x00008 +#define REG_ISCR 0x0000C +#define REG_ISR 0x00010 +#define REG_FB0 0x00018 +#define REG_FB1 0x00024 +#define REG_FB2 0x00030 +#define REG_FB3 0x0003C + +#define REG_HT 0x00100 +#define REG_VT0 0x00104 +#define REG_VT1 0x00108 +#define REG_POL 0x0010C + +#define REG_SPPR 0x00200 +#define REG_CCIR 0x00204 + +/* LCD Function Enable Register */ +#define FER_EN (1 << 0) +#define FER_ON (1 << 1) +#define FER_YUV420 (3 << 2) +#define FER_YUV422 (2 << 2) +#define FER_YUV (1 << 3) + +/* LCD Panel Pixel Register */ +#define PPR_BPP_1 (0 << 0) +#define PPR_BPP_2 (1 << 0) +#define PPR_BPP_4 (2 << 0) +#define PPR_BPP_8 (3 << 0) +#define PPR_BPP_16 (4 << 0) +#define PPR_BPP_24 (5 << 0) +#define PPR_BPP_MASK (7 << 0) +#define PPR_PWROFF (1 << 3) +#define PPR_BGR (1 << 4) +#define PPR_ENDIAN_LBLP (0 << 5) +#define PPR_ENDIAN_BBBP (1 << 5) +#define PPR_ENDIAN_LBBP (2 << 5) +#define PPR_ENDIAN_MASK (3 << 5) +#define PPR_RGB1 (PPR_BPP_1) +#define PPR_RGB2 (PPR_BPP_2) +#define PPR_RGB4 (PPR_BPP_4) +#define PPR_RGB8 (PPR_BPP_8) +#define PPR_RGB12 (PPR_BPP_16 | (2 << 7)) +#define PPR_RGB16_555 (PPR_BPP_16 | (1 << 7)) +#define PPR_RGB16_565 (PPR_BPP_16 | (0 << 7)) +#define PPR_RGB24 (PPR_BPP_24) +#define PPR_RGB32 (PPR_BPP_24) +#define PPR_RGB_MASK (PPR_BPP_MASK | (3 << 7)) +#define PPR_VCOMP_VSYNC (0 << 9) +#define PPR_VCOMP_VBP (1 << 9) +#define PPR_VCOMP_VAIMG (2 << 9) +#define PPR_VCOMP_VFP (3 << 9) +#define PPR_VCOMP_MASK (3 << 9) + +/* LCD Interrupt Enable Register */ +#define IER_FIFOUR (1 << 0) +#define IER_NEXTFB (1 << 1) +#define IER_VCOMP (1 << 2) +#define IER_BUSERR (1 << 3) + +/* LCD Interrupt Status Register */ +#define ISR_FIFOUR (1 << 0) +#define ISR_NEXTFB (1 << 1) +#define ISR_VCOMP (1 << 2) +#define ISR_BUSERR (1 << 3) + +/* LCD Horizontal Timing Control Register */ +#define HT_HBP(x) ((((x) - 1) & 0xff) << 24) +#define HT_HFP(x) ((((x) - 1) & 0xff) << 16) +#define HT_HSYNC(x) ((((x) - 1) & 0xff) << 8) +#define HT_PL(x) (((x >> 4) - 1) & 0xff) + +/* LCD Vertical Timing Control Register 0 */ +#define VT0_VFP(x) (((x) & 0xff) << 24) +#define VT0_VSYNC(x) ((((x) - 1) & 0x3f) << 16) +#define VT0_LF(x) (((x) - 1) & 0xfff) + +/* LCD Polarity Control Register */ +#define POL_IVS (1 << 0) +#define POL_IHS (1 << 1) +#define POL_ICK (1 << 2) +#define POL_IDE (1 << 3) +#define POL_IPWR (1 << 4) +#define POL_DIV(x) ((((x) - 1) & 0x7f) << 8) + +/* LCD Serial Panel Pixel Register */ +#define SPPR_SERIAL (1 << 0) +#define SPPR_DELTA (1 << 1) +#define SPPR_CS_RGB (0 << 2) +#define SPPR_CS_BRG (1 << 2) +#define SPPR_CS_GBR (2 << 2) +#define SPPR_LSR (1 << 4) +#define SPPR_AUO052 (1 << 5) + +#endif /* __FTLCDC2XX_H__ */ diff --git a/hw/arm/ftlcdc200_template.h b/hw/arm/ftlcdc200_template.h new file mode 100644 index 0000000..f2785fc --- /dev/null +++ b/hw/arm/ftlcdc200_template.h @@ -0,0 +1,439 @@ +/* + * Faraday FTLCDC200 Color LCD Controller + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under the GNU LGPL + * + * Framebuffer format conversion routines. + */ + +#ifndef ORDER + +#if BITS == 8 +#define COPY_PIXEL(to, from) \ + do { *((to)++) = (from); } while (0) +#elif BITS == 15 || BITS == 16 +#define COPY_PIXEL(to, from) \ + do { \ + *(uint16_t *)to = from;\ + to += 2;\ + } while (0) +#elif BITS == 24 +#define COPY_PIXEL(to, from) \ + do {\ + *(to++) = from;\ + *(to++) = (from) >> 8;\ + *(to++) = (from) >> 16;\ + } while (0) +#elif BITS == 32 +#define COPY_PIXEL(to, from) \ + do {\ + *(uint32_t *)to = from;\ + to += 4;\ + } while (0) +#else +#error unknown bit depth +#endif + +#undef RGB +#define BORDER bgr +#define ORDER 0 +#include "ftlcdc200_template.h" +#define ORDER 1 +#include "ftlcdc200_template.h" +#define ORDER 2 +#include "ftlcdc200_template.h" +#undef BORDER +#define RGB +#define BORDER rgb +#define ORDER 0 +#include "ftlcdc200_template.h" +#define ORDER 1 +#include "ftlcdc200_template.h" +#define ORDER 2 +#include "ftlcdc200_template.h" +#undef BORDER + +static drawfn glue(ftlcdc200_draw_fn_, BITS)[48] = { + glue(ftlcdc200_draw_line1_lblp_bgr, BITS), + glue(ftlcdc200_draw_line2_lblp_bgr, BITS), + glue(ftlcdc200_draw_line4_lblp_bgr, BITS), + glue(ftlcdc200_draw_line8_lblp_bgr, BITS), + glue(ftlcdc200_draw_line16_555_lblp_bgr, BITS), + glue(ftlcdc200_draw_line32_lblp_bgr, BITS), + glue(ftlcdc200_draw_line16_lblp_bgr, BITS), + glue(ftlcdc200_draw_line12_lblp_bgr, BITS), + + glue(ftlcdc200_draw_line1_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line2_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line4_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line8_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line16_555_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line32_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line16_bbbp_bgr, BITS), + glue(ftlcdc200_draw_line12_bbbp_bgr, BITS), + + glue(ftlcdc200_draw_line1_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line2_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line4_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line8_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line16_555_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line32_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line16_lbbp_bgr, BITS), + glue(ftlcdc200_draw_line12_lbbp_bgr, BITS), + + glue(ftlcdc200_draw_line1_lblp_rgb, BITS), + glue(ftlcdc200_draw_line2_lblp_rgb, BITS), + glue(ftlcdc200_draw_line4_lblp_rgb, BITS), + glue(ftlcdc200_draw_line8_lblp_rgb, BITS), + glue(ftlcdc200_draw_line16_555_lblp_rgb, BITS), + glue(ftlcdc200_draw_line32_lblp_rgb, BITS), + glue(ftlcdc200_draw_line16_lblp_rgb, BITS), + glue(ftlcdc200_draw_line12_lblp_rgb, BITS), + + glue(ftlcdc200_draw_line1_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line2_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line4_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line8_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line16_555_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line32_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line16_bbbp_rgb, BITS), + glue(ftlcdc200_draw_line12_bbbp_rgb, BITS), + + glue(ftlcdc200_draw_line1_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line2_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line4_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line8_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line16_555_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line32_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line16_lbbp_rgb, BITS), + glue(ftlcdc200_draw_line12_lbbp_rgb, BITS), +}; + +#undef BITS +#undef COPY_PIXEL + +#else + +#if ORDER == 0 +#define NAME glue(glue(lblp_, BORDER), BITS) +#ifdef HOST_WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#elif ORDER == 1 +#define NAME glue(glue(bbbp_, BORDER), BITS) +#ifndef HOST_WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#else +#define SWAP_PIXELS 1 +#define NAME glue(glue(lbbp_, BORDER), BITS) +#ifdef HOST_WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#endif + +#define FN_2(x, y) FN(x, y) FN(x + 1, y) +#define FN_4(x, y) FN_2(x, y) FN_2(x + 2, y) +#define FN_8(y) FN_4(0, y) FN_4(4, y) + +static void glue(ftlcdc200_draw_line1_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t *palette = opaque; + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 7 - (x))) & 1]); +#else +#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) + y)) & 1]); +#endif +#ifdef SWAP_WORDS + FN_8(24) + FN_8(16) + FN_8(8) + FN_8(0) +#else + FN_8(0) + FN_8(8) + FN_8(16) + FN_8(24) +#endif +#undef FN + width -= 32; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line2_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t *palette = opaque; + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 6 - (x) * 2)) & 3]); +#else +#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) * 2 + y)) & 3]); +#endif +#ifdef SWAP_WORDS + FN_4(0, 24) + FN_4(0, 16) + FN_4(0, 8) + FN_4(0, 0) +#else + FN_4(0, 0) + FN_4(0, 8) + FN_4(0, 16) + FN_4(0, 24) +#endif +#undef FN + width -= 16; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line4_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t *palette = opaque; + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 4 - (x) * 4)) & 0xf]); +#else +#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) * 4 + y)) & 0xf]); +#endif +#ifdef SWAP_WORDS + FN_2(0, 24) + FN_2(0, 16) + FN_2(0, 8) + FN_2(0, 0) +#else + FN_2(0, 0) + FN_2(0, 8) + FN_2(0, 16) + FN_2(0, 24) +#endif +#undef FN + width -= 8; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line8_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t *palette = opaque; + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#define FN(x) COPY_PIXEL(d, palette[(data >> (x)) & 0xff]); +#ifdef SWAP_WORDS + FN(24) + FN(16) + FN(8) + FN(0) +#else + FN(0) + FN(8) + FN(16) + FN(24) +#endif +#undef FN + width -= 4; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line16_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif +#ifdef RGB +#define LSB r +#define MSB b +#else +#define LSB b +#define MSB r +#endif +#if 0 + LSB = data & 0x1f; + data >>= 5; + g = data & 0x3f; + data >>= 6; + MSB = data & 0x1f; + data >>= 5; +#else + LSB = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + MSB = (data & 0x1f) << 3; + data >>= 5; +#endif + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); + LSB = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + MSB = (data & 0x1f) << 3; + data >>= 5; + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); +#undef MSB +#undef LSB + width -= 2; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line32_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef RGB +#define LSB r +#define MSB b +#else +#define LSB b +#define MSB r +#endif +#ifndef SWAP_WORDS + LSB = data & 0xff; + g = (data >> 8) & 0xff; + MSB = (data >> 16) & 0xff; +#else + LSB = (data >> 24) & 0xff; + g = (data >> 16) & 0xff; + MSB = (data >> 8) & 0xff; +#endif + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); +#undef MSB +#undef LSB + width--; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line16_555_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + /* RGB 555 plus an intensity bit (which we ignore) */ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif +#ifdef RGB +#define LSB r +#define MSB b +#else +#define LSB b +#define MSB r +#endif + LSB = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x1f) << 3; + data >>= 5; + MSB = (data & 0x1f) << 3; + data >>= 5; + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); + LSB = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x1f) << 3; + data >>= 5; + MSB = (data & 0x1f) << 3; + data >>= 6; + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); +#undef MSB +#undef LSB + width -= 2; + src += 4; + } +} + +static void glue(ftlcdc200_draw_line12_, NAME)(void *opaque, + uint8_t *d, + const uint8_t *src, + int width, + int deststep) +{ + /* RGB 444 with 4 bits of zeroes at the top of each halfword */ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif +#ifdef RGB +#define LSB r +#define MSB b +#else +#define LSB b +#define MSB r +#endif + LSB = (data & 0xf) << 4; + data >>= 4; + g = (data & 0xf) << 4; + data >>= 4; + MSB = (data & 0xf) << 4; + data >>= 8; + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); + LSB = (data & 0xf) << 4; + data >>= 4; + g = (data & 0xf) << 4; + data >>= 4; + MSB = (data & 0xf) << 4; + data >>= 8; + COPY_PIXEL(d, glue(rgb_to_pixel, BITS)(r, g, b)); +#undef MSB +#undef LSB + width -= 2; + src += 4; + } +} + +#undef SWAP_PIXELS +#undef NAME +#undef SWAP_WORDS +#undef ORDER + +#endif