mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
Some purely mechanical fixes to get the normal build working. Besides missing symbols all the plugins and codecs build just fine. Change-Id: I946ba39096a46be8308450bafd51a0995db8e323
615 lines
18 KiB
C
615 lines
18 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id: lcd-nano2g.c 28868 2010-12-21 06:59:17Z Buschel $
|
|
*
|
|
* Copyright (C) 2009 by Dave Chapman
|
|
*
|
|
* 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 <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
|
|
|
#include "kernel.h"
|
|
#include "system.h"
|
|
#include "cpu.h"
|
|
#include "lcd.h"
|
|
#ifdef IPOD_6G
|
|
#include "pmu-target.h"
|
|
#endif
|
|
#include "backlight-target.h"
|
|
|
|
#include "s5l87xx.h"
|
|
#include "clocking-s5l8702.h"
|
|
#include "dma-s5l8702.h"
|
|
#include "lcd-s5l8702.h"
|
|
#include "lcd-target.h"
|
|
|
|
|
|
// TODO TODO TODO: HAVE_LCD_ENABLE
|
|
|
|
/* Switch command/frame mode:
|
|
*
|
|
* XXX: WIP, TBC
|
|
*
|
|
* Configures MPU interface and pixel mode.
|
|
*
|
|
* Frame mode:
|
|
* 6g:
|
|
* 16-bit 8080-series parallel MPU interface
|
|
* pixel mode: RBG565/1-transfer
|
|
* APB[15:0] -> D[17:10,8:1]
|
|
* nano3g:
|
|
* 9-bit 8080-series parallel PMU interface (TBC)
|
|
* pixel mode: RGB565/2-transfers (TBC)
|
|
* APB[15:0] -> D[8:1] + D[8:1] (TBC)
|
|
*
|
|
* Command mode:
|
|
* LCD HW that uses 8-bit command set (6g, nano3g):
|
|
* 8-bit 8080-series parallel MPU interface
|
|
* APB[7:0] -> D[8:1]
|
|
* LCD HW that uses 16-bit command set (6g):
|
|
* 18-bit 8080-series parallel MPU interface (TBC)
|
|
* APB[15:0] -> D[17:10,8:1]
|
|
*
|
|
* See ILI9320, ILI9326, ILI9340 DS.
|
|
*/
|
|
/* XXX: see ili9340, pg.27 for MCU interface selection,
|
|
* bit31 could be the one that selects interface D[7:0] or D[8:1] for 8-bit), Type-I uses D[7:0] and Type-II uses D[17:10] (ILI9340)
|
|
* 9-bit D[8:0] D[17:9]
|
|
* 16-bit D[15:0] D[17:10],D[8:1]
|
|
* 18-bit D[17:0] D[17:0]
|
|
* bit24 could be the one that selects interface D[8:1] or D[17:10]
|
|
* MCU_interface Mode Pins Write Read
|
|
* #define LCD_MODE_8 0x80000c20 8080 MCU 8-bit bus iface D[8:1] APB[7:0] -> D[8:1] D[8:1] -> LCD_DBUFF[8:1]
|
|
* #define LCD_MODE_9 0x81100db8 9-bit
|
|
* #define LCD_MODE_16 0x80100db0 16-bit D[17:10,8:1] APB[15:0] -> D[17:10,8:1]
|
|
* #define LCD_MODE_18 0x80000da8 18-bit D[17:0] APB[17:0] -> D[17:0] (TBC)
|
|
*
|
|
* #define LCD_MODE_8 0x80000c20 // LCD_MODE_P8T1 // paralled 8-bit, 1 transfer, TBC: DB[17:10] or DB[8:1] ???
|
|
* #define LCD_MODE_9 0x81100db8 // LCD_MODE_P9T2 // TBC: DB[8:1] or DB[17:10] ???, 2-transfers or 1.5-transfers, RGB565 or RBG666 ???
|
|
* #define LCD_MODE_16 0x80100db0 // LCD_MODE_P16T1 // TBC: DB[17:10,8:1]
|
|
* #define LCD_MODE_18 0x80000da8 // LCD_MODE_P18T1 // TBC: DB[17:10,8:1]
|
|
*/
|
|
|
|
#define LCD_MODE_P8 0x80000c20 // LCD_MODE_P8T1 // paralled 8-bit, 1 transfer, TBC: DB[17:10] or DB[8:1] ???
|
|
#define LCD_MODE_P8b 0x80100c20 // TODO: see if it influences, so far we are using it in nano3g with 0x80000c20 and it seems to be working fine
|
|
// It may be a "pixel format" setting for 8 bit, which would only affect DATA and not CMD ???
|
|
#define LCD_MODE_P9 0x81100db8 // LCD_MODE_P9T2 // TBC: DB[8:1] or DB[17:10] ???, 2-transfers or 1.5-transfers, RGB565 or RBG666 ???
|
|
#define LCD_MODE_P16 0x80100db0 // LCD_MODE_P16T1 // TBC: DB[17:10,8:1]
|
|
#define LCD_MODE_P18 0x80000da8 // LCD_MODE_P18T1 // TBC: DB[17:10,8:1]
|
|
|
|
#define LCD_MODE_S8 0x41000c20 // nano4g
|
|
#define LCD_MODE_S9 0x41100db8 // nano4g
|
|
|
|
|
|
|
|
#ifdef S5L_LCD_WITH_CMDSET16
|
|
/* LCD type 16-bit register defines */
|
|
#define R_HORIZ_GRAM_ADDR_SET 0x200
|
|
#define R_VERT_GRAM_ADDR_SET 0x201
|
|
#define R_WRITE_DATA_TO_GRAM 0x202
|
|
#define R_HORIZ_ADDR_START_POS 0x210
|
|
#define R_HORIZ_ADDR_END_POS 0x211
|
|
#define R_VERT_ADDR_START_POS 0x212
|
|
#define R_VERT_ADDR_END_POS 0x213
|
|
#endif
|
|
|
|
/* LCD type 8-bit register defines */
|
|
#define R_COLUMN_ADDR_SET 0x2a
|
|
#define R_ROW_ADDR_SET 0x2b
|
|
#define R_MEMORY_WRITE 0x2c
|
|
|
|
|
|
/** globals **/
|
|
|
|
int lcd_type; /* also needed in debug-s5l8702.c */
|
|
static struct mutex lcd_mutex;
|
|
static uint16_t lcd_dblbuf[LCD_HEIGHT][LCD_WIDTH] CACHEALIGN_ATTR;
|
|
static bool lcd_ispowered;
|
|
|
|
/* TODO: there is no info about these specific drivers,
|
|
some useful info on similar HW such as ILI9320, ILI9326, ILI9340 and */
|
|
|
|
static struct lcd_info_rec *lcd_info;
|
|
static void (*lcd_run_seq)(void*);
|
|
|
|
/* LCD_CONFIG values for command/frame modes */
|
|
static uint32_t lcd_cmd_mode IDATA_ATTR;
|
|
static uint32_t lcd_frame_mode IDATA_ATTR;
|
|
|
|
|
|
// TODO
|
|
/* Each target must define a list containing all supported LCD types */
|
|
// extern struct lcd_info_rec lcd_info_list[];
|
|
// static struct lcd_info_rec lcd_info_list[];
|
|
|
|
|
|
/* DMA configuration */
|
|
|
|
/* One single transfer at once, needed LLIs:
|
|
* screen_size / (DMAC_LLI_MAX_COUNT << swidth) =
|
|
* (320*240*2) / (4095*2) = 19
|
|
*/
|
|
#define LCD_DMA_TSKBUF_SZ 1 /* N tasks, MUST be pow2 */
|
|
#define LCD_DMA_LLIBUF_SZ 32 /* N LLIs, MUST be pow2 */
|
|
|
|
static struct dmac_tsk lcd_dma_tskbuf[LCD_DMA_TSKBUF_SZ];
|
|
static struct dmac_lli volatile \
|
|
lcd_dma_llibuf[LCD_DMA_LLIBUF_SZ] CACHEALIGN_ATTR;
|
|
|
|
static struct dmac_ch lcd_dma_ch =
|
|
{
|
|
.dmac = &s5l8702_dmac0,
|
|
.prio = DMAC_CH_PRIO(4),
|
|
.cb_fn = NULL,
|
|
|
|
.tskbuf = lcd_dma_tskbuf,
|
|
.tskbuf_mask = LCD_DMA_TSKBUF_SZ - 1,
|
|
.queue_mode = QUEUE_NORMAL,
|
|
|
|
.llibuf = lcd_dma_llibuf,
|
|
.llibuf_mask = LCD_DMA_LLIBUF_SZ - 1,
|
|
.llibuf_bus = DMAC_MASTER_AHB1,
|
|
};
|
|
|
|
static struct dmac_ch_cfg lcd_dma_ch_cfg =
|
|
{
|
|
.srcperi = S5L8702_DMAC0_PERI_MEM,
|
|
.dstperi = S5L8702_DMAC0_PERI_LCD_WR,
|
|
.sbsize = DMACCxCONTROL_BSIZE_1,
|
|
.dbsize = DMACCxCONTROL_BSIZE_1,
|
|
.swidth = DMACCxCONTROL_WIDTH_16,
|
|
.dwidth = DMACCxCONTROL_WIDTH_16,
|
|
.sbus = DMAC_MASTER_AHB1,
|
|
.dbus = DMAC_MASTER_AHB1,
|
|
.sinc = DMACCxCONTROL_INC_ENABLE,
|
|
.dinc = DMACCxCONTROL_INC_DISABLE,
|
|
.prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV,
|
|
.lli_xfer_max_count = DMAC_LLI_MAX_COUNT,
|
|
};
|
|
|
|
|
|
/*** clocks ***/
|
|
|
|
// TODO: In mks5lboot --mkraw put a command to specify the address of the binary,
|
|
// for example --address 0x6000, it must be greater than 0x310 which would be the default address,
|
|
// pass this address (0x310..128Kb) in the dfu_options flag
|
|
|
|
// TODO: to lcd-target.c
|
|
static void lcd_target_enable_clocks(bool enable)
|
|
{
|
|
clockgate_enable(CLOCKGATE_LCD, enable);
|
|
#ifdef IPOD_NANO4G
|
|
clockgate_enable(CLOCKGATE_LCD_2, enable);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** LCD controller - low level functions ***/
|
|
|
|
static void s5l_lcd_write_config(uint32_t config) ICODE_ATTR;
|
|
static void s5l_lcd_write_config(uint32_t config)
|
|
{
|
|
while (!(LCD_STATUS & 0x2));
|
|
udelay(1);
|
|
LCD_CON = config;
|
|
}
|
|
|
|
static void s5l_lcd_write_cmd(uint16_t cmd) ICODE_ATTR;
|
|
static void s5l_lcd_write_cmd(uint16_t cmd)
|
|
{
|
|
while (LCD_STATUS & 0x10);
|
|
LCD_WCMD = cmd;
|
|
}
|
|
|
|
static void s5l_lcd_write_data(uint16_t data) ICODE_ATTR;
|
|
static void s5l_lcd_write_data(uint16_t data)
|
|
{
|
|
while (LCD_STATUS & 0x10);
|
|
LCD_WDATA = data;
|
|
}
|
|
|
|
static void s5l_lcd_send_cmd8(uint8_t cmd, int len, uint8_t *data) ICODE_ATTR;
|
|
static void s5l_lcd_send_cmd8(uint8_t cmd, int len, uint8_t *data)
|
|
{
|
|
s5l_lcd_write_cmd(cmd);
|
|
while (len--)
|
|
s5l_lcd_write_data(*data++);
|
|
}
|
|
|
|
#ifdef S5L_LCD_WITH_READID
|
|
static void s5l_lcd_recv_cmd8(uint8_t cmd, int len, uint8_t *buf)
|
|
{
|
|
s5l_lcd_write_cmd(cmd);
|
|
while (len--) {
|
|
while (!(LCD_STATUS & 0x2));
|
|
LCD_RDATA = 0;
|
|
while (!(LCD_STATUS & 1));
|
|
*buf++ = LCD_DBUFF >> 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define s5l_lcd_set_command_mode() s5l_lcd_write_config(lcd_cmd_mode)
|
|
#define s5l_lcd_set_frame_mode() s5l_lcd_write_config(lcd_frame_mode)
|
|
|
|
static void s5l_lcd_run_seq8(void *seq8)
|
|
{
|
|
uint8_t *seq = seq8;
|
|
|
|
while (1) switch (*seq++)
|
|
{
|
|
case CMD:
|
|
{
|
|
uint8_t cmd = *seq++;
|
|
int len = *seq++;
|
|
s5l_lcd_send_cmd8(cmd, len, seq);
|
|
seq += len;
|
|
break;
|
|
}
|
|
case SLEEP:
|
|
sleep(*seq++);
|
|
break;
|
|
case END:
|
|
default:
|
|
/* bye */
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef S5L_LCD_WITH_CMDSET16
|
|
static void s5l_lcd_write_reg(uint16_t cmd, uint16_t data) ICODE_ATTR;
|
|
static void s5l_lcd_write_reg(uint16_t cmd, uint16_t data)
|
|
{
|
|
s5l_lcd_write_cmd(cmd);
|
|
s5l_lcd_write_data(data);
|
|
}
|
|
|
|
static void s5l_lcd_run_seq16(void *seq16)
|
|
{
|
|
uint16_t *seq = seq16;
|
|
int action, param;
|
|
uint16_t reg;
|
|
|
|
while (1)
|
|
{
|
|
action = *seq & 0xff;
|
|
param = *seq++ >> 8;
|
|
|
|
switch (action)
|
|
{
|
|
case CMD:
|
|
s5l_lcd_write_cmd(*seq++);
|
|
break;
|
|
case MREG:
|
|
reg = *seq++;
|
|
while (param--)
|
|
s5l_lcd_write_reg(reg++, *seq++);
|
|
break;
|
|
case SLEEP:
|
|
sleep(param);
|
|
break;
|
|
case DELAY:
|
|
udelay(param<<6);
|
|
break;
|
|
case END:
|
|
default:
|
|
/* bye */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif /* S5L_LCD_WITH_CMDSET16 */
|
|
|
|
|
|
/*** Update functions ***/
|
|
|
|
/* 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)
|
|
{
|
|
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
|
|
}
|
|
|
|
static void displaylcd_setup(int x, int y, int width, int height) ICODE_ATTR;
|
|
static void displaylcd_setup(int x, int y, int width, int height)
|
|
{
|
|
int xe = (x + width) - 1; /* max horiz */
|
|
int ye = (y + height) - 1; /* max vert */
|
|
|
|
s5l_lcd_set_command_mode();
|
|
|
|
#ifdef S5L_LCD_WITH_CMDSET16
|
|
if (lcd_info->cmdset == LCD_CMDSET_16BIT)
|
|
{
|
|
s5l_lcd_write_reg(R_HORIZ_ADDR_START_POS, x);
|
|
s5l_lcd_write_reg(R_HORIZ_ADDR_END_POS, xe);
|
|
s5l_lcd_write_reg(R_VERT_ADDR_START_POS, y);
|
|
s5l_lcd_write_reg(R_VERT_ADDR_END_POS, ye);
|
|
|
|
s5l_lcd_write_reg(R_HORIZ_GRAM_ADDR_SET, x);
|
|
s5l_lcd_write_reg(R_VERT_GRAM_ADDR_SET, y);
|
|
|
|
s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
uint8_t col[] = { x >> 8, x & 0xff, xe >> 8, xe & 0xff };
|
|
uint8_t row[] = { y >> 8, y & 0xff, ye >> 8, ye & 0xff };
|
|
s5l_lcd_send_cmd8(R_COLUMN_ADDR_SET, 4, col);
|
|
s5l_lcd_send_cmd8(R_ROW_ADDR_SET, 4, row);
|
|
|
|
s5l_lcd_write_cmd(R_MEMORY_WRITE);
|
|
}
|
|
}
|
|
|
|
static void displaylcd_dma(int pixels) ICODE_ATTR;
|
|
static void displaylcd_dma(int pixels)
|
|
{
|
|
s5l_lcd_set_frame_mode();
|
|
commit_dcache();
|
|
dmac_ch_queue(&lcd_dma_ch, lcd_dblbuf,
|
|
(void*)S5L8702_DADDR_PERI_LCD_WR, pixels*2, NULL);
|
|
}
|
|
|
|
// TODO: wait if there is a DMA transfer in progress
|
|
static void displaylcd_wait_dma(void) ICODE_ATTR;
|
|
static void displaylcd_wait_dma(void)
|
|
{
|
|
while (dmac_ch_running(&lcd_dma_ch))
|
|
yield();
|
|
}
|
|
|
|
/* 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 pixels = width * height;
|
|
fb_data* p = FBADDR(x,y);
|
|
uint16_t* out = lcd_dblbuf[0];
|
|
|
|
/* FIXME: ISR()->panicf()->lcd_update() blocks forever */
|
|
mutex_lock(&lcd_mutex);
|
|
if (lcd_ispowered)
|
|
{
|
|
// XXX: We contemplate the case where the LCD has entered SLEEP,
|
|
// the s5l_lcd_set_command_mode() writes LCD_CONFIG
|
|
// XXX: It is also possible that if the LCD clockgate has been disabled,
|
|
// the LCD_CONFIG = xxx may still be blocked by the while(status == ok),
|
|
// since it does not check the same status bit as when writing command/data
|
|
|
|
displaylcd_wait_dma();
|
|
|
|
displaylcd_setup(x, y, width, height);
|
|
|
|
/* Copy display bitmap to hardware */
|
|
if (LCD_WIDTH == width) {
|
|
/* Write all lines at once */
|
|
memcpy(out, p, pixels * 2);
|
|
} else {
|
|
do {
|
|
/* Write a single line */
|
|
memcpy(out, p, width * 2);
|
|
p += LCD_WIDTH;
|
|
out += width;
|
|
} while (--height);
|
|
}
|
|
|
|
displaylcd_dma(pixels);
|
|
}
|
|
mutex_unlock(&lcd_mutex);
|
|
}
|
|
|
|
/* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
|
|
extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
|
|
uint16_t* outbuf,
|
|
int width,
|
|
int stride);
|
|
|
|
/* Blit a YUV bitmap directly to the LCD */
|
|
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) ICODE_ATTR;
|
|
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 int z;
|
|
unsigned char const * yuv_src[3];
|
|
|
|
width = (width + 1) & ~1; /* ensure width is even */
|
|
|
|
int pixels = width * height;
|
|
uint16_t* out = lcd_dblbuf[0];
|
|
|
|
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]);
|
|
|
|
/* TODO: ISR()->panicf()->lcd_update() blocks forever */
|
|
mutex_lock(&lcd_mutex);
|
|
if (lcd_ispowered)
|
|
{
|
|
displaylcd_wait_dma();
|
|
|
|
displaylcd_setup(x, y, width, height);
|
|
|
|
height >>= 1;
|
|
|
|
do {
|
|
lcd_write_yuv420_lines(yuv_src, out, width, stride);
|
|
yuv_src[0] += stride << 1;
|
|
yuv_src[1] += stride >> 1; /* Skip down one chroma line */
|
|
yuv_src[2] += stride >> 1;
|
|
out += width << 1;
|
|
} while (--height);
|
|
|
|
displaylcd_dma(pixels);
|
|
}
|
|
mutex_unlock(&lcd_mutex);
|
|
}
|
|
|
|
|
|
/*** hardware configuration ***/
|
|
|
|
int lcd_default_contrast(void)
|
|
{
|
|
return 0x1f;
|
|
}
|
|
|
|
void lcd_set_contrast(int val)
|
|
{
|
|
(void)val;
|
|
}
|
|
|
|
void lcd_set_invert_display(bool yesno)
|
|
{
|
|
(void)yesno;
|
|
}
|
|
|
|
void lcd_set_flip(bool yesno)
|
|
{
|
|
(void)yesno;
|
|
}
|
|
|
|
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
|
|
bool lcd_active(void)
|
|
{
|
|
return lcd_ispowered;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_LCD_SHUTDOWN) || defined(HAVE_LCD_SLEEP)
|
|
static void lcd_powersave(void)
|
|
{
|
|
mutex_lock(&lcd_mutex);
|
|
|
|
displaylcd_wait_dma();
|
|
// XXX: Do not change modes while data is being sent
|
|
s5l_lcd_set_command_mode();
|
|
lcd_run_seq(lcd_info->seq_sleep);
|
|
|
|
lcd_target_enable_clocks(false);
|
|
|
|
lcd_ispowered = false;
|
|
|
|
mutex_unlock(&lcd_mutex);
|
|
}
|
|
#endif /* HAVE_LCD_SHUTDOWN || HAVE_LCD_SLEEP */
|
|
|
|
#ifdef HAVE_LCD_SHUTDOWN
|
|
void lcd_shutdown(void)
|
|
{
|
|
backlight_hw_kill(); /* Kill the backlight, instantly. */
|
|
lcd_powersave();
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LCD_SLEEP
|
|
void lcd_sleep(void)
|
|
{
|
|
lcd_powersave();
|
|
}
|
|
|
|
void lcd_awake(void)
|
|
{
|
|
mutex_lock(&lcd_mutex);
|
|
|
|
lcd_target_enable_clocks(true);
|
|
// XXX: Do not change modes while data is being sent
|
|
s5l_lcd_set_command_mode();
|
|
lcd_run_seq(lcd_info->seq_awake);
|
|
lcd_ispowered = true; // XXX: we have to put the lcd_ispowered before the lcd_update()
|
|
|
|
lcd_update(); // XXX: update the display and wait for the update to finish before returning,
|
|
|
|
displaylcd_wait_dma(); // we should really do sleep_out + lcd_update + display_on,
|
|
// we can put a command in the sequence to do it
|
|
|
|
mutex_unlock(&lcd_mutex);
|
|
send_event(LCD_EVENT_ACTIVATION, NULL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef S5L_LCD_WITH_READID
|
|
// TODO: protect it with mutex and while(dma) if you want to call it from other places (e.g. DEBUG)
|
|
void lcd_read_display_id(int mpuiface, uint8_t *lcd_id)
|
|
{
|
|
s5l_lcd_write_config(
|
|
(mpuiface == LCD_MPUIFACE_SERIAL) ? LCD_MODE_S8 : LCD_MODE_P8);
|
|
s5l_lcd_recv_cmd8(4, 4, lcd_id);
|
|
}
|
|
#endif
|
|
|
|
/* LCD init */
|
|
void lcd_init_device(void)
|
|
{
|
|
mutex_init(&lcd_mutex);
|
|
|
|
lcd_target_enable_clocks(true);
|
|
#if defined(IPOD_6G) || defined(IPOD_NANO3G)
|
|
LCD_PHTIME = 0x33;
|
|
#elif defined(IPOD_NANO4G) && defined(BOOTLOADER)
|
|
cg16_config(&CG16_LCD, true, CG16_SEL_PLL0, 16, 1, 0x0);
|
|
s5l_lcd_write_config(LCD_MODE_S9);
|
|
cg16_config(&CG16_LCD, false, CG16_SEL_PLL0, 16, 1, 0x0);
|
|
LCD_PHTIME = 0x11;
|
|
#endif
|
|
|
|
lcd_info = lcd_target_get_info();
|
|
lcd_type = lcd_info->lcd_type;
|
|
|
|
/* select mode to send commands */
|
|
#ifdef S5L_LCD_WITH_CMDSET16
|
|
if (lcd_info->cmdset == LCD_CMDSET_16BIT)
|
|
{
|
|
lcd_cmd_mode = LCD_MODE_P18;
|
|
lcd_run_seq = s5l_lcd_run_seq16;
|
|
}
|
|
else /* LCD_CMDSET_8BIT */
|
|
#endif
|
|
{
|
|
if (lcd_info->mpuiface == LCD_MPUIFACE_SERIAL)
|
|
lcd_cmd_mode = LCD_MODE_S8;
|
|
else
|
|
lcd_cmd_mode = LCD_MODE_P8;
|
|
lcd_run_seq = s5l_lcd_run_seq8;
|
|
}
|
|
|
|
/* select mode to send RGB565 data */
|
|
if (lcd_info->mpuiface == LCD_MPUIFACE_PAR9)
|
|
lcd_frame_mode = LCD_MODE_P9;
|
|
else if (lcd_info->mpuiface == LCD_MPUIFACE_PAR18)
|
|
lcd_frame_mode = LCD_MODE_P16;
|
|
else /* LCD_MPUIFACE_SERIAL */
|
|
lcd_frame_mode = LCD_MODE_S9;
|
|
|
|
s5l_lcd_set_command_mode();
|
|
|
|
/* Configure DMA channel */ // TODO: this right after mutex_init()
|
|
dmac_ch_init(&lcd_dma_ch, &lcd_dma_ch_cfg);
|
|
|
|
#ifdef BOOTLOADER
|
|
lcd_run_seq(lcd_info->seq_init);
|
|
#endif
|
|
|
|
lcd_ispowered = true;
|
|
}
|