diff options
author | Steve Markgraf <steve@steve-m.de> | 2012-02-12 21:54:37 +0100 |
---|---|---|
committer | Steve Markgraf <steve@steve-m.de> | 2012-02-12 21:54:37 +0100 |
commit | 05e9b142c38321b45a23d6495d1a92b9cbae326b (patch) | |
tree | ade803cf25f58ac7443196085055d7cc05da444c /src/target/firmware/fb | |
parent | 90ab06ce5c1259f0ff91a4ff9ef5b2d0050234cd (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>
Diffstat (limited to 'src/target/firmware/fb')
-rw-r--r-- | src/target/firmware/fb/fb_s6b33b1x.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/target/firmware/fb/fb_s6b33b1x.c b/src/target/firmware/fb/fb_s6b33b1x.c new file mode 100644 index 00000000..3629a101 --- /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; |