summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Markgraf <steve@steve-m.de>2012-02-12 21:54:37 +0100
committerSteve Markgraf <steve@steve-m.de>2012-02-12 21:54:37 +0100
commit05e9b142c38321b45a23d6495d1a92b9cbae326b (patch)
treeade803cf25f58ac7443196085055d7cc05da444c
parent90ab06ce5c1259f0ff91a4ff9ef5b2d0050234cd (diff)
firmware/fb: add framebuffer driver for the Pirelli DP-L10 display
This commit adds a combined driver for the Sunplus SPCA-552E Multimedia Controller and the Samsung S6B33B1X LCD controller. I have to thank Stephan Meier, who helped me to reverse-engineer this beast during 28c3. Signed-off-by: Steve Markgraf <steve@steve-m.de>
-rw-r--r--src/target/firmware/Makefile3
-rw-r--r--src/target/firmware/board/pirelli_dpl10/init.c38
-rw-r--r--src/target/firmware/fb/fb_s6b33b1x.c193
3 files changed, 225 insertions, 9 deletions
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile
index ffdfe5d..3af2f67 100644
--- a/src/target/firmware/Makefile
+++ b/src/target/firmware/Makefile
@@ -15,6 +15,7 @@ FB_e88_OBJS=$(FB_OBJS) fb/fb_bw8.o fb/fb_st7558.o
FB_e99_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1783.o
FB_e86_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_td014.o
FB_j100_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1963.o
+FB_dpl10_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_s6b33b1x.o
FB_dummy_OBJS=$(FB_OBJS) fb/fb_dummy.o
# TI Calypso
@@ -28,7 +29,7 @@ gta0x_ENVIRONMENTS=highram
# Pirelli DP-L10
-pirelli_dpl10_OBJS=$(calypso_COMMON_OBJS) board/pirelli_dpl10/rffe_dpl10_triband.o board/pirelli_dpl10/init.o board/pirelli_dpl10/rf_power.o $(FB_dummy_OBJS)
+pirelli_dpl10_OBJS=$(calypso_COMMON_OBJS) board/pirelli_dpl10/rffe_dpl10_triband.o board/pirelli_dpl10/init.o board/pirelli_dpl10/rf_power.o $(FB_dpl10_OBJS)
pirelli_dpl10_ENVIRONMENTS=highram
# Compal Generic
diff --git a/src/target/firmware/board/pirelli_dpl10/init.c b/src/target/firmware/board/pirelli_dpl10/init.c
index 3f3145e..ef18403 100644
--- a/src/target/firmware/board/pirelli_dpl10/init.c
+++ b/src/target/firmware/board/pirelli_dpl10/init.c
@@ -1,7 +1,7 @@
/* Initialization for the Pirelli DP-L10 */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2011 by Steve Markgraf <steve@steve-m.de>
+ * (C) 2011-12 by Steve Markgraf <steve@steve-m.de>
*
* All Rights Reserved
*
@@ -49,7 +49,9 @@
#include <fb/framebuffer.h>
#define ARMIO_LATCH_OUT 0xfffe4802
+#define IO_CNTL_REG 0xfffe4804
#define ASIC_CONF_REG 0xfffef008
+#define IO_CONF_REG 0xfffef00a
static void board_io_init(void)
{
@@ -60,17 +62,36 @@ static void board_io_init(void)
reg |= ((1 << 12) | (1 << 7)); /* SCL / SDA */
/* TWL3025: Set SPI+RIF RX clock to rising edge */
reg |= (1 << 13) | (1 << 14);
+ reg &= ~(1 << 1);
writew(reg, ASIC_CONF_REG);
+
+ /* enable IO functionality */
+ reg = readw(IO_CONF_REG);
+ reg |= (1 << 9) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0);
+ writew(reg, IO_CONF_REG);
+
+ /* set IO directions */
+ reg = readw(IO_CNTL_REG);
+ reg &= ~((1 << 7) | (1 << 4) | (1 << 1));
+ writew(reg, IO_CNTL_REG);
+
+ /* reset display controller, disable bypass mode, set nCS4 to display */
+ reg = readw(ARMIO_LATCH_OUT);
+ reg &= ~(1 << 4);
+ writew(reg, ARMIO_LATCH_OUT);
+ reg &= ~(1 << 7);
+ reg |= (1 << 4) | (1 << 1);
+ writew(reg, ARMIO_LATCH_OUT);
}
void board_init(void)
{
/* Configure the memory interface */
- calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1);
- calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 4, CALYPSO_MEM_16bit, 1);
calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1);
- calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1);
- calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS3, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_CS4, 7, CALYPSO_MEM_16bit, 1);
calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1);
calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0);
@@ -111,10 +132,11 @@ void board_init(void)
/* Initialize system timers (uses hwtimer 2) */
timer_init();
- /* Initialize LCD driver (uses I2C) and backlight */
- fb_init();
+ /* Initialize LCD driver and backlight (0 is max, 255 min brightness) */
bl_mode_pwl(1);
- bl_level(0);
+ bl_level(50);
+
+ fb_init();
/* Initialize keypad driver */
keypad_init(1);
diff --git a/src/target/firmware/fb/fb_s6b33b1x.c b/src/target/firmware/fb/fb_s6b33b1x.c
new file mode 100644
index 0000000..3629a10
--- /dev/null
+++ b/src/target/firmware/fb/fb_s6b33b1x.c
@@ -0,0 +1,193 @@
+/* Framebuffer implementation - combined Sunplus SPCA552E and
+ * Samsung S6B33B1X LCD driver - as used in the Pirelli DP-L10 */
+
+/* (C) 2012 by Steve Markgraf <steve@steve-m.de>
+ *
+ * based on fb_ssd1783.c:
+ * (C) 2010 by Christian Vogel <vogelchr@vogel.cx>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <fb/framebuffer.h>
+#include <fb/fb_rgb332.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <delay.h>
+#include <memory.h>
+
+#define S6B33B1X_WIDTH 128
+#define S6B33B1X_HEIGHT 128
+#define LCD_INVIS_X_PIXELS 4
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define nCS4_ADDR 0x02800000
+
+static uint8_t fb_s6b33b1x_mem[S6B33B1X_WIDTH * S6B33B1X_HEIGHT];
+
+enum s6b33b1x_cmdflag { CMD, DATA, END };
+
+struct s6b33b1x_cmdlist {
+ enum s6b33b1x_cmdflag is_cmd:8; /* 1: is a command, 0: is data, 2: end marker! */
+ uint8_t data; /* 8 bit to send to LC display */
+} __attribute__((packed));
+
+static const struct s6b33b1x_cmdlist
+s6b33b1x_initdata[] = {
+ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */
+ { DATA, 0x00 }, /* DATA: everything off */
+ { CMD, 0x02 }, /* CMD Oscillation Mode Set */
+ { DATA, 0x00 }, /* DATA: oscillator off */
+ { CMD, 0x2c }, /* CMD Standby Mode off */
+ { CMD, 0x50 }, /* CMD Display off */
+ { CMD, 0x02 }, /* CMD Oscillation Mode Set */
+ { DATA, 0x01 }, /* DATA: oscillator on */
+ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */
+ { DATA, 0x01 }, /* DATA: Booster 1 on */
+ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */
+ { DATA, 0x09 }, /* DATA: Booster 1 on, OP-AMP on */
+ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */
+ { DATA, 0x0b }, /* DATA: Booster 1 + 2 on, OP-AMP on */
+ { CMD, 0x26 }, /* CMD DCDC and AMP ON/OFF set */
+ { DATA, 0x0f }, /* DATA: Booster 1 + 2 + 3 on, OP-AMP on */
+ { CMD, 0x20 }, /* CMD DC-DC Select */
+ { DATA, 0x01 }, /* DATA: step up x1.5 */
+ { CMD, 0x24 }, /* CMD DCDC Clock Division Set */
+ { DATA, 0x0a }, /* DATA: fPCK = fOSC/6 */
+ { CMD, 0x2a }, /* CMD Contrast Control */
+ { DATA, 0x2d }, /* DATA: default contrast */
+ { CMD, 0x30 }, /* CMD Adressing mode set */
+ { DATA, 0x0b }, /* DATA: 65536 color mode */
+ { CMD, 0x10 }, /* CMD Driver output mode set */
+ { DATA, 0x03 }, /* DATA: Display duty: 1/132 */
+ { CMD, 0x34 }, /* CMD N-line inversion set */
+ { DATA, 0x88 }, /* DATA: inversion on, one frame, every 8 blocks */
+ { CMD, 0x40 }, /* CMD Entry mode set */
+ { DATA, 0x00 }, /* DATA: Y address counter mode */
+ { CMD, 0x28 }, /* CMD Temperature Compensation set */
+ { DATA, 0x01 }, /* DATA: slope -0.05%/degC */
+ { CMD, 0x32 }, /* CMD ROW vector mode set */
+ { DATA, 0x01 }, /* DATA: every 2 subgroup */
+ { CMD, 0x51 }, /* CMD Display on */
+ { END, 0x00 }, /* MARKER: end of list */
+};
+
+static void fb_s6b33b1x_send_cmdlist(const struct s6b33b1x_cmdlist *p)
+{
+ while(p->is_cmd != END){
+ writew(p->data, nCS4_ADDR);
+ p++;
+ }
+}
+
+static void fb_spca_write(uint16_t addr, uint16_t val)
+{
+ writew(addr, nCS4_ADDR);
+ writew(val , nCS4_ADDR | 2);
+}
+
+static void fb_spca_init(void)
+{
+ uint16_t reg;
+
+ /* Initialize Sunplus SPCA552E Media Controller for bypass mode */
+ fb_spca_write(0x7e, 0x00); /* internal register access */
+ delay_ms(10);
+ fb_spca_write(0x7a, 0x00); /* keep CPU in reset state */
+ delay_ms(10);
+ fb_spca_write(0x7f, 0x00); /* select main page */
+ delay_ms(5);
+ fb_spca_write(0x72, 0x07); /* don't reshape timing, 16 bit mode */
+ fb_spca_write(0x14, 0x03);
+ fb_spca_write(0x7f, 0x00); /* select main page */
+ delay_ms(5);
+ fb_spca_write(0x06, 0xff);
+ fb_spca_write(0x7f, 0x09);
+ fb_spca_write(0x19, 0x08); /* backlight: 0x08 is on, 0x0c is off */
+ fb_spca_write(0x23, 0x18);
+
+ /* enable bypass mode */
+ reg = readw(ARMIO_LATCH_OUT);
+ reg |= (1 << 7);
+ writew(reg, ARMIO_LATCH_OUT);
+}
+
+static void fb_s6b33b1x_init(void)
+{
+ printf("%s: initializing LCD.\n",__FUNCTION__);
+
+ fb_spca_init();
+ fb_s6b33b1x_send_cmdlist(s6b33b1x_initdata);
+}
+
+static void fb_s6b33b1x_flush(void)
+{
+ int x,y;
+ uint8_t *p;
+ struct s6b33b1x_cmdlist prepare_disp_write_cmds[] = {
+ { CMD, 0x42 }, /* set column address */
+ { DATA, fb_rgb332->damage_x1 + LCD_INVIS_X_PIXELS },
+ { DATA, fb_rgb332->damage_x2 + LCD_INVIS_X_PIXELS - 1 },
+ { CMD, 0x43 }, /* set page address (Y) */
+ { DATA, fb_rgb332->damage_y1 },
+ { DATA, fb_rgb332->damage_y2 - 1 },
+ { END, 0x00 }
+ };
+
+ /* If everything's clean, just return */
+ if(fb_rgb332->damage_x1 == fb_rgb332->damage_x2 ||
+ fb_rgb332->damage_y1 == fb_rgb332->damage_y2) {
+ printf("%s: no damage\n",__FUNCTION__);
+ return;
+ }
+
+ fb_s6b33b1x_send_cmdlist(prepare_disp_write_cmds);
+
+ for(y=fb_rgb332->damage_y1;y<fb_rgb332->damage_y2;y++) {
+ p = & fb_rgb332->mem[y * framebuffer->width]; // start of line
+ p += fb_rgb332->damage_x1; // start of damage area
+
+ for(x=fb_rgb332->damage_x1; x<fb_rgb332->damage_x2; x++) {
+ uint16_t data = rgb332_to_565(*p++);
+ writew(data , nCS4_ADDR | 2);
+ }
+ }
+
+ fb_rgb332->damage_x1 = fb_rgb332->damage_x2 = 0;
+ fb_rgb332->damage_y1 = fb_rgb332->damage_y2 = 0;
+}
+
+static struct framebuffer fb_s6b33b1x_framebuffer = {
+ .name = "s6b33b1x",
+ .init = fb_s6b33b1x_init,
+ .clear = fb_rgb332_clear,
+ .boxto = fb_rgb332_boxto,
+ .lineto = fb_rgb332_lineto,
+ .putstr = fb_rgb332_putstr,
+ .flush = fb_s6b33b1x_flush,
+ .width = S6B33B1X_WIDTH,
+ .height = S6B33B1X_HEIGHT
+};
+
+static struct fb_rgb332 fb_s6b33b1x_rgb332 = {
+ .mem = fb_s6b33b1x_mem
+};
+
+struct framebuffer *framebuffer = &fb_s6b33b1x_framebuffer;
+struct fb_rgb332 *fb_rgb332 = &fb_s6b33b1x_rgb332;