diff options
author | Christian Vogel <vogelchr@vogel.cx> | 2010-10-11 20:54:57 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2012-02-03 23:59:35 +0100 |
commit | df4218c8fd6d4a20c8840fd6246e41c58172bc09 (patch) | |
tree | aff0c481d77ab8a070b847fcbdc4a26c63fce0a3 /src/target/firmware/fb | |
parent | 080124619d964ccb0ebcb3fb9703423e79029b1f (diff) |
Calypso FB: C155 / SSD1783 Color Support
Diffstat (limited to 'src/target/firmware/fb')
-rw-r--r-- | src/target/firmware/fb/fb_rgb332.c | 283 | ||||
-rw-r--r-- | src/target/firmware/fb/fb_ssd1783.c | 194 |
2 files changed, 477 insertions, 0 deletions
diff --git a/src/target/firmware/fb/fb_rgb332.c b/src/target/firmware/fb/fb_rgb332.c new file mode 100644 index 00000000..223e792d --- /dev/null +++ b/src/target/firmware/fb/fb_rgb332.c @@ -0,0 +1,283 @@ +/* utility functions for a color framebuffer organized + as one pixel per byte, with bits mapped as RRRGGGBB. + This matches the SSD1783 LC Display Controller used + on the Motorola C155 */ + +/* (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 <stdio.h> +#include <stdlib.h> + +void +fb_rgb332_clear(){ + int i,n; + + /* bytes to clear */ + n = framebuffer->height * framebuffer->width; + for(i=0;i<n;i++) + fb_rgb332->mem[i]=0xff; /* white */ + + /* mark everything as dirty */ + fb_rgb332->damage_x1 = 0; + fb_rgb332->damage_x2 = framebuffer->width; + fb_rgb332->damage_y1 = 0; + fb_rgb332->damage_y2 = framebuffer->height; +} + +/* update damage rectangle to include the area + x1,y1 (upper left) to x2,y2 (lower right) + Note that all pixels *including* x1y2 and x2y2 are + marked as dirty */ +static void +fb_rgb332_update_damage( + uint16_t x1,uint16_t y1, /* left upper corner (inclusive) */ + uint16_t x2,uint16_t y2 /* right lower corner (inclusive) */ +){ + fb_sanitize_box(&x1,&y1,&x2,&y2); + + x2++; /* see definition of fb_rgb332->damage_x2/y2 */ + y2++; + + /* maybe currently everything is clean? */ + if(fb_rgb332->damage_x1 == fb_rgb332->damage_x2 || + fb_rgb332->damage_y1 == fb_rgb332->damage_y2 + ){ + fb_rgb332->damage_x1 = x1; + fb_rgb332->damage_y1 = y1; + fb_rgb332->damage_x2 = x2; + fb_rgb332->damage_y2 = y2; + return; + } + + /* grow damage box */ + if(x1 < fb_rgb332->damage_x1) + fb_rgb332->damage_x1 = x1; + if(y1 < fb_rgb332->damage_y1) + fb_rgb332->damage_y1 = y1; + if(x2 > fb_rgb332->damage_x2) + fb_rgb332->damage_x2 = x2; + if(y2 > fb_rgb332->damage_y2) + fb_rgb332->damage_y2 = y2; +#if 0 + printf("%s: damage now %d %d %d %d\n", + __FUNCTION__,fb_rgb332->damage_x1,fb_rgb332->damage_y1, + fb_rgb332->damage_x2,fb_rgb332->damage_y2); +#endif +} + +/* we trust gcc to move this expensive bitshifting out of + the loops in the drawing funtcions */ +static uint8_t rgb_to_pixel(uint32_t color){ + uint8_t ret; + ret = (FB_COLOR_TO_R(color) & 0xe0); /* 765 = RRR */ + ret |= (FB_COLOR_TO_G(color) & 0xe0) >> 2; /* 432 = GGG */ + ret |= (FB_COLOR_TO_B(color) & 0xc0) >> 6; /* 10 = BB */ + return ret; +} + +static void set_pix(uint8_t *pixel,uint32_t color){ + if(color == FB_COLOR_TRANSP) + return; + *pixel = rgb_to_pixel(color); +} + +static void set_fg(uint8_t *pixel){ + set_pix(pixel,framebuffer->fg_color); +} + +static void set_bg(uint8_t *pixel){ + set_pix(pixel,framebuffer->bg_color); +} + +void fb_rgb332_boxto(uint16_t x2,uint16_t y2) +{ + uint16_t x1 = framebuffer->cursor_x; + uint16_t y1 = framebuffer->cursor_y; + int x,y; + uint8_t *p; + + framebuffer->cursor_x = x2; + framebuffer->cursor_y = y2; + + fb_sanitize_box(&x1,&y1,&x2,&y2); + fb_rgb332_update_damage(x1,y1,x2,y2); + + for(y=y1; y<=y2; y++){ + p = & fb_rgb332->mem[x1 + framebuffer->width * y]; + for(x=x1;x<=x2;x++){ + set_bg(p); + if(y==y1 || y==y2 || x==x1 || x==x2) /* border */ + set_fg(p); + p++; + } + } +} + +/* draw a line like Brensenham did... (roughly) */ +void fb_rgb332_lineto(uint16_t x2,uint16_t y2){ + uint8_t *p,pixel; /* framebuffer pointer */ + int delta_regular; /* framebuffer offset per step */ + int delta_step; /* " */ + + uint16_t x1 = framebuffer->cursor_x; /* start */ + uint16_t y1 = framebuffer->cursor_y; + + int t,tmax; /* counter for steps */ + int err_inc,err_accu=0; /* error delta and accumulator for */ + /* Brensenham's algorhithm */ + + fb_limit_fb_range(&x1,&y1); + fb_limit_fb_range(&x2,&y2); + fb_rgb332_update_damage(x1,y1,x2,y2); + + framebuffer->cursor_x = x2; /* end pixel */ + framebuffer->cursor_y = y2; + + /* pointer to first pixel, pixel value in FB memory */ + p = fb_rgb332->mem + framebuffer->width * y1 + x1; + pixel = rgb_to_pixel(framebuffer->fg_color); + + if(abs(x2-x1) >= abs(y2-y1)){ /* shallow line */ + /* set pointer deltas for directions */ + delta_regular = 1; /* X */ + if(x2 < x1) + delta_regular = -delta_regular; + delta_step = framebuffer->width; /* Y */ + if(y2 < y1) + delta_step = -delta_step; + tmax = abs(x2-x1); + err_inc = abs(y2-y1); + } else { /* steep line */ + delta_regular = framebuffer->width; /* Y */ + if(y2 < y1) + delta_regular = -delta_regular; + delta_step = 1; /* X */ + if(x2 < x1) + delta_step = -1; + tmax = abs(y2-y1); + err_inc = abs(x2-y1); + } + +#if 0 + printf("%s: (%d,%d) -> (%d,%d) step=%d regular=%d err_inc=%d tmax=%d\n", + __FUNCTION__,x1,y1,x2,y2,delta_step,delta_regular,err_inc,tmax); +#endif + + for(t=0;t<=tmax;t++){ + *p = pixel; + err_accu += err_inc; + if(err_accu >= tmax){ + p += delta_step; + err_accu -= tmax; + } + p += delta_regular; + } +} + +int fb_rgb332_putstr(char *str,int maxwidth){ + const struct fb_font *font = fb_fonts[framebuffer->font]; + const struct fb_char *fchr; + + int x1,y1,x2,y2; // will become bounding box + int y; // coordinates in display + int char_x,char_y; // coordinates in font character + int bitmap_x,bitmap_y; // coordinates in character's bitmap + int byte_per_line; // depending on character width in font + int bitmap_offs,bitmap_bit; // offset inside bitmap, bit number of pixel + uint8_t *p,fgpixel,bgpixel; // pointer into framebuffer memory + + x1 = framebuffer->cursor_x; // first col (incl!) + x2 = x1 + maxwidth - 1; // last col (incl!) + if(x2 >= framebuffer->width) + x2 = framebuffer->width - 1; + + y1 = framebuffer->cursor_y - font->ascent + 1; // first row + y2 = y1 + font->height - 1; // last row + + fgpixel = rgb_to_pixel(framebuffer->fg_color); + bgpixel = rgb_to_pixel(framebuffer->bg_color); + + if(y1 < 0) // sanitize in case of overflow + y1 = 0; + if(y2 >= framebuffer->height) + y2 = framebuffer->height - 1; + + /* iterate over all characters */ + for(;*str && framebuffer->cursor_x <= x2;str++){ + fchr = fb_font_get_char(font,*str); + if(!fchr) /* FIXME: Does '?' exist in every font? */ + fchr = fb_font_get_char(font,'?'); + if(!fchr) + return 0; + byte_per_line = (fchr->bbox_w+7)/8; + + for(y=y1;y<=y2;y++){ + p=fb_rgb332->mem+y*framebuffer->width; + p+=framebuffer->cursor_x; + + for(char_x=0; + char_x<fchr->width && + char_x+framebuffer->cursor_x <= x2; + char_x++ + ){ + /* bitmap coordinates, X= left to right */ + bitmap_x = char_x - fchr->bbox_x; + /* character coords. Y increases from + cursor upwards */ + char_y = framebuffer->cursor_y-y; + /* bitmap index = height-(bitmap coords)-1 */ + bitmap_y = fchr->bbox_h - + (char_y - fchr->bbox_y) - 1; + + /* outside pixel data of this + character? */ + if(bitmap_x < 0 || + bitmap_x >= fchr->bbox_w || + bitmap_y < 0 || + bitmap_y >= fchr->bbox_h + ) + goto outside_char_bitmap; + + /* check bit in pixel data for + this character */ + bitmap_offs=bitmap_x/8+bitmap_y*byte_per_line; + bitmap_bit=7-(bitmap_x%8); + + /* bit is set */ + if(fchr->data[bitmap_offs]&(1<<bitmap_bit)){ + *p = fgpixel; + } else { // unset, or outside bitmap +outside_char_bitmap: + *p = bgpixel; + } + p++; + } // for(x...) + } // for(char_x...) + framebuffer->cursor_x += char_x; + } // str + + x2 = framebuffer->cursor_x; + fb_rgb332_update_damage(x1,y1,x2,y2); + return x2-x1; +} + diff --git a/src/target/firmware/fb/fb_ssd1783.c b/src/target/firmware/fb/fb_ssd1783.c new file mode 100644 index 00000000..19aa86db --- /dev/null +++ b/src/target/firmware/fb/fb_ssd1783.c @@ -0,0 +1,194 @@ +/* Framebuffer implementation - SSD1783 LCD driver for C155 */ +/* Based on ssd1783.c by Steve Markgraf and Harald Welte */ + +/* (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 <uwire.h> +#include <calypso/clock.h> + +#define SSD1783_WIDTH 98 +#define SSD1783_HEIGHT 67 +#define SSD1783_UWIRE_BITLEN 9 +#define SSD1783_DEV_ID 0 + +#define LCD_TOP_FREE_ROWS 3 +#define LCD_LEFT_FREE_COLS 0 +#define PIXEL_BYTES 3 +#define FONT_HEIGHT 8 +#define FONT_WIDTH 8 + +static uint8_t fb_ssd1783_mem[SSD1783_WIDTH * SSD1783_HEIGHT]; + +enum ssd1783_cmdflag { CMD, DATA, END }; + +struct ssd1783_cmdlist { + enum ssd1783_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 ssd1783_cmdlist +ssd1783_initdata[] = { + { CMD, 0xD1 }, /* CMD set internal oscillator on */ + { CMD, 0x94 }, /* CMD leave sleep mode */ + { CMD, 0xbb }, /* CMD Set COM Output Scan Direction: */ + { DATA, 0x01 }, /* DATA: 01: COM0-79, then COM159-80 */ +/* -------- DIFFERENT FROM ORIGINAL CODE: -------------- */ +/* we use 8bit per pixel packed RGB 332 */ + { CMD, 0xbc }, /* CMD Set Data Output Scan Direction */ + { DATA, 0x00 }, /* DATA: column scan, normal rotation, normal display */ + { DATA, 0x00 }, /* DATA: RGB color arrangement R G B R G B ... */ +/*-->*/ { DATA, 0x01 }, /* DATA: 8 bit per pixel mode MSB <RRRGGGBB> LSB */ +/* --------- /DIFFERENT ---------- */ + { CMD, 0xce }, /* CMD Set 256 Color Look Up Table LUT */ + { DATA, 0x00 }, /* DATA: R[000], G[000], B[00] 3-bit R and G have */ + { DATA, 0x09 }, /* DATA: R[001], G[001] eight levels defined */ + { DATA, 0x12 }, /* DATA: R[010], G[010] of which the first and */ + { DATA, 0x1b }, /* DATA: R[011], G[011] the last are shared */ + { DATA, 0x24 }, /* DATA: R[100], G[100] by 2-bit blue */ + { DATA, 0x2d }, /* DATA: R[101], G[101] The intermediate two */ + { DATA, 0x36 }, /* DATA: R[110], G[110] steps are defined */ + { DATA, 0x3f }, /* DATA: R[111], G[111], B[11] separately */ + { DATA, 0x12 }, /* DATA: B[01] */ + { DATA, 0x24 }, /* DATA: B[10] */ + { CMD, 0xca }, /* CMD Set Display Control - Driver Duty Selection */ + { DATA, 0xff }, // can't find description of the values in the original + { DATA, 0x10 }, // display/ssd1783.c in my datasheet :-( + { DATA, 0x01 }, // + { CMD, 0xab }, /* CMD Set Scroll Start */ + { DATA, 0x00 }, /* DATA: Starting address at block 0 */ + { CMD, 0x20 }, /* CMD Set power control register */ + { DATA, 0x0b }, /* DATA: booster 6x, reference gen. & int regulator */ + { CMD, 0x81 }, /* CMD Contrast Lvl & Int. Regul. Resistor Ratio */ + { DATA, 0x29 }, /* DATA: contrast = 0x29 */ + { DATA, 0x05 }, /* DATA: 0x05 = 0b101 -> 1+R2/R1 = 11.37 */ + { CMD, 0xa7 }, /* CMD Invert Display */ + { CMD, 0x82 }, /* CMD Set Temperature Compensation Coefficient */ + { DATA, 0x00 }, /* DATA: Gradient is -0.10 % / degC */ + { CMD, 0xfb }, /* CMD Set Biasing Ratio */ + { DATA, 0x03 }, /* DATA: 1/10 bias */ + { CMD, 0xf2 }, /* CMD Set Frame Frequency and N-line inversion */ + { DATA, 0x08 }, /* DATA: 75 Hz (POR) */ + { DATA, 0x06 }, /* DATA: n-line inversion: 6 lines */ + { CMD, 0xf7 }, /* CMD Select PWM/FRC Select Full Col./8col mode */ + { DATA, 0x28 }, /* DATA: always 0x28 */ + { DATA, 0x8c }, /* DATA: 4bit PWM + 2 bit FRC */ + { DATA, 0x05 }, /* DATA: full color mode */ + { CMD, 0xaf }, /* CMD Display On */ + { END, 0x00 }, /* MARKER: end of list */ +}; + +static void +fb_ssd1783_send_cmdlist(const struct ssd1783_cmdlist *p){ + int i=0; + while(p->is_cmd != END){ + uint16_t sendcmd = p->data; + if(p->is_cmd == DATA) + sendcmd |= 0x0100; /* 9th bit is cmd/data flag */ + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &sendcmd, NULL); + p++; + i++; + } +} + +static void +fb_ssd1783_init(void){ + printf("%s: initializing LCD.\n",__FUNCTION__); + calypso_reset_set(RESET_EXT, 0); + delay_ms(5); + uwire_init(); + delay_ms(5); + fb_ssd1783_send_cmdlist(ssd1783_initdata); +} + +/* somehow the palette is messed up, RRR seems to have the + bits reversed! R0 R1 R2 G G G B B ---> R2 R1 R0 G G G B B */ +static uint8_t fix_rrr(uint8_t v){ + return (v & 0x5f) | (v & 0x80) >> 2 | (v & 0x20) << 2; +} + +static void +fb_ssd1783_flush(void){ + int x,y; + uint8_t *p; + struct ssd1783_cmdlist prepare_disp_write_cmds[] = { + { CMD, 0x15 }, /* set column address */ + { DATA, fb_rgb332->damage_x1 }, + { DATA, fb_rgb332->damage_x2-1 }, + { CMD, 0x75 }, /* set page address (Y) */ + { DATA, fb_rgb332->damage_y1 }, + { DATA, fb_rgb332->damage_y2-1 }, + { CMD, 0x5c }, /* enter write display ram mode */ + { END, 0x00 } + }; + struct ssd1783_cmdlist nop[] = { + { CMD, 0x25 }, // NOP command + { 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_ssd1783_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 = 0x0100 | fix_rrr(*p++); // dummy data + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, + &data, NULL); + } + } + fb_ssd1783_send_cmdlist(nop); + + fb_rgb332->damage_x1 = fb_rgb332->damage_x2 = 0; + fb_rgb332->damage_y1 = fb_rgb332->damage_y2 = 0; +} + +static struct framebuffer fb_ssd1783_framebuffer = { + .name = "ssd1783", + .init = fb_ssd1783_init, + .clear = fb_rgb332_clear, + .boxto = fb_rgb332_boxto, + .lineto = fb_rgb332_lineto, + .putstr = fb_rgb332_putstr, + .flush = fb_ssd1783_flush, + .width = SSD1783_WIDTH, + .height = SSD1783_HEIGHT +}; + +static struct fb_rgb332 fb_ssd1783_rgb332 = { + .mem = fb_ssd1783_mem +}; + +struct framebuffer *framebuffer = &fb_ssd1783_framebuffer; +struct fb_rgb332 *fb_rgb332 = &fb_ssd1783_rgb332; |