From: Kuo-Jung Su <dant...@faraday-tech.com> The FTLCDC200 is an LCD controller that is compliant with the AMBA 2.0. It is a reusable soft-IP block and provides all the necessary control signals for a variety of TFT/CSTN/STN LCD panels. It was designed for portable electronics, including Personal Digital Assistants (PDAs), smart phones, hand-helds, and portable color-screen game terminals.
Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- hw/ftlcdc200.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++ hw/ftlcdc200.h | 110 +++++++++++ hw/ftlcdc200_template.h | 439 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1054 insertions(+) create mode 100644 hw/ftlcdc200.c create mode 100644 hw/ftlcdc200.h create mode 100644 hw/ftlcdc200_template.h diff --git a/hw/ftlcdc200.c b/hw/ftlcdc200.c new file mode 100644 index 0000000..9ff7c6e --- /dev/null +++ b/hw/ftlcdc200.c @@ -0,0 +1,505 @@ +/* + * Faraday FTLCDC200 Color LCD Controller + * + * base is pl110.c + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su <dant...@faraday-tech.com> + * + * This code is licensed under the GNU LGPL + */ + +#include "sysbus.h" +#include "ui/console.h" +#include "framebuffer.h" +#include "ui/pixel_ops.h" + +#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 */ + +} ftlcdc200_state; + +#define FTLCDC200(obj) \ + OBJECT_CHECK(ftlcdc200_state, obj, TYPE_FTLCDC200) + +static const VMStateDescription vmstate_ftlcdc200 = { + .name = TYPE_FTLCDC200, + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(cols, ftlcdc200_state), + VMSTATE_INT32(rows, ftlcdc200_state), + VMSTATE_UINT32(bpp, ftlcdc200_state), + VMSTATE_INT32(invalidate, ftlcdc200_state), + VMSTATE_UINT32_ARRAY(palette, ftlcdc200_state, 256), + VMSTATE_UINT32_ARRAY(raw_palette, ftlcdc200_state, 128), + VMSTATE_UINT32(fer, ftlcdc200_state), + VMSTATE_UINT32(ppr, ftlcdc200_state), + VMSTATE_UINT32(ier, ftlcdc200_state), + VMSTATE_UINT32(isr, ftlcdc200_state), + VMSTATE_UINT32(sppr, ftlcdc200_state), + VMSTATE_UINT32_ARRAY(fb, ftlcdc200_state, 4), + VMSTATE_UINT32(ht, ftlcdc200_state), + VMSTATE_UINT32(vt0, ftlcdc200_state), + VMSTATE_UINT32(vt1, ftlcdc200_state), + VMSTATE_UINT32(pol, ftlcdc200_state), + 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(ftlcdc200_state *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(ftlcdc200_state *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) +{ + ftlcdc200_state *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: + fprintf(stderr, "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) +{ + ftlcdc200_state *s = FTLCDC200(opaque); + s->invalidate = 1; + if (ftlcdc200_enabled(s)) { + qemu_console_resize(s->ds, s->cols, s->rows); + } +} + +static void ftlcdc200_update_palette(ftlcdc200_state *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(ftlcdc200_state *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) +{ + ftlcdc200_state *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) +{ + ftlcdc200_state *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); + ftlcdc200_state *s = FTLCDC200(FROM_SYSBUS(ftlcdc200_state, 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) +{ + ftlcdc200_state *s = FTLCDC200(FROM_SYSBUS(ftlcdc200_state, 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(ftlcdc200_state), + .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..2d1714d --- /dev/null +++ b/hw/ftlcdc200.h @@ -0,0 +1,110 @@ +/* + * Faraday FTLCDC200 Color LCD Controller + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su <dant...@faraday-tech.com> + * + * This code is licensed under the GNU LGPL + */ + +#ifndef __FTLCDC2XX_H__ +#define __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/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 <dant...@faraday-tech.com> + * + * 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 -- 1.7.9.5