From patchwork Mon Mar 25 12:09:55 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: 230647 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 2D0AD2C00B1 for ; Mon, 25 Mar 2013 23:14:06 +1100 (EST) Received: from localhost ([::1]:59190 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UK6Hs-0007eH-AE for incoming@patchwork.ozlabs.org; Mon, 25 Mar 2013 08:14:04 -0400 Received: from eggs.gnu.org ([208.118.235.92]:37887) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UK6FR-0004P3-UY for qemu-devel@nongnu.org; Mon, 25 Mar 2013 08:11:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UK6FG-0006kE-P5 for qemu-devel@nongnu.org; Mon, 25 Mar 2013 08:11:33 -0400 Received: from mail-pb0-f47.google.com ([209.85.160.47]:36023) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UK6FG-0006kA-CI for qemu-devel@nongnu.org; Mon, 25 Mar 2013 08:11:22 -0400 Received: by mail-pb0-f47.google.com with SMTP id rp2so4138398pbb.20 for ; Mon, 25 Mar 2013 05:11:21 -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=0xefhRt1U8QQDep1KpHNSl9O7y+ATmu0JdPpT2BnQo4=; b=MaL3rdySQbKTvxEZXAC1Nf7llOp5KRcwXhZZZgUEbwlBDIV5jCoLwWi/xKV5GSJwAy H3TycTUyO+1ndJZUmrDbo0GZpvTrSo8hOSHMH6yrYyJvVmLvfmC+7SprUwGXH6sLRBfJ /pVaJDnEKpO55/XGGX8hL0pCbK3XjKpmj/mEmYEwkaokBWqMOLB6nCejrkU6INECbucY qi4DZu3i1c8k9VIi++V7/xk/rsNHV2gg9WcNDTEZqPel0dqAELXvgKz08JcQmjtsIScw 7bi0PiDXjocsMcj6M7GFWAGptyp8Ji2cTCmeH+KjhZSrxczJSDmgZJlMiY89o0bmVDg9 g6tg== X-Received: by 10.66.193.227 with SMTP id hr3mr17554530pac.45.1364213481703; Mon, 25 Mar 2013 05:11:21 -0700 (PDT) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id wi7sm14669199pac.9.2013.03.25.05.11.19 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Mon, 25 Mar 2013 05:11:21 -0700 (PDT) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Mon, 25 Mar 2013 20:09:55 +0800 Message-Id: <1364213400-10266-20-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1364213400-10266-1-git-send-email-dantesu@gmail.com> References: <1364213400-10266-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.160.47 Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v9 19/24] hw/arm: add 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 | 2 +- hw/arm/ftplat_a369soc.c | 10 + hw/ftlcdc200.c | 516 +++++++++++++++++++++++++++++++++++++++++++++++ hw/ftlcdc200.h | 110 ++++++++++ hw/ftlcdc200_template.h | 439 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1076 insertions(+), 1 deletion(-) create mode 100644 hw/ftlcdc200.c create mode 100644 hw/ftlcdc200.h create mode 100644 hw/ftlcdc200_template.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 62c823d..f6b947e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -26,7 +26,7 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-$(CONFIG_KVM) += kvm/arm_gic.o obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \ ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \ - ftssp010.o ftgmac100.o + ftssp010.o ftgmac100.o ftlcdc200.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c index 622b1db..cdc6d4a 100644 --- a/hw/arm/ftplat_a369soc.c +++ b/hw/arm/ftplat_a369soc.c @@ -228,6 +228,16 @@ static void a369soc_chip_init(FaradaySoCState *s) if (nb_nics > 0) { ftgmac100_init(&nd_table[0], 0x90c00000, s->pic[32]); } + + /* ftlcdc200 */ + sysbus_create_varargs("ftlcdc200", + 0x94a00000, + s->pic[0], /* ALL (NC in A369) */ + s->pic[25], /* VSTATUS */ + s->pic[24], /* Base Address Update */ + s->pic[23], /* FIFO Under-Run */ + s->pic[22], /* AHB Bus Error */ + NULL); } static void a369soc_realize(DeviceState *dev, Error **errp) diff --git a/hw/ftlcdc200.c b/hw/ftlcdc200.c new file mode 100644 index 0000000..2e25372 --- /dev/null +++ b/hw/ftlcdc200.c @@ -0,0 +1,516 @@ +/* + * 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 "hw/sysbus.h" +#include "hw/framebuffer.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" + +#include "qemu/bitops.h" +#include "hw/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; + QemuConsole *con; + + 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 & ISR_FIFOUR) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_BASEUPT], (mask & ISR_NEXTFB) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_VSTATUS], (mask & ISR_VCOMP) ? 1 : 0); + qemu_set_irq(s->irq[IRQ_BUSERR], (mask & ISR_BUSERR) ? 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); + DisplaySurface *surface = qemu_console_surface(s->con); + drawfn *fntable; + drawfn fn; + int dest_width; + int src_width; + int bpp_offset; + int first; + int last; + + if (!ftlcdc200_enabled(s)) { + return; + } + + switch (surface_bits_per_pixel(surface)) { + 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: + fprintf(stderr, "ftlcdc200: Bad color depth\n"); + abort(); + } + + bpp_offset = 0; + fn = fntable[s->bpp + bpp_offset]; + + 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(surface, + 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->con, 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->con, s->cols, s->rows); + } +} + +static void ftlcdc200_update_palette(Ftlcdc200State *s, int n) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i; + uint32_t raw; + unsigned int r, g, b; + + raw = s->raw_palette[n]; + n <<= 1; + for (i = 0; i < 2; i++) { + r = extract32(raw, 0, 5) << 3; + g = extract32(raw, 5, 5) << 3; + b = extract32(raw, 10, 5) << 3; + /* The I bit is ignored. */ + raw >>= 6; + switch (surface_bits_per_pixel(surface)) { + 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->con, 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; + case 0xA00 ... 0xBFC: /* palette. */ + return s->raw_palette[(addr - 0xA00) >> 2]; + /* we don't care */ + case REG_CCIR: + case 0x300 ... 0x310: /* image parameters */ + case 0x400 ... 0x40C: /* color management */ + case 0x600 ... 0x8FC: /* gamma correction */ + case 0xC00 ... 0xD3C: /* cstn parameters */ + case 0x1100 ... 0x112C: /* scalar control */ + case 0x2000 ... 0xBFFC: /* osd control */ + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "ftlcdc200: undefined memory access@%#" HWADDR_PRIx "\n", addr); + 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; + + switch (addr) { + case REG_FER: + s->fer = (uint32_t)val; + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->con, 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->con, 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; + case 0xA00 ... 0xBFC: /* palette. */ + n = (addr - 0xA00) >> 2; + s->raw_palette[(addr - 0xA00) >> 2] = val; + ftlcdc200_update_palette(s, n); + break; + case 0x300 ... 0x310: /* image parameters */ + case 0x400 ... 0x40C: /* color management */ + case 0x600 ... 0x8FC: /* gamma correction */ + case 0xC00 ... 0xD3C: /* cstn parameters */ + case 0x1100 ... 0x112C: /* scalar control */ + case 0x2000 ... 0xBFFC: /* osd control */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "ftlcdc200: undefined memory access@%#" HWADDR_PRIx "\n", addr); + break; + } +} + +static const MemoryRegionOps mmio_ops = { + .read = ftlcdc200_mem_read, + .write = ftlcdc200_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void ftlcdc200_reset(DeviceState *ds) +{ + Ftlcdc200State *s = FTLCDC200(SYS_BUS_DEVICE(ds)); + + 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; + + memset(s->raw_palette, 0, sizeof(s->raw_palette)); + + /* Make sure we redraw, and at the right size */ + ftlcdc200_invalidate_display(s); +} + +static void ftlcdc200_realize(DeviceState *dev, Error **errp) +{ + Ftlcdc200State *s = FTLCDC200(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, + &mmio_ops, + s, + TYPE_FTLCDC200, + 0x10000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq[IRQ_ALL]); + sysbus_init_irq(sbd, &s->irq[IRQ_VSTATUS]); + sysbus_init_irq(sbd, &s->irq[IRQ_BASEUPT]); + sysbus_init_irq(sbd, &s->irq[IRQ_FIFOUR]); + sysbus_init_irq(sbd, &s->irq[IRQ_BUSERR]); + s->con = graphic_console_init(ftlcdc200_update_display, + ftlcdc200_invalidate_display, + NULL, NULL, s); +} + +static void ftlcdc200_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = ftlcdc200_reset; + dc->vmsd = &vmstate_ftlcdc200; + dc->realize = ftlcdc200_realize; + 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/ftlcdc200.h b/hw/ftlcdc200.h new file mode 100644 index 0000000..53917e1 --- /dev/null +++ b/hw/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 /* LCD Function Enable Register */ +#define REG_PPR 0x00004 /* LCD Panel Pixel Register */ +#define REG_IER 0x00008 /* LCD Interrupt Enable Register */ +#define REG_ISCR 0x0000C /* LCD Interrupt Status Clear Register */ +#define REG_ISR 0x00010 /* LCD Interrupt Status Register */ +#define REG_FB0 0x00018 /* LCD Framebuffer Base Register 0 */ +#define REG_FB1 0x00024 /* LCD Framebuffer Base Register 1 */ +#define REG_FB2 0x00030 /* LCD Framebuffer Base Register 2 */ +#define REG_FB3 0x0003C /* LCD Framebuffer Base Register 3 */ + +#define REG_HT 0x00100 /* LCD Horizontal Timing Control Register */ +#define REG_VT0 0x00104 /* LCD Vertical Timing Control Register 0 */ +#define REG_VT1 0x00108 /* LCD Vertical Timing Control Register 1 */ +#define REG_POL 0x0010C /* LCD Polarity Control Register */ + +#define REG_SPPR 0x00200 /* LCD Serial Panel Pixel Register */ +#define REG_CCIR 0x00204 /* LCD CCIR565 Register */ + +/* LCD Function Enable Register */ +#define FER_EN (1 << 0) /* chip enabled */ +#define FER_ON (1 << 1) /* screen on */ +#define FER_YUV420 (3 << 2) +#define FER_YUV422 (2 << 2) +#define FER_YUV (1 << 3) /* 1:YUV, 0:RGB */ + +/* 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 /* HW_ARM_FTLCDC2XX_H */ diff --git a/hw/ftlcdc200_template.h b/hw/ftlcdc200_template.h new file mode 100644 index 0000000..f2785fc --- /dev/null +++ b/hw/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