rockbox/firmware/target/arm/sandisk/sansa-e200/lcd-e200.c
Thomas Martitz 382d1861af kernel: Break out kernel primitives into separate files and move to separate dir.
No code changed, just shuffling stuff around. This should make it easier to
build only select parts kernel and use different implementations.

Change-Id: Ie1f00f93008833ce38419d760afd70062c5e22b5
2014-03-03 18:11:57 +01:00

544 lines
17 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Rockbox driver for Sansa e200 LCDs
*
* Based on reverse engineering done my MrH
*
* Copyright (c) 2006 Daniel Ankers
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "kernel.h"
#include "lcd.h"
#include "lcd-target.h"
/* Power and display status */
extern bool lcd_on; /* lcd-memframe.c */
static bool power_on = false; /* Is the power turned on? */
/* Reverse Flag */
#define R_DISP_CONTROL_NORMAL 0x0004
#define R_DISP_CONTROL_REV 0x0000
static unsigned short r_disp_control_rev = R_DISP_CONTROL_NORMAL;
/* Flipping */
#define R_DRV_OUTPUT_CONTROL_NORMAL 0x101b
#define R_DRV_OUTPUT_CONTROL_FLIPPED 0x131b
static unsigned short r_drv_output_control = R_DRV_OUTPUT_CONTROL_NORMAL;
#define LCD_DATA_IN_GPIO GPIOB_INPUT_VAL
#define LCD_DATA_IN_PIN 6
#define LCD_DATA_OUT_GPIO GPIOB_OUTPUT_VAL
#define LCD_DATA_OUT_PIN 7
#define LCD_CLOCK_GPIO GPIOB_OUTPUT_VAL
#define LCD_CLOCK_PIN 5
#define LCD_CS_GPIO GPIOD_OUTPUT_VAL
#define LCD_CS_PIN 6
#define LCD_REG_0 (*(volatile unsigned long *)(0xc2000000))
#define LCD_REG_1 (*(volatile unsigned long *)(0xc2000004))
#define LCD_REG_2 (*(volatile unsigned long *)(0xc2000008))
#define LCD_REG_3 (*(volatile unsigned long *)(0xc200000c))
#define LCD_REG_4 (*(volatile unsigned long *)(0xc2000010))
#define LCD_REG_5 (*(volatile unsigned long *)(0xc2000014))
#define LCD_REG_6 (*(volatile unsigned long *)(0xc2000018))
#define LCD_REG_7 (*(volatile unsigned long *)(0xc200001c))
#define LCD_REG_8 (*(volatile unsigned long *)(0xc2000020))
#define LCD_REG_9 (*(volatile unsigned long *)(0xc2000024))
#define LCD_FB_BASE_REG (*(volatile unsigned long *)(0xc2000028))
/* Taken from HD66789 datasheet and seems similar enough.
Definitely a Renesas chip though with a perfect register index
match but at least one bit seems to be set that that datasheet
doesn't show. It says T.B.D. on the regmap anyway. */
#define R_START_OSC 0x00
#define R_DRV_OUTPUT_CONTROL 0x01
#define R_DRV_WAVEFORM_CONTROL 0x02
#define R_ENTRY_MODE 0x03
#define R_COMPARE_REG1 0x04
#define R_COMPARE_REG2 0x05
#define R_DISP_CONTROL1 0x07
#define R_DISP_CONTROL2 0x08
#define R_DISP_CONTROL3 0x09
#define R_FRAME_CYCLE_CONTROL 0x0b
#define R_EXT_DISP_INTF_CONTROL 0x0c
#define R_POWER_CONTROL1 0x10
#define R_POWER_CONTROL2 0x11
#define R_POWER_CONTROL3 0x12
#define R_POWER_CONTROL4 0x13
#define R_RAM_ADDR_SET 0x21
#define R_RAM_READ_DATA 0x21
#define R_RAM_WRITE_DATA 0x22
#define R_RAM_WRITE_DATA_MASK1 0x23
#define R_RAM_WRITE_DATA_MASK2 0x24
#define R_GAMMA_FINE_ADJ_POS1 0x30
#define R_GAMMA_FINE_ADJ_POS2 0x31
#define R_GAMMA_FINE_ADJ_POS3 0x32
#define R_GAMMA_GRAD_ADJ_POS 0x33
#define R_GAMMA_FINE_ADJ_NEG1 0x34
#define R_GAMMA_FINE_ADJ_NEG2 0x35
#define R_GAMMA_FINE_ADJ_NEG3 0x36
#define R_GAMMA_GRAD_ADJ_NEG 0x37
#define R_GAMMA_AMP_ADJ_POS 0x38
#define R_GAMMA_AMP_ADJ_NEG 0x39
#define R_GATE_SCAN_START_POS 0x40
#define R_VERT_SCROLL_CONTROL 0x41
#define R_1ST_SCR_DRIVE_POS 0x42
#define R_2ND_SCR_DRIVE_POS 0x43
#define R_HORIZ_RAM_ADDR_POS 0x44
#define R_VERT_RAM_ADDR_POS 0x45
/* We don't know how to receive a DMA finished signal from the LCD controller.
* To avoid problems with flickering, we double-buffer the framebuffer.
* Align as in lcd-16bit.c and not cached. */
fb_data lcd_driver_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH]
__attribute__((aligned(16))) NOCACHEBSS_ATTR;
#ifdef BOOTLOADER
static void lcd_init_gpio(void)
{
GPIOB_ENABLE |= (1<<7);
GPIOB_ENABLE |= (1<<5);
GPIOB_OUTPUT_EN |= (1<<7);
GPIOB_OUTPUT_EN |= (1<<5);
GPIOD_ENABLE |= (1<<6);
GPIOD_OUTPUT_EN |= (1<<6);
}
#endif
static void lcd_bus_idle(void)
{
LCD_CLOCK_GPIO |= (1 << LCD_CLOCK_PIN);
LCD_DATA_OUT_GPIO |= (1 << LCD_DATA_OUT_PIN);
}
static void lcd_send_byte(unsigned char byte)
{
int i;
for (i = 7; i >=0 ; i--)
{
LCD_CLOCK_GPIO &= ~(1 << LCD_CLOCK_PIN);
if ((byte >> i) & 1)
{
LCD_DATA_OUT_GPIO |= (1 << LCD_DATA_OUT_PIN);
} else {
LCD_DATA_OUT_GPIO &= ~(1 << LCD_DATA_OUT_PIN);
}
udelay(1);
LCD_CLOCK_GPIO |= (1 << LCD_CLOCK_PIN);
udelay(1);
lcd_bus_idle();
udelay(3);
}
}
static void lcd_send_msg(unsigned char cmd, unsigned int data)
{
lcd_bus_idle();
udelay(1);
LCD_CS_GPIO &= ~(1 << LCD_CS_PIN);
udelay(10);
lcd_send_byte(cmd);
lcd_send_byte((unsigned char)(data >> 8));
lcd_send_byte((unsigned char)(data & 0xff));
LCD_CS_GPIO |= (1 << LCD_CS_PIN);
udelay(1);
lcd_bus_idle();
}
static void lcd_write_reg(unsigned int reg, unsigned int data)
{
lcd_send_msg(0x70, reg);
lcd_send_msg(0x72, data);
}
/* Run the powerup sequence for the driver IC */
static void lcd_power_on(void)
{
/* Clear standby bit */
lcd_write_reg(R_POWER_CONTROL1, 0x0000);
/** Power ON Sequence **/
lcd_write_reg(R_START_OSC, 0x0001);
/* 10ms or more for oscillation circuit to stabilize */
sleep(HZ/50);
/* SAP2-0=100, BT2-0=100, AP2-0=100, DK=1, SLP=0, STB=0 */
lcd_write_reg(R_POWER_CONTROL1, 0x4444);
/* DC12-10=000, DC2-0=000, VC2-0=001 */
lcd_write_reg(R_POWER_CONTROL2, 0x0001);
/* PON=0, VRH3-0=0011 */
lcd_write_reg(R_POWER_CONTROL3, 0x0003);
/* VCOMG=0, VDV4-0=10001, VCM3-0=11001 */
lcd_write_reg(R_POWER_CONTROL4, 0x1119);
/* PON=1, VRH3-0=0011 */
lcd_write_reg(R_POWER_CONTROL3, 0x0013);
sleep(HZ/25);
/* SAP2-0=100, BT2-0=100, AP2-0=100, DK=0, SLP=0, STB=0 */
lcd_write_reg(R_POWER_CONTROL1, 0x4440);
/* VCOMG=1, VDV4-0=10001, VCM3-0=11001 */
lcd_write_reg(R_POWER_CONTROL4, 0x3119);
sleep(HZ/6);
/* VSPL=0, HSPL=0, DPL=1, EPL=0, SM=0, GS=x, SS=x, NL4-0=11011 */
lcd_write_reg(R_DRV_OUTPUT_CONTROL, r_drv_output_control);
/* FLD=0, FLD0=1, B/C=1, EOR=1, NW5-0=000000 */
lcd_write_reg(R_DRV_WAVEFORM_CONTROL, 0x0700);
/* TRI=0, DFM1-0=11, BGR=0, HWM=1, ID1-0=10, AM=0, LG2-0=000
* AM: horizontal update direction
* ID1-0: H decrement, V increment
*/
lcd_write_reg(R_ENTRY_MODE, 0x6020);
lcd_write_reg(R_COMPARE_REG1, 0x0000);
lcd_write_reg(R_COMPARE_REG2, 0x0000);
/* FP3-0=0010, BP3-0=0010 */
lcd_write_reg(R_DISP_CONTROL2, 0x0202);
/* PTG1-0=00 (normal scan), ISC3-0=0000 (ignored) */
lcd_write_reg(R_DISP_CONTROL3, 0x0000);
/* NO2-0=01, SDT1-0=00, EQ1-0=01, DIV1-0=00, RTN3-0=0000 */
lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4400);
/* RM=1, DM1-0=01, RIM1-0=00 */
lcd_write_reg(R_EXT_DISP_INTF_CONTROL, 0x0110);
/* SCN4-0=00000 - G1 if GS=0, G240 if GS=1 */
lcd_write_reg(R_GATE_SCAN_START_POS, 0x0000);
/* VL7-0=00000000 (0 lines) */
lcd_write_reg(R_VERT_SCROLL_CONTROL, 0x0000);
/* SE17-10=219, SS17-10=0 - 220 gates */
lcd_write_reg(R_1ST_SCR_DRIVE_POS, (219 << 8));
/* SE27-10=0, SS27-10=0 - no second screen */
lcd_write_reg(R_2ND_SCR_DRIVE_POS, 0x0000);
/* HEA=175, HSA=0 = H window from 0-175 */
lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (175 << 8));
/* VEA=219, VSA=0 = V window from 0-219 */
lcd_write_reg(R_VERT_RAM_ADDR_POS, (219 << 8));
/* PKP12-10=000, PKP02-00=000 */
lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x0000);
/* PKP32-30=111, PKP22-20=100 */
lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0704);
/* PKP52-50=001, PKP42-40=111 */
lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0107);
/* PRP12-10=111, PRP02-00=100 */
lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x0704);
/* PKN12-10=001, PKN02-00=111 */
lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0107);
/* PKN32-30=000, PKN22-20=010 */
lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 0x0002);
/* PKN52-50=111, PKN42-40=111 */
lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0707);
/* PRN12-10=101, PRN02-00=011 */
lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 0x0503);
/* VRP14-10=00000, VRP03-00=0000 */
lcd_write_reg(R_GAMMA_AMP_ADJ_POS, 0x0000);
/* WRN14-10=00000, VRN03-00=0000 */
lcd_write_reg(R_GAMMA_AMP_ADJ_NEG, 0x0000);
/* AD15-0=175 (upper right corner) */
lcd_write_reg(R_RAM_ADDR_SET, 175);
/* RM=1, DM1-0=01, RIM1-0=00 */
lcd_write_reg(R_EXT_DISP_INTF_CONTROL, 0x0110);
power_on = true;
}
/* Run the display on sequence for the driver IC */
static void lcd_display_on(void)
{
if (!power_on)
{
/* Power has been turned off so full reinit is needed */
lcd_power_on();
}
else
{
/* Restore what we fiddled with when turning display off */
/* PON=1, VRH3-0=0011 */
lcd_write_reg(R_POWER_CONTROL3, 0x0013);
/* NO2-0=01, SDT1-0=00, EQ1-0=01, DIV1-0=00, RTN3-0=0000 */
lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4400);
/* VCOMG=1, VDV4-0=10001, VCM3-0=11001 */
lcd_write_reg(R_POWER_CONTROL4, 0x3119);
}
/* SAP2-0=100, BT2-0=111, AP2-0=100, DK=1, SLP=0, STB=0 */
lcd_write_reg(R_POWER_CONTROL1, 0x4740);
sleep(HZ/25);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=0, DTE=0, CL=0,
REV=x, D1-0=01 */
lcd_write_reg(R_DISP_CONTROL1, 0x0041 | r_disp_control_rev);
sleep(HZ/30);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
REV=x, D1-0=01 */
lcd_write_reg(R_DISP_CONTROL1, 0x0061 | r_disp_control_rev);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
REV=x, D1-0=11 */
lcd_write_reg(R_DISP_CONTROL1, 0x0063 | r_disp_control_rev);
sleep(HZ/30);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=1, CL=0,
REV=x, D1-0=11 */
lcd_write_reg(R_DISP_CONTROL1, 0x0073 | r_disp_control_rev);
/* Go into write data mode */
lcd_send_msg(0x70, R_RAM_WRITE_DATA);
/* tell that we're on now */
lcd_on = true;
}
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
/* Turn off visible display operations */
static void lcd_display_off(void)
{
/* block drawing operations and changing of first */
lcd_on = false;
/* NO2-0=01, SDT1-0=00, EQ1-0=00, DIV1-0=00, RTN3-0=0000 */
lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4000);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=1, CL=0,
REV=x, D1-0=10 */
lcd_write_reg(R_DISP_CONTROL1, 0x0072 | r_disp_control_rev);
sleep(HZ/25);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
REV=x, D1-0=10 */
lcd_write_reg(R_DISP_CONTROL1, 0x0062 | r_disp_control_rev);
sleep(HZ/25);
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=0, GON=0, DTE=0, CL=0,
REV=0, D1-0=00 */
lcd_write_reg(R_DISP_CONTROL1, 0x0000);
/* SAP2-0=000, BT2-0=000, AP2-0=000, DK=0, SLP=0, STBY=0 */
lcd_write_reg(R_POWER_CONTROL1, 0x0000);
/* PON=0, VRH3-0=0011 */
lcd_write_reg(R_POWER_CONTROL3, 0x0003);
/* VCOMG=0, VDV4-0=10001, VCM4-0=11001 */
lcd_write_reg(R_POWER_CONTROL4, 0x1119);
}
#endif
void lcd_init_device(void)
{
/* All this is magic worked out by MrH */
/* Stop any DMA which is in progress */
LCD_REG_6 &= ~1;
udelay(100000);
#ifdef BOOTLOADER /* Bother at all to do this again? */
/* Init GPIO ports */
lcd_init_gpio();
/* Controller init */
GPO32_ENABLE |= (1 << 28);
GPO32_VAL &= ~(1 << 28);
DEV_INIT1 = ( (DEV_INIT1 & 0x03ffffff) | (0x15 << 26) );
outl(((inl(0x70000014) & (0x0fffffff)) | (0x5 << 28)), 0x70000014);
outl((inl(0x70000020) & ~(0x3 << 10)), 0x70000020);
DEV_EN |= DEV_LCD; /* Enable controller */
outl(0x6, 0x600060d0);
DEV_RS |= DEV_LCD; /* Reset controller */
outl((inl(0x70000020) & ~(1 << 14)), 0x70000020);
lcd_bus_idle();
DEV_RS &=~DEV_LCD; /* Clear reset */
udelay(1000);
LCD_REG_0 = (LCD_REG_0 & (0x00ffffff)) | (0x22 << 24);
LCD_REG_0 = (LCD_REG_0 & (0xff00ffff)) | (0x14 << 16);
LCD_REG_0 = (LCD_REG_0 & (0xffffc0ff)) | (0x3 << 8);
LCD_REG_0 = (LCD_REG_0 & (0xffffffc0)) | (0xa);
LCD_REG_1 &= 0x00ffffff;
LCD_REG_1 &= 0xff00ffff;
LCD_REG_1 = (LCD_REG_1 & 0xffff03ff) | (0x2 << 10);
LCD_REG_1 = (LCD_REG_1 & 0xfffffc00) | (0xdd);
LCD_REG_2 |= (1 << 5);
LCD_REG_2 |= (1 << 6);
LCD_REG_2 = (LCD_REG_2 & 0xfffffcff) | (0x2 << 8);
LCD_REG_7 &= (0xf800ffff);
LCD_REG_7 &= (0xfffff800);
LCD_REG_8 = (LCD_REG_8 & (0xf800ffff)) | (0xb0 << 16);
LCD_REG_8 = (LCD_REG_8 & (0xfffff800)) | (0xdc); /* X-Y Geometry? */
LCD_REG_5 |= 0xc;
LCD_REG_5 = (LCD_REG_5 & ~(0x70)) | (0x3 << 4);
LCD_REG_5 |= 2;
LCD_REG_6 &= ~(1 << 15);
LCD_REG_6 |= (0xe00);
LCD_REG_6 = (LCD_REG_6 & (0xffffff1f)) | (0x4 << 5);
LCD_REG_6 |= (1 << 4);
LCD_REG_5 &= ~(1 << 7);
/* lcd_driver_framebuffer is uncached therefore at the physical address */
LCD_FB_BASE_REG = (long)lcd_driver_framebuffer;
udelay(100000);
/* LCD init */
/* Pull RESET low, then high to reset driver IC */
GPO32_VAL &= ~(1 << 28);
udelay(10000);
GPO32_VAL |= (1 << 28);
udelay(10000);
lcd_display_on();
#else
/* Power and display already ON - switch framebuffer address and reset
settings */
/* lcd_driver_framebuffer is uncached therefore at the physical address */
LCD_FB_BASE_REG = (long)lcd_driver_framebuffer;
power_on = true;
lcd_on = true;
lcd_set_invert_display(false);
lcd_set_flip(false);
#endif
LCD_REG_6 |= 1; /* Start DMA */
}
#if defined(HAVE_LCD_ENABLE)
void lcd_enable(bool on)
{
if (on == lcd_on)
return;
if (on)
{
DEV_EN |= DEV_LCD; /* Enable LCD controller */
lcd_display_on(); /* Turn on display */
lcd_update(); /* Resync display */
send_event(LCD_EVENT_ACTIVATION, NULL);
LCD_REG_6 |= 1; /* Restart DMA */
sleep(HZ/50); /* Wait for a frame to be written */
}
else
{
LCD_REG_6 &= ~1; /* Disable DMA */
sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
lcd_display_off(); /* Turn off display */
DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
}
}
#endif
#if defined(HAVE_LCD_SLEEP)
void lcd_sleep(void)
{
LCD_REG_6 &= ~1;
sleep(HZ/50);
if (power_on)
{
/* Turn off display */
if (lcd_on)
lcd_display_off();
power_on = false;
}
/* Set standby mode */
/* SAP2-0=000, BT2-0=000, AP2-0=000, DK=0, SLP=0, STB=1 */
lcd_write_reg(R_POWER_CONTROL1, 0x0001);
}
#endif
/*** hardware configuration ***/
void lcd_set_contrast(int val)
{
/* TODO: Implement lcd_set_contrast() */
(void)val;
}
void lcd_set_invert_display(bool yesno)
{
bool dma_on = LCD_REG_6 & 1;
if (dma_on)
{
LCD_REG_6 &= ~1; /* Disable DMA */
sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
}
r_disp_control_rev = yesno ? R_DISP_CONTROL_REV :
R_DISP_CONTROL_NORMAL;
if (lcd_on)
{
/* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, CL=0,
DTE=1, REV=x, D1-0=11 */
lcd_write_reg(R_DISP_CONTROL1, 0x0073 | r_disp_control_rev);
}
if (dma_on)
{
DEV_EN |= DEV_LCD; /* Enable LCD controller */
lcd_send_msg(0x70, R_RAM_WRITE_DATA); /* Set to RAM write mode */
LCD_REG_6 |= 1; /* Restart DMA */
}
}
/* turn the display upside down (call lcd_update() afterwards) */
void lcd_set_flip(bool yesno)
{
bool dma_on = LCD_REG_6 & 1;
if (dma_on)
{
LCD_REG_6 &= ~1; /* Disable DMA */
sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
}
r_drv_output_control = yesno ? R_DRV_OUTPUT_CONTROL_FLIPPED :
R_DRV_OUTPUT_CONTROL_NORMAL;
if (power_on)
{
/* VSPL=0, HSPL=0, DPL=1, EPL=0, SM=0, GS=x, SS=x,
NL4-0=11011 (G1-G224) */
lcd_write_reg(R_DRV_OUTPUT_CONTROL, r_drv_output_control);
}
if (dma_on)
{
DEV_EN |= DEV_LCD; /* Enable LCD controller */
lcd_send_msg(0x70, R_RAM_WRITE_DATA); /* Set to RAM write mode */
LCD_REG_6 |= 1; /* Restart DMA */
}
}