diff options
author | Christian Vogel <vogelchr@vogel.cx> | 2010-10-11 20:53:25 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2012-02-03 23:59:35 +0100 |
commit | 080124619d964ccb0ebcb3fb9703423e79029b1f (patch) | |
tree | b5c0628111374a279bf1cc12366387e616cf4af1 | |
parent | ec097f532e5fd77cc627f8d18a0455bbeb694962 (diff) |
Calypso FB: C123 / ST7558 Black and White Support
-rw-r--r-- | src/target/firmware/board/compal_e88/init.c | 3 | ||||
-rw-r--r-- | src/target/firmware/fb/fb_bw8.c | 293 | ||||
-rw-r--r-- | src/target/firmware/fb/fb_st7558.c | 133 | ||||
-rw-r--r-- | src/target/firmware/include/fb/fb_bw8.h | 51 |
4 files changed, 480 insertions, 0 deletions
diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c index b2c360c1..fe3fd72b 100644 --- a/src/target/firmware/board/compal_e88/init.c +++ b/src/target/firmware/board/compal_e88/init.c @@ -44,6 +44,7 @@ #include <abb/twl3025.h> #include <rf/trf6151.h> +#include <fb/framebuffer.h> #define ARMIO_LATCH_OUT 0xfffe4802 #define IO_CNTL_REG 0xfffe4804 @@ -122,6 +123,8 @@ void board_init(void) timer_init(); /* Initialize LCD driver (uses I2C) and backlight */ + fb_init(); + bl_mode_pwl(1); bl_level(50); diff --git a/src/target/firmware/fb/fb_bw8.c b/src/target/firmware/fb/fb_bw8.c new file mode 100644 index 00000000..7365713a --- /dev/null +++ b/src/target/firmware/fb/fb_bw8.c @@ -0,0 +1,293 @@ +/* utility functions for a black-and-white framebuffer organized + as 8-vertically-stacked-pixels per byte. This matches the + ST7558 LC Display Controller used on the Motorola C123 */ + +/* (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_bw8.h> + +#include <stdio.h> // debugging + +void fb_bw8_clear(){ + int i,n; + + /* bytes to clear */ + n = (framebuffer->height+7)/8 * framebuffer->width; + for(i=0;i<n;i++) + fb_bw8->mem[i]=0; + + /* mark everything as dirty */ + fb_bw8->damage_x1 = 0; + fb_bw8->damage_x2 = framebuffer->width; + fb_bw8->damage_y1 = 0; + fb_bw8->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_bw8_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_bw8->damage_x2/y2 */ + y2++; + + /* maybe currently everything is clean? */ + if(fb_bw8->damage_x1 == fb_bw8->damage_x2 || + fb_bw8->damage_y1 == fb_bw8->damage_y2){ + fb_bw8->damage_x1 = x1; + fb_bw8->damage_y1 = y1; + fb_bw8->damage_x2 = x2; + fb_bw8->damage_y2 = y2; +/* + printf("%s: was clean! damage now %d %d %d %d\n", + __FUNCTION__,fb_bw8->damage_x1,fb_bw8->damage_y1, + fb_bw8->damage_x2,fb_bw8->damage_y2); +*/ + return; + } + + /* grow damage box */ + if(x1 < fb_bw8->damage_x1) + fb_bw8->damage_x1 = x1; + if(y1 < fb_bw8->damage_y1) + fb_bw8->damage_y1 = y1; + if(x2 > fb_bw8->damage_x2) + fb_bw8->damage_x2 = x2; + if(y2 > fb_bw8->damage_y2) + fb_bw8->damage_y2 = y2; +#if 0 + printf("%s: damage now %d %d %d %d\n", + __FUNCTION__,fb_bw8->damage_x1,fb_bw8->damage_y1, + fb_bw8->damage_x2,fb_bw8->damage_y2); +#endif +} + +static void fb_bw8_line(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2){ + fb_sanitize_box(&x1,&y1,&x2,&y2); + /* FIXME : this is currently unimplemented! */ +} + +void fb_bw8_lineto(uint16_t x,uint16_t y){ + fb_bw8_line(framebuffer->cursor_x,framebuffer->cursor_y,x,y); + framebuffer->cursor_x = x; + framebuffer->cursor_y = y; +} + +/* depending on color set (add to or_mask) or clear + (remove from and_mask) bit number bitnum */ +static void set_pixel(uint8_t *and_mask, + uint8_t *or_mask, + int bitnum, + uint32_t color +){ + uint16_t v; + if(color == FB_COLOR_TRANSP) + return; + if(color == FB_COLOR_WHITE) + *and_mask &= ~(1<<bitnum); + else + *or_mask |= 1<<bitnum; +} + +static void set_fg_pixel(uint8_t *and_mask,uint8_t *or_mask,int bitnum){ + set_pixel(and_mask,or_mask,bitnum,framebuffer->fg_color); +} + +static void set_bg_pixel(uint8_t *and_mask,uint8_t *or_mask,int bitnum){ + set_pixel(and_mask,or_mask,bitnum,framebuffer->bg_color); +} + +static void fb_bw8_box(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2) +{ + uint16_t y,w; + uint8_t *p; + + uint8_t and_mask,or_mask; // filling + uint8_t and_mask_side,or_mask_side; // left and right side + + fb_sanitize_box(&x1,&y1,&x2,&y2); + fb_bw8_update_damage(x1,y1,x2,y2); + + for(y=y1&0xfff8;y<=y2;y+=8){ + /* don't clear any pixels (white) */ + and_mask = and_mask_side = 0xff; + or_mask = or_mask_side = 0; + + for(w=0;w<8;w++){ /* check which pixels are affected */ + if(y+w >= y1 && y+w <= y2){ + set_bg_pixel(&and_mask,&or_mask,w); + set_fg_pixel(&and_mask_side,&or_mask_side,w); + } + + if(y+w == y1 || y+w == y2){ /* top and bottom line */ + set_fg_pixel(&and_mask,&or_mask,w); + } + } + + p = fb_bw8->mem + (y/8)*framebuffer->width + x1; + for(w=x1;w<=x2;w++){ + if(w == x1 || w == x2) + *p = (*p & and_mask_side)|or_mask_side; + else + *p = (*p & and_mask)|or_mask; + p++; + } + } +} + +/* draw box from cursor to (x,y) */ +void +fb_bw8_boxto(uint16_t x,uint16_t y){ + fb_bw8_box(framebuffer->cursor_x,framebuffer->cursor_y,x,y); + framebuffer->cursor_x = x; + framebuffer->cursor_y = y; +} + +/* this is the most ridiculous function ever, because it has to + fiddle with two braindead bitmaps at once, both being + organized differently */ + +/* draw text at current position, with current font and colours up + to a width of maxwidth pixels, return pixelwidth consumed */ + +int +fb_bw8_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 dy; // char_y = screen_y + dy + int w; // 0..7 while building bits per byte + 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 + int fb8_offs; // offset to current pixel in framebuffer + uint8_t and_mask,or_mask; // to draw on framebuffer + uint8_t *p; // 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 + + printf("%s: %d %d %d %d\n",__FUNCTION__,x1,y1,x2,y2); + + if(y1 < 0) // sanitize in case of overflow + y1 = 0; + if(y2 >= framebuffer->height) + y2 = framebuffer->height - 1; + + fb8_offs = x1 + (y1 & 0xfff8)/8; + + /* 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,'?'); + + byte_per_line = (fchr->bbox_w+7)/8;; + + /* character pixels, left to right */ + for(char_x=0; + char_x<fchr->width && char_x + framebuffer->cursor_x <= x2; + char_x++ + ){ + /* character pixels, top to bottom, in stripes + of 8 to match LCD RAM organisation */ + for(y=y1&0xfff8;y<=y2;y+=8){ // display lines + /* 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; + + fb8_offs = framebuffer->cursor_x + + char_x + (y/8)*framebuffer->width; + + and_mask = 0xff; + or_mask = 0x00; + + /* top to bottom inside of a 8bit column */ + for(w=0;w<8;w++,bitmap_y++){ + /* inside drawing area? */ + if(y+w < y1 || y+w > y2) + continue; + + /* 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)){ + set_fg_pixel(&and_mask, + &or_mask,w); + } else { // unset, or outside bitmap +outside_char_bitmap: + set_bg_pixel(&and_mask, + &or_mask,w); + } + } // for(w...) + /* adjust byte in framebuffer */ + p = fb_bw8->mem + fb8_offs; + *p = ( *p & and_mask ) | or_mask; + } // for(y...) + } // for(char_x...) + framebuffer->cursor_x += char_x; + } // str + + x2 = framebuffer->cursor_x; + fb_bw8_update_damage(x1,y1,x2,y2); + return x2-x1; +} + +int +fb_bw8_putchar(char c,int maxwidth){ + char tmp[2]; + tmp[0]=c; + tmp[1]=c; + return fb_bw8_putstr(tmp,maxwidth); +} diff --git a/src/target/firmware/fb/fb_st7558.c b/src/target/firmware/fb/fb_st7558.c new file mode 100644 index 00000000..f3318b15 --- /dev/null +++ b/src/target/firmware/fb/fb_st7558.c @@ -0,0 +1,133 @@ +/* Framebuffer implementation - ST1783 LCD driver for C123 */ +/* Based on st7558.c by 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_bw8.h> + +#include <i2c.h> +#include <calypso/clock.h> +#include <delay.h> + +#include <stdio.h> + +/* Sitronix ST7558 LCD Driver for OSMOCOM framebuffer interface. */ +/* (c) 2010 Christian Vogel <vogelchr@vogel.cx> */ +/* Based on the initial LCD driver by Harald Welte */ + +#define CONTROL_RS_CMD + +#define ST7558_SLAVE_ADDR 0x3c +#define ST7558_CMD_ADDR 0x00 +#define ST7558_RAM_ADDR 0x40 + +#define ST7558_WIDTH 96 /* in pixels */ +#define ST7558_HEIGHT 65 + +#define I2C_MAX_TRANSFER 16 + +static uint8_t +fb_st7558_mem[ST7558_WIDTH * ((ST7558_HEIGHT+7)/8)]; + +/* setup as initially proposed by Harald in st7558.c */ +static const uint8_t st7558_setup[] = { + 0x2e, /* ext. display control, set mirror X, set mirror Y*/ + 0x21, /* function set, enable extended instruction mode */ + 0x12, /* bias system BS[2,1,0] = [0,1,0] */ + 0xc0, /* set V_OP (V_OP6 = 1, V_OP[5:0] = 0) */ + 0x0b, /* booster stages PC1=1, PC0=1 */ + 0x20, /* function set, disable extended instruction mode */ + 0x11, /* V_LCD L/H select, PRS=1 */ + 0x00, /* NOP */ + 0x0c, /* normal video mode */ + 0x40, /* set X address to 0 */ + 0x80 /* set Y address to 0 */ +}; + + +static void +fb_st7558_init(){ + calypso_reset_set(RESET_EXT, 0); + i2c_init(0,0); + + /* initialize controller */ + i2c_write(ST7558_SLAVE_ADDR,ST7558_CMD_ADDR,1, + st7558_setup,sizeof(st7558_setup)); +} + +static void +fb_st7558_flush(){ + uint16_t x; + int page,chunksize,nbytes; + uint8_t *p; + uint8_t cmd[2]; + int i; + + if(fb_bw8->damage_y1 == fb_bw8->damage_y2 || + fb_bw8->damage_x1 == fb_bw8->damage_x2) + return; /* nothing to update */ + + /* update display in stripes of 8 rows, called "pages" */ + for(page=fb_bw8->damage_y1 >> 3;page <= fb_bw8->damage_y2>>3;page++){ + /* base offset in RAM framebuffer */ + x = fb_bw8->damage_x1; + nbytes = fb_bw8->damage_x2 - fb_bw8->damage_x1; + p = fb_bw8->mem + (page * framebuffer->width + x); + + /* i2c fifo can only handle a maximum of 16 bytes */ + while(nbytes){ + cmd[0]=0x40 | page; /* Set Y address of RAM. */ + cmd[1]=0x80 | x; + chunksize = nbytes > I2C_MAX_TRANSFER ? I2C_MAX_TRANSFER : nbytes; + + i2c_write(ST7558_SLAVE_ADDR,ST7558_CMD_ADDR,1,cmd,sizeof(cmd)); + i2c_write(ST7558_SLAVE_ADDR,ST7558_RAM_ADDR,1,p,chunksize); + + nbytes -= chunksize; + p+=I2C_MAX_TRANSFER; + x+=I2C_MAX_TRANSFER; + } + } + + /* mark current buffer as unmodified! */ + fb_bw8->damage_x1 = fb_bw8->damage_x2 = 0; + fb_bw8->damage_y1 = fb_bw8->damage_y2 = 0; +} + +static struct framebuffer fb_st7558_framebuffer = { + .name = "st7558", + .init = fb_st7558_init, + .clear = fb_bw8_clear, + .boxto = fb_bw8_boxto, + .lineto = fb_bw8_lineto, + .putstr = fb_bw8_putstr, + .flush = fb_st7558_flush, + .width = ST7558_WIDTH, + .height = ST7558_HEIGHT +}; + +static struct fb_bw8 fb_st7558_bw8 = { + .mem = fb_st7558_mem +}; + +struct framebuffer *framebuffer = &fb_st7558_framebuffer; +struct fb_bw8 *fb_bw8 = &fb_st7558_bw8; diff --git a/src/target/firmware/include/fb/fb_bw8.h b/src/target/firmware/include/fb/fb_bw8.h new file mode 100644 index 00000000..c77fa71f --- /dev/null +++ b/src/target/firmware/include/fb/fb_bw8.h @@ -0,0 +1,51 @@ +#ifndef FB_BW8_H +#define FB_BW8_H + +/* 8bit monochrome framebuffer, organized with 8 stacked pixels + per byte, backed by local memory. fb_bw8.c lists functions that + are common to simmilar organized displays. */ + +/* + Sketch of Memory Layout + Left Upper Corner of Display + + col0 col2 + col1 + +------------- + 1st row: | A0 B0 C0 + 2nd row: | A1 B1 C1 + ... + 7th row: | A6 B6 C6 + 8th row: | A7 B7 C7 + 9th row: | Q0 R0 S0 + 10th row: | Q1 R1 S1 ... + ... + + Backing store (and internal display memory?) looks like... + + uint8_t mem[] = { A, B, C, .... Q, R, S, ... } + + We work on a in-memory copy of the framebuffer and only + update the physical display on demand. The damage window + has two corners, left upper inclusive x1,y1 and right + lower x2,y2 exclusive. So dirty pixels are defined to + be x1 <= x_pixel < x2 and y1 <= y_pixel < y2. +*/ + +/* data specific to a bw8-type framebuffer as described above */ + +struct fb_bw8 { + uint8_t *mem; /* set to backingstore memory */ + uint16_t damage_x1,damage_y1; /* current damage window, ul */ + uint16_t damage_x2,damage_y2; /* current damage window, lr */ +}; + +extern struct fb_bw8 *fb_bw8; /* symbol defined by the specific LCD driver */ + +extern void fb_bw8_clear(); +extern void fb_bw8_boxto(uint16_t x,uint16_t y); /* draw a box from cursor to x,y */ +extern void fb_bw8_lineto(uint16_t x,uint16_t y); /* draw a line from cursor to x,y */ + +extern int fb_bw8_putstr(char *str,int maxwidth); + +#endif |