| /* |
| * 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; |
| } |
| |
| |
| |