5511611622
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12879 a1c6a512-1295-4272-9138-f99709370657
443 lines
12 KiB
C
443 lines
12 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
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "config.h"
|
|
#include "lcd.h"
|
|
#include "system.h"
|
|
#include <string.h>
|
|
|
|
#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))
|
|
|
|
/* 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 and turn
|
|
* off DMA while updates are taking place */
|
|
static fb_data lcd_driver_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH];
|
|
|
|
static inline 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);
|
|
}
|
|
|
|
static inline void lcd_bus_idle(void)
|
|
{
|
|
LCD_CLOCK_GPIO |= (1 << LCD_CLOCK_PIN);
|
|
LCD_DATA_OUT_GPIO |= (1 << LCD_DATA_OUT_PIN);
|
|
}
|
|
|
|
static inline 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 inline 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 inline void lcd_write_reg(unsigned int reg, unsigned int data)
|
|
{
|
|
lcd_send_msg(0x70, reg);
|
|
lcd_send_msg(0x72, data);
|
|
}
|
|
|
|
static inline void cache_flush(void)
|
|
{
|
|
#ifndef BOOTLOADER
|
|
outl(inl(0xf000f044) | 0x2, 0xf000f044);
|
|
while ((CACHE_CTL & 0x8000) != 0)
|
|
{
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* The LCD controller gets passed the address of the framebuffer, but can only
|
|
use the physical, not the remapped, address. This is a quick and dirty way
|
|
of correcting it */
|
|
static unsigned long phys_fb_address(unsigned long address)
|
|
{
|
|
if(address < 0x10000000)
|
|
{
|
|
return address + 0x10000000;
|
|
} else {
|
|
return address;
|
|
}
|
|
}
|
|
|
|
inline 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);
|
|
|
|
/* Init GPIO ports */
|
|
lcd_init_gpio();
|
|
/* Controller init */
|
|
outl((inl(0x70000084) | (1 << 28)), 0x70000084);
|
|
outl((inl(0x70000080) & ~(1 << 28)), 0x70000080);
|
|
outl(((inl(0x70000010) & (0x03ffffff)) | (0x15 << 26)), 0x70000010);
|
|
outl(((inl(0x70000014) & (0x0fffffff)) | (0x5 << 28)), 0x70000014);
|
|
outl((inl(0x70000020) & ~(0x3 << 10)), 0x70000020);
|
|
DEV_EN |= (1 << 26); /* Enable controller */
|
|
outl(0x6, 0x600060d0);
|
|
DEV_RS |= (1 << 26); /* Reset controller */
|
|
outl((inl(0x70000020) & ~(1 << 14)), 0x70000020);
|
|
lcd_bus_idle();
|
|
DEV_RS &=~(1 << 26); /* 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)) | (0xde); /* 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_FB_BASE_REG = phys_fb_address((unsigned long)lcd_driver_framebuffer);
|
|
|
|
udelay(100000);
|
|
|
|
/* LCD init */
|
|
outl((inl(0x70000080) & ~(1 << 28)), 0x70000080);
|
|
udelay(10000);
|
|
outl((inl(0x70000080) | (1 << 28)), 0x70000080);
|
|
udelay(10000);
|
|
|
|
lcd_write_reg(16, 0x4444);
|
|
lcd_write_reg(17, 0x0001);
|
|
lcd_write_reg(18, 0x0003);
|
|
lcd_write_reg(19, 0x1119);
|
|
lcd_write_reg(18, 0x0013);
|
|
udelay(50000);
|
|
|
|
lcd_write_reg(16, 0x4440);
|
|
lcd_write_reg(19, 0x3119);
|
|
udelay(150000);
|
|
|
|
lcd_write_reg(1, 0x101b);
|
|
lcd_write_reg(2, 0x0700);
|
|
lcd_write_reg(3, 0x6020);
|
|
lcd_write_reg(4, 0x0000);
|
|
lcd_write_reg(5, 0x0000);
|
|
lcd_write_reg(8, 0x0102);
|
|
lcd_write_reg(9, 0x0000);
|
|
lcd_write_reg(11, 0x4400);
|
|
lcd_write_reg(12, 0x0110);
|
|
|
|
lcd_write_reg(64, 0x0000);
|
|
lcd_write_reg(65, 0x0000);
|
|
lcd_write_reg(66, (219 << 8)); /* Screen resolution? */
|
|
lcd_write_reg(67, 0x0000);
|
|
lcd_write_reg(68, (175 << 8));
|
|
lcd_write_reg(69, (219 << 8));
|
|
|
|
lcd_write_reg(48, 0x0000);
|
|
lcd_write_reg(49, 0x0704);
|
|
lcd_write_reg(50, 0x0107);
|
|
lcd_write_reg(51, 0x0704);
|
|
lcd_write_reg(52, 0x0107);
|
|
lcd_write_reg(53, 0x0002);
|
|
lcd_write_reg(54, 0x0707);
|
|
lcd_write_reg(55, 0x0503);
|
|
lcd_write_reg(56, 0x0000);
|
|
lcd_write_reg(57, 0x0000);
|
|
|
|
lcd_write_reg(33, 175);
|
|
|
|
lcd_write_reg(12, 0x0110);
|
|
|
|
lcd_write_reg(16, 0x4740);
|
|
|
|
lcd_write_reg(7, 0x0045);
|
|
|
|
udelay(50000);
|
|
|
|
lcd_write_reg(7, 0x0065);
|
|
lcd_write_reg(7, 0x0067);
|
|
|
|
udelay(50000);
|
|
|
|
lcd_write_reg(7, 0x0077);
|
|
lcd_send_msg(0x70, 34);
|
|
}
|
|
|
|
inline void lcd_update_rect(int x, int y, int width, int height)
|
|
{
|
|
(void)x;
|
|
(void)width;
|
|
/* Turn off DMA and wait for the transfer to complete */
|
|
/* TODO: Work out the proper delay */
|
|
LCD_REG_6 &= ~1;
|
|
udelay(1000);
|
|
|
|
/* Copy the Rockbox framebuffer to the second framebuffer */
|
|
/* TODO: Move the second framebuffer into uncached SDRAM */
|
|
memcpy(((char*)&lcd_driver_framebuffer)+(y * sizeof(fb_data) * LCD_WIDTH),
|
|
((char *)&lcd_framebuffer)+(y * sizeof(fb_data) * LCD_WIDTH),
|
|
((height * sizeof(fb_data) * LCD_WIDTH)));
|
|
cache_flush();
|
|
|
|
/* Restart DMA */
|
|
LCD_REG_6 |= 1;
|
|
}
|
|
|
|
inline void lcd_update(void)
|
|
{
|
|
/* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer
|
|
* and lcd_framebuffer */
|
|
/* Turn off DMA and wait for the transfer to complete */
|
|
LCD_REG_6 &= ~1;
|
|
udelay(1000);
|
|
|
|
/* Copy the Rockbox framebuffer to the second framebuffer */
|
|
memcpy(lcd_driver_framebuffer, lcd_framebuffer, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
|
|
cache_flush();
|
|
|
|
/* Restart DMA */
|
|
LCD_REG_6 |= 1;
|
|
}
|
|
|
|
|
|
/*** hardware configuration ***/
|
|
|
|
void lcd_set_contrast(int val)
|
|
{
|
|
/* TODO: Implement lcd_set_contrast() */
|
|
(void)val;
|
|
}
|
|
|
|
void lcd_set_invert_display(bool yesno)
|
|
{
|
|
/* TODO: Implement lcd_set_invert_display() */
|
|
(void)yesno;
|
|
}
|
|
|
|
/* turn the display upside down (call lcd_update() afterwards) */
|
|
void lcd_set_flip(bool yesno)
|
|
{
|
|
/* TODO: Implement lcd_set_flip() */
|
|
(void)yesno;
|
|
}
|
|
|
|
/* Blitting functions */
|
|
|
|
void lcd_blit(const fb_data* data, int x, int by, int width,
|
|
int bheight, int stride)
|
|
{
|
|
/* TODO: Implement lcd_blit() */
|
|
(void)data;
|
|
(void)x;
|
|
(void)by;
|
|
(void)width;
|
|
(void)bheight;
|
|
(void)stride;
|
|
}
|
|
|
|
#define CSUB_X 2
|
|
#define CSUB_Y 2
|
|
|
|
#define RYFAC (31*257)
|
|
#define GYFAC (63*257)
|
|
#define BYFAC (31*257)
|
|
#define RVFAC 11170 /* 31 * 257 * 1.402 */
|
|
#define GVFAC (-11563) /* 63 * 257 * -0.714136 */
|
|
#define GUFAC (-5572) /* 63 * 257 * -0.344136 */
|
|
#define BUFAC 14118 /* 31 * 257 * 1.772 */
|
|
|
|
#define ROUNDOFFS (127*257)
|
|
|
|
/* Performance function to blit a YUV bitmap directly to the LCD
|
|
Actually this code is from gigabeat, because this target is also
|
|
writing direct to a buffer. */
|
|
void lcd_yuv_blit(unsigned char * const src[3],
|
|
int src_x, int src_y, int stride,
|
|
int _x, int _y, int width, int height)
|
|
{
|
|
const unsigned char *usrc;
|
|
const unsigned char *vsrc;
|
|
const unsigned char *ysrc;
|
|
int xphase;
|
|
int rc, gc, bc;
|
|
int y, u, v;
|
|
int red, green, blue;
|
|
unsigned rbits, gbits, bbits;
|
|
int count;
|
|
fb_data *dst_row;
|
|
|
|
|
|
width = (width + 1) & ~1;
|
|
fb_data *dst = (fb_data*)lcd_driver_framebuffer +
|
|
_x * LCD_WIDTH + (LCD_WIDTH - _y) - 1;
|
|
fb_data *dst_last = dst - (height - 1);
|
|
|
|
do
|
|
{
|
|
dst_row = dst;
|
|
count = width;
|
|
ysrc = src[0] + stride * src_y + src_x;
|
|
|
|
/* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
|
|
usrc = src[1] + (stride/CSUB_X) * (src_y/CSUB_Y)
|
|
+ (src_x/CSUB_X);
|
|
vsrc = src[2] + (stride/CSUB_X) * (src_y/CSUB_Y)
|
|
+ (src_x/CSUB_X);
|
|
xphase = src_x % CSUB_X;
|
|
|
|
u = *usrc++ - 128;
|
|
v = *vsrc++ - 128;
|
|
rc = RVFAC * v + ROUNDOFFS;
|
|
gc = GVFAC * v + GUFAC * u + ROUNDOFFS;
|
|
bc = BUFAC * u + ROUNDOFFS;
|
|
|
|
do
|
|
{
|
|
y = *ysrc++;
|
|
red = RYFAC * y + rc;
|
|
green = GYFAC * y + gc;
|
|
blue = BYFAC * y + bc;
|
|
|
|
if ((unsigned)red > (RYFAC*255+ROUNDOFFS))
|
|
{
|
|
if (red < 0)
|
|
red = 0;
|
|
else
|
|
red = (RYFAC*255+ROUNDOFFS);
|
|
}
|
|
if ((unsigned)green > (GYFAC*255+ROUNDOFFS))
|
|
{
|
|
if (green < 0)
|
|
green = 0;
|
|
else
|
|
green = (GYFAC*255+ROUNDOFFS);
|
|
}
|
|
if ((unsigned)blue > (BYFAC*255+ROUNDOFFS))
|
|
{
|
|
if (blue < 0)
|
|
blue = 0;
|
|
else
|
|
blue = (BYFAC*255+ROUNDOFFS);
|
|
}
|
|
rbits = ((unsigned)red) >> 16 ;
|
|
gbits = ((unsigned)green) >> 16 ;
|
|
bbits = ((unsigned)blue) >> 16 ;
|
|
|
|
*dst_row = (rbits << 11) | (gbits << 5) | bbits;
|
|
|
|
/* next pixel - since rotated, add WIDTH */
|
|
dst_row += LCD_WIDTH;
|
|
|
|
if (++xphase >= CSUB_X)
|
|
{
|
|
u = *usrc++ - 128;
|
|
v = *vsrc++ - 128;
|
|
rc = RVFAC * v + ROUNDOFFS;
|
|
gc = GVFAC * v + GUFAC * u + ROUNDOFFS;
|
|
bc = BUFAC * u + ROUNDOFFS;
|
|
xphase = 0;
|
|
}
|
|
}
|
|
while (--count);
|
|
|
|
if (dst == dst_last) break;
|
|
|
|
dst--;
|
|
src_y++;
|
|
} while( 1);
|
|
}
|
|
|