osmocom-bb/src/target/firmware/fb/fb_s6b33b1x.c

195 lines
6.0 KiB
C

/* 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 Addressing 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);
delay_ms(1);
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;