From 400cd026f3267cff8b67d0bf0ab5f4645732cc5e Mon Sep 17 00:00:00 2001 From: Bertrik Sikken Date: Mon, 24 Aug 2009 17:37:53 +0000 Subject: Meizu M6SP: initial LCD driver (compiles but is untested) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22500 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/s5l8700/meizu-m6sp/lcd-m6sp.c | 514 +++++++++++++++++----- 1 file changed, 413 insertions(+), 101 deletions(-) diff --git a/firmware/target/arm/s5l8700/meizu-m6sp/lcd-m6sp.c b/firmware/target/arm/s5l8700/meizu-m6sp/lcd-m6sp.c index 1affab3b01..b6330406f2 100644 --- a/firmware/target/arm/s5l8700/meizu-m6sp/lcd-m6sp.c +++ b/firmware/target/arm/s5l8700/meizu-m6sp/lcd-m6sp.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2002 by Alan Korr + * Copyright (C) 2009 Bertrik Sikken * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,118 +18,430 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "config.h" + +#include + +#include "config.h" +#include "s5l8700.h" +#include "lcd.h" + +/* LCD driver for the Meizu M6 SP using the CLCD controller in the S5L8700 -#include "hwcompat.h" -#include "kernel.h" -#include "lcd.h" -#include "system.h" -#include "cpu.h" + The Meizu M6 SP can have two different LCDs, the S6D0139 and another + (yet unknown) type, the exact type is detected at run-time. -/*** definitions ***/ + 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; -/** globals **/ - -static int xoffset; /* needed for flip */ - -/*** hardware configuration ***/ - -int lcd_default_contrast(void) -{ - return 0x1f; -} - -void lcd_set_contrast(int val) -{ -} - -void lcd_set_invert_display(bool yesno) -{ -} - -/* turn the display upside down (call lcd_update() afterwards) */ -void lcd_set_flip(bool yesno) -{ - /* TODO: flip mode isn't working. The commands in the else part of - this function are how the original firmware inits the LCD */ - - if (yesno) - { - xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 128 we have */ - } - else - { - xoffset = 0; - } -} - - -/* LCD init */ -void lcd_init_device(void) +/* local frame buffer, keeps pixels in 32-bit words in format 0x00RRGGBB */ +static uint32_t lcd_local_fb[LCD_HEIGHT][LCD_WIDTH]; + + +/* simple and crude delay */ +static void lcd_delay(int count) +{ + volatile int i; + for (i = 0; i < count; i++); +} + +/* 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; + + /* SSn active */ + PDAT7 &= ~LCD_SPI_SSn; + lcd_delay(10); + + /* send and receive data */ + while (bits--) { + /* CLK low */ + PDAT3 &= ~LCD_SPI_SCLK; + + /* set MOSI */ + if (data_out & (1 << bits)) { + 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); + } + + /* SSn inactive */ + 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); + } +} -/*** Update functions ***/ +/* 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; +} + +void lcd_init_device(void) +{ + unsigned int lcd_id; + + /* configure LCD SPI pins */ + lcd_spi_init(); + + /* identify display through SPI */ + lcd_id = lcd_read_reg(0); + lcd_type = (lcd_id == LCD_TYPE1_ID) ? 1 : 2; + + /* configure LCD pins */ + PCON_ASRAM = 1; + + /* init LCD controller */ + lcd_controller_init(); + + /* display specific init sequence */ + if (lcd_type == 1) { + lcd_init1(); + } + else { + lcd_init2(); + } + + /* 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); +} -/* Performance function that works with an external buffer - note that by and bheight are in 8-pixel units! */ -void lcd_blit_mono(const unsigned char *data, int x, int by, int width, - int bheight, int stride) +void lcd_update_rect(int x, int y, int width, int height) { - /* Copy display bitmap to hardware */ - while (bheight--) - { + fb_data *src; + uint32_t *dst; + fb_data pixel; + int h, w; + + for (h = 0; h < height; h++) { + src = &lcd_framebuffer[y][x]; + dst = &lcd_local_fb[y][x]; + for (w = 0; w < width; w++) { + pixel = src[w]; + dst[w] = (RGB_UNPACK_RED(pixel) << 16) | + (RGB_UNPACK_GREEN(pixel) << 8) | + (RGB_UNPACK_BLUE(pixel) << 0); + } + y++; } } - -/* 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++) - { - } + lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); } -/* 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) -{ - int ymax; - - /* The Y coordinates have to work on even 8 pixel rows */ - ymax = (y + height-1) >> 3; - y >>= 3; - - if(x + width > LCD_WIDTH) - width = LCD_WIDTH - x; - if (width <= 0) - return; /* nothing left to do, 0 is harmful to lcd_write_data() */ - if(ymax >= LCD_FBHEIGHT) - ymax = LCD_FBHEIGHT-1; - - /* Copy specified rectange bitmap to hardware */ - for (; y <= ymax; y++) - { - } -} -- cgit v1.2.3