blob: a4e21cba1a1564fcc0cf0a6966c560cb5d7462b3 [file] [log] [blame]
/*
* Copyright (C) 2015 Marvell International Ltd.
* All rights reserved.
*
* 2015-03-12 adapted from original version for PXA1826
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include <asm/arch/pxa_dma.h>
#include <asm/arch/pxa1826fb.h>
#include <asm/arch/cpu.h>
#ifdef CONFIG_NT35510_PANEL
#define DISP_RD_TIMING 0x15151515
#define DISP_WR_TIMING 0x01010100
#endif
#ifdef CONFIG_P502_PANEL
#define DISP_RD_TIMING 0x15151515
#define DISP_WR_TIMING 0x01010100
#endif
#ifdef CONFIG_SSD1963_PANEL
#define DISP_RD_TIMING 0x20202020
#define DISP_WR_TIMING 0x05050505
#endif
#define LCD_BUS_WIDTH_8BIT 0
#define LCD_BUS_WIDTH_16BIT 1
#ifdef CONFIG_LCD_BUS8BIT
static unsigned int lcd_bus_width = LCD_BUS_WIDTH_8BIT;
#else
static unsigned int lcd_bus_width = LCD_BUS_WIDTH_16BIT;
#endif
static int lcd_stat = 0;
static int *lcd_buff;
static void lcd_send_pixel(unsigned int addr, unsigned int len,
unsigned int block_size, unsigned int mode, unsigned int width);
/* pxa1826 lcd controller configuration */
static struct lcd_fifo_ctrl disp_fifo_ctrl = {
.read_order = 1,
.bus_width = 0,
};
/* pxa1826 lcd info configuration */
static struct pxa1826fb_info disp_info = {
.reg_base = LCD_REG_BASE,
.fifo_ctrl = &disp_fifo_ctrl,
.enabled = 1
};
struct pxa1826fb_mach_info disp_mach_info = {
.info = &disp_info,
.xres = DEFAULT_FB_XRES,
.yres = DEFAULT_FB_YRES
};
unsigned int disp_bus_width(void)
{
return lcd_bus_width;
}
static void lcd_clk_init(void)
{
unsigned int val = readfb(LCD_CLK_REG_BASE+0x70);
val |= (0x1<<2);
mdelay(1);
val &= ~LCD_FNUC_CLK_MASK;
/* set lcd function clock */
val |= LCD_FUNC_CLK(0x3);
/* enable lcd function/apb clock */
val |= 0x3;
/* release lcd reset */
val &= ~(0x1<<2);
writefb(val, LCD_CLK_REG_BASE+0x70);
val = readfb(LCD_CLK_REG_BASE+0x74);
val |= (0x1<<2);
mdelay(1);
val &= ~LCD_FNUC_CLK_MASK;
/* set lcd function clock */
val |= LCD_FUNC_CLK(0x3);
/* enable lcd function/apb clock */
val |= 0x3;
/* release lcd reset */
val &= ~(0x1<<2);
writefb(val, LCD_CLK_REG_BASE+0x74);
}
static void lcd_set_fifo_control(unsigned int mode, unsigned int width,
unsigned int block_len, uint8_t entries)
{
unsigned int val = 0;
block_len = block_len - 1;
if (mode == 0) {
val = (mode<<20)|(block_len<<11)|
(width<<3)|(entries<<1)|0x1;
} else {
val = (mode<<20)|(width<<3)|
(DISP_FIFO_DMA_THRESHOLD<<24)|
(DISP_FIFO_DMA_BURSTSIZE<<5)|
(entries<<1)|0x1;
}
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
}
static void lcd_fifo_reset(void)
{
unsigned int val = readfb(LCD_REG_BASE+FIFO_CONTROL_OFFSET)|(0x1<<21);
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
udelay(1);
val = readfb(LCD_REG_BASE+FIFO_CONTROL_OFFSET) & (~(0x1<<21));
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
}
static void lcd_set_wr_params(void)
{
writefb(DISP_WR_TIMING, LCD_REG_BASE+WR_PARAMS_OFFSET);
}
static void lcd_set_rd_params(void)
{
writefb(DISP_RD_TIMING, LCD_REG_BASE+RD_PARAMS_OFFSET);
}
static void lcd_set_dev_select(void)
{
writefb(0x101, LCD_REG_BASE+DEVICE_SELECT_OFFSET);
}
void rect_fill(void *addr, int left, int up, int right,
int down, int width, unsigned int color)
{
int i, j;
for (j = up; j < down; j++)
for (i = left; i < right; i++)
*((unsigned short *)addr + j * width + i) = color;
}
#ifdef CONFIG_LCD_LOGO
void lcd_draw_logo(void *addr)
{
#ifdef CONFIG_NT35510_PANEL
nt35510_draw_logo(addr);
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_draw_logo(addr);
#endif
}
void lcd_show_logo()
{
pxa1826_fb_start();
memset(lcd_buff, 0x00, DEFAULT_FB_SIZE);
lcd_draw_logo(lcd_buff);
flush_cache((unsigned long)lcd_buff, DEFAULT_FB_SIZE);
lcd_send_pixel((unsigned long)lcd_buff, DEFAULT_FB_SIZE,
DEFAULT_FB_BURST, 1, lcd_bus_width);
}
#endif
static void lcd_init(struct pxa1826fb_mach_info *info)
{
(void)info;
/*
* workaround for LCD pinmux
* enable EMMC5.0 clock and bypass in EMMC PHY
*/
if (cpu_is_pxa1826_z3()) {
writefb(0x80000009, PMUA_EM_CLK_RES_CTRL);
writefb(0x0f00f000, EMMC_PHY_PAD_CONTROL);
} else if (cpu_is_pxa1826_a0())
writefb(0x80ff0009, PMUA_EM_CLK_RES_CTRL);
/* set apb bus clock to 52M */
writefb(0x1, 0xd4051050);
/* fifo reset */
lcd_fifo_reset();
/* dev select register */
lcd_set_dev_select();
lcd_set_wr_params();
lcd_set_rd_params();
pxa1826_panel_init();
#ifdef CONFIG_LCD_LOGO
lcd_show_logo();
#endif
mdelay(1);
pxa1826_bl_start();
}
void pxa1826_fb_init(void)
{
lcd_buff = malloc(DEFAULT_FB_SIZE);
lcd_clk_init();
lcd_init(&disp_mach_info);
printf("LCD RGB565 mode\r\n");
}
void pxa1826_panel_init()
{
#ifdef CONFIG_NT35510_PANEL
nt35510_panel_init();
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_panel_init();
#endif
}
void pxa1826_fb_start(void)
{
#ifdef CONFIG_NT35510_PANEL
nt35510_fb_start();
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_fb_start();
#endif
}
void pxa1826_bl_start(void)
{
#ifdef CONFIG_NT35510_PANEL
nt35510_bl_start();
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_bl_start();
#endif
}
void lcd_panel_on(int on_off)
{
if (on_off) {
pxa1826_panel_init();
lcd_show_battery_stage(lcd_stat);
pxa1826_bl_start();
printf("LCD panel on\r\n");
} else {
#ifdef CONFIG_NT35510_PANEL
nt35510_panel_off();
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_panel_off();
#endif
printf("LCD panel off\r\n");
}
}
/*
* send commands Only.
*/
void lcd_cpu_setup(unsigned int *addr, unsigned int len,
unsigned int channel, unsigned int type,
unsigned int entry, unsigned int width)
{
unsigned int i;
unsigned int div;
lcd_fifo_reset();
lcd_set_dev_select();
lcd_set_fifo_control(0, width, len, entry);
if (width == LCD_BUS_WIDTH_8BIT)
div = entry+1;
else
div = (entry+1)*2;
/*
* There is HW issue that we MUST set block length as 1
* when send one 8bit data
*/
if ((len == 2) && (type == 1))
len = len - 1;
for (i = 0; i < (len-1)/div+1; i = i+1) {
if (type == 0)
writefb(addr[i], LCD_REG_BASE+FIFO_COMMAND_OFFSET);
else if (type == 1)
writefb(addr[i], LCD_REG_BASE+FIFO_DATA_OFFSET);
}
}
/*
* send pixel data.
*/
void lcd_controller_dma_setup(int channel, int width, int entry)
{
lcd_fifo_reset();
lcd_set_dev_select();
lcd_set_fifo_control(1, width, 0, entry);
}
static void lcd_cpu_trans(void)
{
unsigned int val = readfb(LCD_REG_BASE+INT_MASK_OFFSET)|(0x1);
writefb(val, LCD_REG_BASE+INT_MASK_OFFSET);
val = readfb(LCD_REG_BASE+FIFO_CONTROL_OFFSET)|(0x1<<19);
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
while (!((readfb(LCD_REG_BASE+STATUS_OFFSET))&0x1))
udelay(1);
val = readfb(LCD_REG_BASE+STATUS_OFFSET)|(0x1);
writefb(val, LCD_REG_BASE+STATUS_OFFSET);
val = readfb(LCD_REG_BASE+FIFO_CONTROL_OFFSET) & (~(0x1<<19));
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
}
static void lcd_dma_trans(void)
{
dmac_start_transfer(DMA_LCD1_CHANNEL);
unsigned int timeout = 2000000;
unsigned int i = 0;
do {
for (i = 0; i < 500; i++)
nop();
if (dmac_read_dcsr(DMA_LCD1_CHANNEL) & DCSR_STOPSTATE)
break;
timeout--;
if (!timeout) {
printf("LCD DMA transfer timeout!!!\r\n");
break;
}
} while (1);
}
static void lcd_controller_cpu_enable(void)
{
unsigned int val = readfb(LCD_REG_BASE+FIFO_CONTROL_OFFSET)|(0x1<<19);
writefb(val, LCD_REG_BASE+FIFO_CONTROL_OFFSET);
}
static void lcd_send_cmd_8bit(unsigned int cmd)
{
unsigned int t = cmd;
lcd_cpu_setup(&t, 2, 1, 0, 1, 0);
lcd_cpu_trans();
}
static void lcd_send_data_8bit(unsigned int data)
{
unsigned int t = data;
lcd_cpu_setup(&t, 2, 1, 1, 0, 0);
lcd_cpu_trans();
}
static void lcd_send_cmd_16bit(unsigned int cmd)
{
unsigned int t = cmd;
lcd_cpu_setup(&t, 2, 1, 0, 0, 1);
lcd_cpu_trans();
}
static void lcd_send_data_16bit(unsigned int data)
{
unsigned int t = data;
lcd_cpu_setup(&t, 2, 1, 1, 0, 1);
lcd_cpu_trans();
}
void lcd_send_block_data(unsigned int *addr, int size, int width)
{
lcd_cpu_setup(addr, size, 1, 1, 0, width);
lcd_cpu_trans();
}
void lcd_send_cmd(unsigned int cmd)
{
if (lcd_bus_width == 0)
lcd_send_cmd_8bit(cmd);
else
lcd_send_cmd_16bit(cmd);
}
void lcd_send_data(unsigned int data)
{
if (lcd_bus_width == 0)
lcd_send_data_8bit(data);
else
lcd_send_data_16bit(data);
}
static void lcd_map_channel(void)
{
dmac_map_device_to_channel(13, DMA_LCD0_CHANNEL);
dmac_map_device_to_channel(14, DMA_LCD1_CHANNEL);
}
void lcd_dma_user_aligment(void)
{
dmac_user_aligment(DMA_LCD1_CHANNEL);
}
static void lcd_send_pixel(unsigned int addr, unsigned int len,
unsigned int block_size, unsigned int mode,
unsigned int width)
{
if (mode == 0) {/* cpu mode */
unsigned int i = 0;
for (i = 0; i < len/block_size; i++) {
lcd_cpu_setup((unsigned int *)addr,
block_size, 1, 1, 3, width);
lcd_cpu_trans();
}
} else if (mode == 1) {
unsigned int *desc_tx = NULL;
unsigned int desc_len = (len/block_size)*16+0x10;
unsigned int p1 = (unsigned int)malloc(desc_len);
unsigned int p = p1;
p1 = p1 & 0xFFFFFFF0;
p1 += 0x10;
desc_tx = (unsigned int *)p1;
lcd_map_channel();
lcd_dma_user_aligment();
unsigned int burst_size = DCMD_BURST64;
unsigned int cmd_width = DCMD_WIDTH4;
unsigned int tx_cmd = block_size|(cmd_width)|
(burst_size)|(1<<28)|(1<<31);
/* lcd controller as dma mode */
lcd_controller_dma_setup(1, width, 3);
unsigned int i;
unsigned int src, des, desc_curr, desc_next = 0;
for (i = 0; i < len/block_size-1; i++) {
src = (unsigned int)(addr+block_size*i);
des = (unsigned int)(LCD_REG_BASE+FIFO_DATA_OFFSET);
desc_curr = (unsigned int)(desc_tx+i*4);
desc_next = (unsigned int)(desc_tx+i*4+4);
config_descriptor((unsigned int *)desc_curr,
(unsigned int *)desc_next,
src, des, tx_cmd, 0);
}
src = (unsigned int)(addr+block_size*(len/block_size-1));
des = (unsigned int)(LCD_REG_BASE+FIFO_DATA_OFFSET);
desc_curr = (unsigned int)(desc_tx+4*(len/block_size-1));
desc_next = 0;
config_descriptor((unsigned int *)desc_curr,
(unsigned int *)desc_next,
src, des, tx_cmd, 1);
load_descriptor((struct dma_descp *)desc_tx, DMA_LCD1_CHANNEL);
/* flush cache */
flush_dcache_all();
lcd_controller_cpu_enable();
lcd_dma_trans();
free((void *)p);
} else {
printf("LCD pixel transfer mode error!!!\r\n");
return;
}
}
void lcd_show_battery_stage(int stage)
{
if ((stage > BATTERY_STAGE_RES) | (stage < -1)) {
printf("Incorrect stage input!!!\r\n");
return;
}
pxa1826_fb_start();
memset(lcd_buff, 0x00, DEFAULT_FB_SIZE);
#ifdef CONFIG_NT35510_PANEL
nt35510_show_battery_stage((void *)lcd_buff, stage);
#endif
#ifdef CONFIG_SSD1963_PANEL
ssd1963_show_battery_stage((void *)lcd_buff, stage);
#endif
flush_cache((unsigned long)lcd_buff, DEFAULT_FB_SIZE);
lcd_send_pixel((unsigned long)lcd_buff, DEFAULT_FB_SIZE, 0x1E00, 1, lcd_bus_width);
}
unsigned short pxa1826fb_read(unsigned short addr)
{
unsigned short val;
unsigned int regaddr;
unsigned int a0;
unsigned int dev;
unsigned int rst;
unsigned int rwo;
unsigned int regdata;
regdata = readfb(LCD_REG_BASE+DEVICE_SELECT_OFFSET);
regdata &= ~0x3;
writefb(regdata, LCD_REG_BASE+DEVICE_SELECT_OFFSET);
regdata |= 0x2;
writefb(regdata, LCD_REG_BASE+DEVICE_SELECT_OFFSET);
if (lcd_bus_width == LCD_BUS_WIDTH_8BIT)
addr >>= 8;
writefb(0x1, LCD_REG_BASE+RD_INT_STAT_OFFSET);
regaddr = addr;
a0 = 2;
dev = 1;
rst = 0;
rwo = 0;
regdata = regaddr << 0 | a0 << 16 | dev << 20 | rst << 24 | rwo << 28;
writefb(regdata, LCD_REG_BASE+READ_OPER_OFFSET);
udelay(10);
rst = 1;
regdata = regaddr << 0 | a0 << 16 | dev << 20 | rst << 24 | rwo << 28;
writefb(regdata, LCD_REG_BASE+READ_OPER_OFFSET);
while (!((readfb(LCD_REG_BASE+RD_INT_STAT_OFFSET))&0x1))
udelay(1);
if (lcd_bus_width == LCD_BUS_WIDTH_8BIT) {
writefb(0x1, LCD_REG_BASE+RD_INT_STAT_OFFSET);
regaddr = 0;
a0 = 2;
dev = 1;
rst = 0;
rwo = 0;
regdata = regaddr << 0 | a0 << 16 | dev << 20 |
rst << 24 | rwo << 28;
writefb(regdata, LCD_REG_BASE+READ_OPER_OFFSET);
udelay(10);
rst = 1;
regdata = regaddr << 0 | a0 << 16 | dev << 20 |
rst << 24 | rwo << 28;
writefb(regdata, LCD_REG_BASE+READ_OPER_OFFSET);
while (!((readfb(LCD_REG_BASE+RD_INT_STAT_OFFSET))&0x1))
udelay(1);
}
regaddr = LCD_REG_BASE+CS1_RD_DATA_OFFSET;
val = (unsigned short)((readfb(regaddr))&0xFFFF);
return val;
}