rockbox/firmware/target/arm/sandisk/sansa-e200/lcd-e200.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

666 lines
20 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 "lcd.h"
#include "system.h"
#include <string.h>
#include "backlight-target.h"
#include "cpu.h"
/* Power and display status */
static bool power_on = false; /* Is the power turned on? */
static bool display_on SHAREDBSS_ATTR = false; /* Is the display turned on? */
static unsigned lcd_yuv_options SHAREDBSS_ATTR = 0;
/* 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. */
static 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 */
display_on = true;
}
/* Turn off visible display operations */
static void lcd_display_off(void)
{
/* block drawing operations and changing of first */
display_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);
}
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;
display_on = true;
lcd_set_invert_display(false);
lcd_set_flip(false);
#endif
LCD_REG_6 |= 1; /* Start DMA */
}
void lcd_enable(bool on)
{
if (on == display_on)
return;
if (on)
{
DEV_EN |= DEV_LCD; /* Enable LCD controller */
lcd_display_on(); /* Turn on display */
lcd_update(); /* Resync display */
lcd_call_enable_hook();
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 */
}
}
bool lcd_enabled(void)
{
return display_on;
}
void lcd_sleep(void)
{
LCD_REG_6 &= ~1;
sleep(HZ/50);
if (power_on)
{
/* Turn off display */
if (display_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);
}
/* Copies a rectangle from one framebuffer to another. Can be used in
single transfer mode with width = num pixels, and height = 1 which
allows a full-width rectangle to be copied more efficiently. */
extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src,
int width, int height);
void lcd_update_rect(int x, int y, int width, int height)
{
fb_data *dst, *src;
if (!display_on)
return;
if (x + width > LCD_WIDTH)
width = LCD_WIDTH - x; /* Clip right */
if (x < 0)
width += x, x = 0; /* Clip left */
if (width <= 0)
return; /* nothing left to do */
if (y + height > LCD_HEIGHT)
height = LCD_HEIGHT - y; /* Clip bottom */
if (y < 0)
height += y, y = 0; /* Clip top */
if (height <= 0)
return; /* nothing left to do */
dst = &lcd_driver_framebuffer[y][x];
src = &lcd_framebuffer[y][x];
/* Copy part of the Rockbox framebuffer to the second framebuffer */
if (width < LCD_WIDTH)
{
/* Not full width - do line-by-line */
lcd_copy_buffer_rect(dst, src, width, height);
}
else
{
/* Full width - copy as one line */
lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1);
}
}
void lcd_update(void)
{
if (!display_on)
return;
/* Copy the Rockbox framebuffer to the second framebuffer */
lcd_copy_buffer_rect(&lcd_driver_framebuffer[0][0],
&lcd_framebuffer[0][0], LCD_WIDTH*LCD_HEIGHT, 1);
}
/*** 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 (display_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 */
}
}
/* Blitting functions */
void lcd_yuv_set_options(unsigned options)
{
lcd_yuv_options = options;
}
/* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
extern void lcd_write_yuv420_lines(fb_data *dst,
unsigned char const * const src[3],
int width,
int stride);
extern void lcd_write_yuv420_lines_odither(fb_data *dst,
unsigned char const * const src[3],
int width,
int stride,
int x_screen, /* To align dither pattern */
int y_screen);
/* Performance function to blit a YUV bitmap directly to the LCD */
/* For the e200 - show it rotated */
/* So the LCD_WIDTH is now the height */
void lcd_blit_yuv(unsigned char * const src[3],
int src_x, int src_y, int stride,
int x, int y, int width, int height)
{
unsigned char const * yuv_src[3];
off_t z;
if (!display_on)
return;
/* Sorry, but width and height must be >= 2 or else */
width &= ~1;
height >>= 1;
y = LCD_WIDTH - 1 - y;
fb_data *dst = &lcd_driver_framebuffer[x][y];
z = stride*src_y;
yuv_src[0] = src[0] + z + src_x;
yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
if (lcd_yuv_options & LCD_YUV_DITHER)
{
do
{
lcd_write_yuv420_lines_odither(dst, yuv_src, width, stride, y, x);
yuv_src[0] += stride << 1; /* Skip down two luma lines */
yuv_src[1] += stride >> 1; /* Skip down one chroma line */
yuv_src[2] += stride >> 1;
dst -= 2;
y -= 2;
}
while (--height > 0);
}
else
{
do
{
lcd_write_yuv420_lines(dst, yuv_src, width, stride);
yuv_src[0] += stride << 1; /* Skip down two luma lines */
yuv_src[1] += stride >> 1; /* Skip down one chroma line */
yuv_src[2] += stride >> 1;
dst -= 2;
}
while (--height > 0);
}
}