Meizu M6SP: initial LCD driver (compiles but is untested)

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22500 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Bertrik Sikken 2009-08-24 17:37:53 +00:00
parent 1747a33fcc
commit 400cd026f3

View file

@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/ * \/ \/ \/ \/ \/
* $Id$ * $Id$
* *
* Copyright (C) 2002 by Alan Korr * Copyright (C) 2009 Bertrik Sikken
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -18,118 +18,430 @@
* KIND, either express or implied. * KIND, either express or implied.
* *
****************************************************************************/ ****************************************************************************/
#include <inttypes.h>
#include "config.h" #include "config.h"
#include "s5l8700.h"
#include "hwcompat.h"
#include "kernel.h"
#include "lcd.h" #include "lcd.h"
#include "system.h"
#include "cpu.h"
/*** definitions ***/ /* LCD driver for the Meizu M6 SP using the CLCD controller in the S5L8700
The Meizu M6 SP can have two different LCDs, the S6D0139 and another
(yet unknown) type, the exact type is detected at run-time.
Open issues:
* untested on actual hardware
* use 16-bit pixel format, currently pixels are converted to a 32-bit pixel
format in lcd_update_rect, that is not natively supported yet in Rockbox.
*/
/* LCD SPI connections */
#define LCD_SPI_SSn (1<<1) /* on PDAT7 */
#define LCD_SPI_MISO (1<<2) /* on PDAT3 */
#define LCD_SPI_MOSI (1<<6) /* on PDAT3 */
#define LCD_SPI_SCLK (1<<7) /* on PDAT3 */
#define LCD_TYPE1_ID 0x139 /* id for LCD type S6D0139 */
static int lcd_type = 0;
/* local frame buffer, keeps pixels in 32-bit words in format 0x00RRGGBB */
static uint32_t lcd_local_fb[LCD_HEIGHT][LCD_WIDTH];
/** globals **/ /* simple and crude delay */
static void lcd_delay(int count)
static int xoffset; /* needed for flip */
/*** hardware configuration ***/
int lcd_default_contrast(void)
{ {
return 0x1f; volatile int i;
for (i = 0; i < count; i++);
} }
void lcd_set_contrast(int val) /* write 'data_out' of length 'bits' over SPI and return received data */
static unsigned int lcd_spi_transfer(int bits, unsigned int data_out)
{ {
} unsigned int data_in = 0;
void lcd_set_invert_display(bool yesno) /* SSn active */
{ PDAT7 &= ~LCD_SPI_SSn;
} lcd_delay(10);
/* turn the display upside down (call lcd_update() afterwards) */ /* send and receive data */
void lcd_set_flip(bool yesno) while (bits--) {
{ /* CLK low */
/* TODO: flip mode isn't working. The commands in the else part of PDAT3 &= ~LCD_SPI_SCLK;
this function are how the original firmware inits the LCD */
if (yesno) /* set MOSI */
{ if (data_out & (1 << bits)) {
xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 128 we have */ PDAT3 |= LCD_SPI_MOSI;
}
else {
PDAT3 &= ~LCD_SPI_MOSI;
}
/* delay */
lcd_delay(10);
/* sample MISO */
data_in <<= 1;
if (PDAT3 & LCD_SPI_MISO) {
data_in |= 1;
}
/* CLK high */
PDAT3 |= LCD_SPI_SCLK;
/* delay */
lcd_delay(10);
} }
else
{ /* SSn inactive */
xoffset = 0; PDAT7 |= LCD_SPI_SSn;
lcd_delay(10);
return data_in;
}
/* initialize the lcd SPI port interface */
static void lcd_spi_init(void)
{
/* configure SSn (P7.1) as output */
PCON7 = (PCON7 & ~0x000000F0) | 0x00000010;
/* configure MISO (P3.2) input, MOSI (P3.6) output, SCLK (P3.7) output */
PCON3 = (PCON3 & ~0xFF000F00) | 0x11000000;
/* set all outputs high */
PDAT7 |= LCD_SPI_SSn;
PDAT3 |= (LCD_SPI_MOSI | LCD_SPI_SCLK);
}
/* read LCD identification word over SPI */
static unsigned int lcd_read_reg(unsigned reg)
{
unsigned int data;
lcd_spi_transfer(24, (0x74 << 16) | reg); //0111.0100
data = lcd_spi_transfer(24, (0x77 << 16)); //0111.0111
return data & 0xFFFF;
}
/* write LCD register over SPI */
static void lcd_write_reg(unsigned char reg, unsigned int data)
{
lcd_spi_transfer(24, (0x74 << 16) | reg); //0111.0100
lcd_spi_transfer(24, (0x76 << 16) | data); //0111.0110
}
/* enable/disable clock signals towards the lcd */
static void lcd_controller_power(bool on)
{
if (on) {
LCDCON1 |= 0x80003;
}
else {
LCDCON1 &= ~0x80003;
} }
} }
/* lcd init configuration for lcd type 1 */
static void lcd_init1(void)
{
lcd_write_reg(0x07, 0x0000); /* display control */
lcd_write_reg(0x13, 0x0000); /* power control 3 */
lcd_delay(166670);
lcd_write_reg(0x11, 0x3304); /* power control 2 */
lcd_write_reg(0x14, 0x1300); /* power control 4 */
lcd_write_reg(0x10, 0x1A20); /* power control 1 */
lcd_write_reg(0x13, 0x0040); /* power control 3 */
lcd_delay(833350);
lcd_write_reg(0x13, 0x0060); /* power control 3 */
lcd_write_reg(0x13, 0x0070); /* power control 3 */
lcd_delay(3333400);
lcd_write_reg(0x01, 0x0127); /* driver output control */
lcd_write_reg(0x02, 0x0700); /* lcd driving waveform control */
lcd_write_reg(0x03, 0x1030); /* entry mode */
lcd_write_reg(0x08, 0x0208); /* blank period control 1 */
lcd_write_reg(0x0B, 0x0620); /* frame cycle control */
lcd_write_reg(0x0C, 0x0110); /* external interface control */
lcd_write_reg(0x30, 0x0120); /* gamma control 1 */
lcd_write_reg(0x31, 0x0117); /* gamma control 2 */
lcd_write_reg(0x32, 0x0000); /* gamma control 3 */
lcd_write_reg(0x33, 0x0305); /* gamma control 4 */
lcd_write_reg(0x34, 0x0717); /* gamma control 5 */
lcd_write_reg(0x35, 0x0124); /* gamma control 6 */
lcd_write_reg(0x36, 0x0706); /* gamma control 7 */
lcd_write_reg(0x37, 0x0503); /* gamma control 8 */
lcd_write_reg(0x38, 0x1F03); /* gamma control 9 */
lcd_write_reg(0x39, 0x0009); /* gamma control 10 */
lcd_write_reg(0x40, 0x0000); /* gate scan position */
lcd_write_reg(0x41, 0x0000); /* vertical scroll control */
lcd_write_reg(0x42, 0x013F); /* 1st screen driving position (end) */
lcd_write_reg(0x43, 0x0000); /* 1st screen driving position (start) */
lcd_write_reg(0x44, 0x013F); /* 2nd screen driving position (end) */
lcd_write_reg(0x45, 0x0000); /* 2nd screen driving position (start) */
lcd_write_reg(0x46, 0xEF00); /* horizontal window address */
lcd_write_reg(0x47, 0x013F); /* vertical window address (end) */
lcd_write_reg(0x48, 0x0000); /* vertical window address (start) */
lcd_write_reg(0x07, 0x0015); /* display control */
lcd_delay(500000);
lcd_write_reg(0x07, 0x0017); /* display control */
lcd_write_reg(0x20, 0x0000); /* RAM address set (low part) */
lcd_write_reg(0x21, 0x0000); /* RAM address set (high part) */
lcd_write_reg(0x22, 0x0000); /* write data to GRAM */
}
/* lcd init configuration for lcd type 2 */
static void lcd_init2(void)
{
lcd_write_reg(0x07, 0x0000);
lcd_write_reg(0x12, 0x0000);
lcd_delay(166670);
lcd_write_reg(0x11, 0x000C);
lcd_write_reg(0x12, 0x0A1C);
lcd_write_reg(0x13, 0x0022);
lcd_write_reg(0x14, 0x0000);
lcd_write_reg(0x10, 0x7404);
lcd_write_reg(0x11, 0x0738);
lcd_write_reg(0x10, 0x7404);
lcd_delay(833350);
lcd_write_reg(0x07, 0x0009);
lcd_write_reg(0x12, 0x065C);
lcd_delay(3333400);
lcd_write_reg(0x01, 0xE127);
lcd_write_reg(0x02, 0x0300);
lcd_write_reg(0x03, 0x1100);
lcd_write_reg(0x08, 0x0008);
lcd_write_reg(0x0B, 0x0000);
lcd_write_reg(0x0C, 0x0000);
lcd_write_reg(0x0D, 0x0007);
lcd_write_reg(0x15, 0x0003);
lcd_write_reg(0x16, 0x0014);
lcd_write_reg(0x17, 0x0000);
lcd_write_reg(0x30, 0x0503);
lcd_write_reg(0x31, 0x0303);
lcd_write_reg(0x32, 0x0305);
lcd_write_reg(0x33, 0x0202);
lcd_write_reg(0x34, 0x0204);
lcd_write_reg(0x35, 0x0404);
lcd_write_reg(0x36, 0x0402);
lcd_write_reg(0x37, 0x0202);
lcd_write_reg(0x38, 0x1000);
lcd_write_reg(0x39, 0x1000);
lcd_write_reg(0x07, 0x0009);
lcd_delay(666680);
lcd_write_reg(0x07, 0x0109);
lcd_delay(666680);
lcd_write_reg(0x07, 0x010B);
}
/* lcd enable for lcd type 1 */
static void lcd_enable1(bool on)
{
if (on) {
lcd_write_reg(0x00, 0x0001); /* start oscillation */
lcd_delay(166670);
lcd_write_reg(0x10, 0x0000); /* power control 1 */
lcd_delay(166670);
lcd_write_reg(0x11, 0x3304); /* power control 2 */
lcd_write_reg(0x14, 0x1300); /* power control 4 */
lcd_write_reg(0x10, 0x1A20); /* power control 1 */
lcd_write_reg(0x07, 0x0015); /* display control */
lcd_delay(500000);
lcd_write_reg(0x20, 0x0000); /* RAM address set (low part) */
lcd_write_reg(0x21, 0x0000); /* RAM address set (high part) */
lcd_write_reg(0x22, 0x0000); /* write data to GRAM */
}
else {
lcd_write_reg(0x07, 0x0016); /* display control */
lcd_delay(166670 * 4);
lcd_write_reg(0x07, 0x0004); /* display control */
lcd_delay(166670 * 4);
lcd_write_reg(0x10, 0x1E21); /* power control 1 */
lcd_delay(166670);
}
}
/* lcd enable for lcd type 2 */
static void lcd_enable2(bool on)
{
if (on) {
lcd_write_reg(0x10, 0x0400);
lcd_delay(666680);
lcd_write_reg(0x07, 0x0000);
lcd_write_reg(0x12, 0x0000);
lcd_delay(166670);
lcd_write_reg(0x11, 0x000C);
lcd_write_reg(0x12, 0x0A1C);
lcd_write_reg(0x13, 0x0022);
lcd_write_reg(0x14, 0x0000);
lcd_write_reg(0x10, 0x7404);
lcd_delay(833350);
lcd_write_reg(0x07, 0x0009);
lcd_write_reg(0x12, 0x065C);
lcd_delay(3333400);
lcd_write_reg(0x0B, 0x0000);
lcd_write_reg(0x07, 0x0009);
lcd_delay(666680);
lcd_write_reg(0x07, 0x0109);
lcd_delay(666680);
lcd_write_reg(0x07, 0x010B);
}
else {
lcd_write_reg(0x0B, 0x0000);
lcd_write_reg(0x07, 0x0009);
lcd_delay(666680);
lcd_write_reg(0x07, 0x0008);
lcd_delay(666680);
lcd_write_reg(0x10, 0x0400);
lcd_write_reg(0x10, 0x0401);
lcd_delay(166670);
}
}
/* turn both the lcd controller and the lcd itself on or off */
void lcd_enable(bool on)
{
if (on) {
/* enable controller clock */
PWRCON &= ~(1 << 18);
lcd_controller_power(true);
lcd_delay(166670);
}
/* call type specific power function */
if (lcd_type == 1) {
lcd_enable1(on);
}
else {
lcd_enable2(on);
}
if (!on) {
lcd_controller_power(false);
/* disable controller clock */
PWRCON |= (1 << 18);
}
}
/* initialise the lcd controller inside the s5l8700 */
static void lcd_controller_init(void)
{
PWRCON &= ~(1 << 18);
LCDCON1 = 0x991DC;
LCDCON2 = 0xE8;
LCDTCON1 = (lcd_type == 1) ? 0x70103 : 0x30303;
LCDTCON2 = (lcd_type == 1) ? 0x70103 : 0x30703;
LCDTCON3 = 0x9F8EF;
LCDOSD1 = 0;
LCDOSD2 = 0;
LCDOSD3 = 0;
LCDB1SADDR1 = 0;
LCDB2SADDR1 = 0;
LCDF1SADDR1 = 0;
LCDF2SADDR1 = 0;
LCDB1SADDR2 = 0;
LCDB2SADDR2 = 0;
LCDF1SADDR2 = 0;
LCDF2SADDR2 = 0;
LCDB1SADDR3 = 0;
LCDB2SADDR3 = 0;
LCDF1SADDR3 = 0;
LCDF2SADDR3 = 0;
LCDKEYCON = 0;
LCDCOLVAL = 0;
LCDBGCON = 0;
LCDFGCON = 0;
LCDDITHMODE = 0;
LCDINTCON = 0;
}
/* LCD init */
void lcd_init_device(void) void lcd_init_device(void)
{ {
} unsigned int lcd_id;
/*** Update functions ***/ /* configure LCD SPI pins */
lcd_spi_init();
/* Performance function that works with an external buffer /* identify display through SPI */
note that by and bheight are in 8-pixel units! */ lcd_id = lcd_read_reg(0);
void lcd_blit_mono(const unsigned char *data, int x, int by, int width, lcd_type = (lcd_id == LCD_TYPE1_ID) ? 1 : 2;
int bheight, int stride)
{ /* configure LCD pins */
/* Copy display bitmap to hardware */ PCON_ASRAM = 1;
while (bheight--)
{ /* init LCD controller */
lcd_controller_init();
/* display specific init sequence */
if (lcd_type == 1) {
lcd_init1();
} }
} else {
lcd_init2();
/* Performance function that works with an external buffer
note that by and bheight are in 8-pixel units! */
void lcd_blit_grey_phase_blit(unsigned char *values, unsigned char *phases,
int x, int by, int width, int bheight, int stride)
{
(void)values;
(void)phases;
(void)x;
(void)by;
(void)width;
(void)bheight;
(void)stride;
}
/* Update the display.
This must be called after all other LCD functions that change the display. */
void lcd_update(void) ICODE_ATTR;
void lcd_update(void)
{
int y;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
} }
/* set active background buffer */
LCDCON1 &= ~(1 << 21); /* clear BDBCON */
/* set background buffer address */
LCDB1SADDR1 = (uint32_t) &lcd_local_fb[0][0];
LCDB1SADDR2 = (uint32_t) &lcd_local_fb[LCD_HEIGHT][0];
lcd_enable(true);
} }
/* Update a fraction of the display. */
void lcd_update_rect(int, int, int, int) ICODE_ATTR;
void lcd_update_rect(int x, int y, int width, int height) void lcd_update_rect(int x, int y, int width, int height)
{ {
int ymax; fb_data *src;
uint32_t *dst;
fb_data pixel;
int h, w;
/* The Y coordinates have to work on even 8 pixel rows */ for (h = 0; h < height; h++) {
ymax = (y + height-1) >> 3; src = &lcd_framebuffer[y][x];
y >>= 3; dst = &lcd_local_fb[y][x];
for (w = 0; w < width; w++) {
if(x + width > LCD_WIDTH) pixel = src[w];
width = LCD_WIDTH - x; dst[w] = (RGB_UNPACK_RED(pixel) << 16) |
if (width <= 0) (RGB_UNPACK_GREEN(pixel) << 8) |
return; /* nothing left to do, 0 is harmful to lcd_write_data() */ (RGB_UNPACK_BLUE(pixel) << 0);
if(ymax >= LCD_FBHEIGHT) }
ymax = LCD_FBHEIGHT-1; y++;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
} }
} }
void lcd_update(void)
{
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
}