xref: /openbmc/u-boot/drivers/video/da8xx-fb.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
213402868SStefano Babic /*
313402868SStefano Babic  * Porting to u-boot:
413402868SStefano Babic  *
513402868SStefano Babic  * (C) Copyright 2011
613402868SStefano Babic  * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
713402868SStefano Babic  *
813402868SStefano Babic  * Copyright (C) 2008-2009 MontaVista Software Inc.
913402868SStefano Babic  * Copyright (C) 2008-2009 Texas Instruments Inc
1013402868SStefano Babic  *
1113402868SStefano Babic  * Based on the LCD driver for TI Avalanche processors written by
1213402868SStefano Babic  * Ajay Singh and Shalom Hai.
1313402868SStefano Babic  */
1413402868SStefano Babic 
1513402868SStefano Babic #include <common.h>
16963be689SNiko Mauno #include <memalign.h>
1713402868SStefano Babic #include <video_fb.h>
1813402868SStefano Babic #include <linux/list.h>
1913402868SStefano Babic #include <linux/fb.h>
2013402868SStefano Babic 
211221ce45SMasahiro Yamada #include <linux/errno.h>
2213402868SStefano Babic #include <asm/io.h>
2313402868SStefano Babic #include <asm/arch/hardware.h>
2413402868SStefano Babic 
2513402868SStefano Babic #include "videomodes.h"
260017f9eeSHeiko Schocher #include "da8xx-fb.h"
2713402868SStefano Babic 
28765f2f08SHeiko Schocher #if !defined(DA8XX_LCD_CNTL_BASE)
29765f2f08SHeiko Schocher #define DA8XX_LCD_CNTL_BASE	DAVINCI_LCD_CNTL_BASE
30765f2f08SHeiko Schocher #endif
31765f2f08SHeiko Schocher 
3213402868SStefano Babic #define DRIVER_NAME "da8xx_lcdc"
3313402868SStefano Babic 
34765f2f08SHeiko Schocher #define LCD_VERSION_1	1
35765f2f08SHeiko Schocher #define LCD_VERSION_2	2
36765f2f08SHeiko Schocher 
3713402868SStefano Babic /* LCD Status Register */
3813402868SStefano Babic #define LCD_END_OF_FRAME1		(1 << 9)
3913402868SStefano Babic #define LCD_END_OF_FRAME0		(1 << 8)
4013402868SStefano Babic #define LCD_PL_LOAD_DONE		(1 << 6)
4113402868SStefano Babic #define LCD_FIFO_UNDERFLOW		(1 << 5)
4213402868SStefano Babic #define LCD_SYNC_LOST			(1 << 2)
4313402868SStefano Babic 
4413402868SStefano Babic /* LCD DMA Control Register */
4513402868SStefano Babic #define LCD_DMA_BURST_SIZE(x)		((x) << 4)
4613402868SStefano Babic #define LCD_DMA_BURST_1			0x0
4713402868SStefano Babic #define LCD_DMA_BURST_2			0x1
4813402868SStefano Babic #define LCD_DMA_BURST_4			0x2
4913402868SStefano Babic #define LCD_DMA_BURST_8			0x3
5013402868SStefano Babic #define LCD_DMA_BURST_16		0x4
51765f2f08SHeiko Schocher #define LCD_V1_END_OF_FRAME_INT_ENA	(1 << 2)
52765f2f08SHeiko Schocher #define LCD_V2_END_OF_FRAME0_INT_ENA	(1 << 8)
53765f2f08SHeiko Schocher #define LCD_V2_END_OF_FRAME1_INT_ENA	(1 << 9)
5413402868SStefano Babic #define LCD_DUAL_FRAME_BUFFER_ENABLE	(1 << 0)
5513402868SStefano Babic 
56765f2f08SHeiko Schocher #define LCD_V2_TFT_24BPP_MODE		(1 << 25)
57765f2f08SHeiko Schocher #define LCD_V2_TFT_24BPP_UNPACK		(1 << 26)
58765f2f08SHeiko Schocher 
5913402868SStefano Babic /* LCD Control Register */
6013402868SStefano Babic #define LCD_CLK_DIVISOR(x)		((x) << 8)
6113402868SStefano Babic #define LCD_RASTER_MODE			0x01
6213402868SStefano Babic 
6313402868SStefano Babic /* LCD Raster Control Register */
6413402868SStefano Babic #define LCD_PALETTE_LOAD_MODE(x)	((x) << 20)
6513402868SStefano Babic #define PALETTE_AND_DATA		0x00
6613402868SStefano Babic #define PALETTE_ONLY			0x01
6713402868SStefano Babic #define DATA_ONLY			0x02
6813402868SStefano Babic 
6913402868SStefano Babic #define LCD_MONO_8BIT_MODE		(1 << 9)
7013402868SStefano Babic #define LCD_RASTER_ORDER		(1 << 8)
7113402868SStefano Babic #define LCD_TFT_MODE			(1 << 7)
72765f2f08SHeiko Schocher #define LCD_V1_UNDERFLOW_INT_ENA	(1 << 6)
73765f2f08SHeiko Schocher #define LCD_V2_UNDERFLOW_INT_ENA	(1 << 5)
74765f2f08SHeiko Schocher #define LCD_V1_PL_INT_ENA		(1 << 4)
75765f2f08SHeiko Schocher #define LCD_V2_PL_INT_ENA		(1 << 6)
7613402868SStefano Babic #define LCD_MONOCHROME_MODE		(1 << 1)
7713402868SStefano Babic #define LCD_RASTER_ENABLE		(1 << 0)
7813402868SStefano Babic #define LCD_TFT_ALT_ENABLE		(1 << 23)
7913402868SStefano Babic #define LCD_STN_565_ENABLE		(1 << 24)
80765f2f08SHeiko Schocher #define LCD_V2_DMA_CLK_EN		(1 << 2)
81765f2f08SHeiko Schocher #define LCD_V2_LIDD_CLK_EN		(1 << 1)
82765f2f08SHeiko Schocher #define LCD_V2_CORE_CLK_EN		(1 << 0)
83765f2f08SHeiko Schocher #define LCD_V2_LPP_B10			26
84765f2f08SHeiko Schocher #define LCD_V2_TFT_24BPP_MODE		(1 << 25)
85765f2f08SHeiko Schocher #define LCD_V2_TFT_24BPP_UNPACK		(1 << 26)
8613402868SStefano Babic 
8713402868SStefano Babic /* LCD Raster Timing 2 Register */
8813402868SStefano Babic #define LCD_AC_BIAS_TRANSITIONS_PER_INT(x)	((x) << 16)
8913402868SStefano Babic #define LCD_AC_BIAS_FREQUENCY(x)		((x) << 8)
9013402868SStefano Babic #define LCD_SYNC_CTRL				(1 << 25)
9113402868SStefano Babic #define LCD_SYNC_EDGE				(1 << 24)
9213402868SStefano Babic #define LCD_INVERT_PIXEL_CLOCK			(1 << 22)
9313402868SStefano Babic #define LCD_INVERT_LINE_CLOCK			(1 << 21)
9413402868SStefano Babic #define LCD_INVERT_FRAME_CLOCK			(1 << 20)
9513402868SStefano Babic 
96765f2f08SHeiko Schocher /* Clock registers available only on Version 2 */
97765f2f08SHeiko Schocher #define  LCD_CLK_MAIN_RESET			(1 << 3)
9813402868SStefano Babic /* LCD Block */
9913402868SStefano Babic struct da8xx_lcd_regs {
10013402868SStefano Babic 	u32	revid;
10113402868SStefano Babic 	u32	ctrl;
10213402868SStefano Babic 	u32	stat;
10313402868SStefano Babic 	u32	lidd_ctrl;
10413402868SStefano Babic 	u32	lidd_cs0_conf;
10513402868SStefano Babic 	u32	lidd_cs0_addr;
10613402868SStefano Babic 	u32	lidd_cs0_data;
10713402868SStefano Babic 	u32	lidd_cs1_conf;
10813402868SStefano Babic 	u32	lidd_cs1_addr;
10913402868SStefano Babic 	u32	lidd_cs1_data;
11013402868SStefano Babic 	u32	raster_ctrl;
11113402868SStefano Babic 	u32	raster_timing_0;
11213402868SStefano Babic 	u32	raster_timing_1;
11313402868SStefano Babic 	u32	raster_timing_2;
11413402868SStefano Babic 	u32	raster_subpanel;
11513402868SStefano Babic 	u32	reserved;
11613402868SStefano Babic 	u32	dma_ctrl;
11713402868SStefano Babic 	u32	dma_frm_buf_base_addr_0;
11813402868SStefano Babic 	u32	dma_frm_buf_ceiling_addr_0;
11913402868SStefano Babic 	u32	dma_frm_buf_base_addr_1;
12013402868SStefano Babic 	u32	dma_frm_buf_ceiling_addr_1;
121765f2f08SHeiko Schocher 	u32	resv1;
122765f2f08SHeiko Schocher 	u32	raw_stat;
123765f2f08SHeiko Schocher 	u32	masked_stat;
124765f2f08SHeiko Schocher 	u32	int_ena_set;
125765f2f08SHeiko Schocher 	u32	int_ena_clr;
126765f2f08SHeiko Schocher 	u32	end_of_int_ind;
127765f2f08SHeiko Schocher 	/* Clock registers available only on Version 2 */
128765f2f08SHeiko Schocher 	u32	clk_ena;
129765f2f08SHeiko Schocher 	u32	clk_reset;
13013402868SStefano Babic };
13113402868SStefano Babic 
13213402868SStefano Babic #define LCD_NUM_BUFFERS	1
13313402868SStefano Babic 
13413402868SStefano Babic #define WSI_TIMEOUT	50
13513402868SStefano Babic #define PALETTE_SIZE	256
13613402868SStefano Babic #define LEFT_MARGIN	64
13713402868SStefano Babic #define RIGHT_MARGIN	64
13813402868SStefano Babic #define UPPER_MARGIN	32
13913402868SStefano Babic #define LOWER_MARGIN	32
140765f2f08SHeiko Schocher #define WAIT_FOR_FRAME_DONE	true
141765f2f08SHeiko Schocher #define NO_WAIT_FOR_FRAME_DONE	false
14213402868SStefano Babic 
14313402868SStefano Babic #define calc_fbsize() (panel.plnSizeX * panel.plnSizeY * panel.gdfBytesPP)
14413402868SStefano Babic 
14513402868SStefano Babic static struct da8xx_lcd_regs *da8xx_fb_reg_base;
14613402868SStefano Babic 
14713402868SStefano Babic DECLARE_GLOBAL_DATA_PTR;
14813402868SStefano Babic 
14913402868SStefano Babic /* graphics setup */
15013402868SStefano Babic static GraphicDevice gpanel;
15113402868SStefano Babic static const struct da8xx_panel *lcd_panel;
15213402868SStefano Babic static struct fb_info *da8xx_fb_info;
15313402868SStefano Babic static int bits_x_pixel;
154765f2f08SHeiko Schocher static unsigned int lcd_revision;
155765f2f08SHeiko Schocher const struct lcd_ctrl_config *da8xx_lcd_cfg;
15613402868SStefano Babic 
lcdc_read(u32 * addr)15713402868SStefano Babic static inline unsigned int lcdc_read(u32 *addr)
15813402868SStefano Babic {
15913402868SStefano Babic 	return (unsigned int)readl(addr);
16013402868SStefano Babic }
16113402868SStefano Babic 
lcdc_write(unsigned int val,u32 * addr)16213402868SStefano Babic static inline void lcdc_write(unsigned int val, u32 *addr)
16313402868SStefano Babic {
16413402868SStefano Babic 	writel(val, addr);
16513402868SStefano Babic }
16613402868SStefano Babic 
16713402868SStefano Babic struct da8xx_fb_par {
16813402868SStefano Babic 	u32			 p_palette_base;
16913402868SStefano Babic 	unsigned char *v_palette_base;
17013402868SStefano Babic 	dma_addr_t		vram_phys;
17113402868SStefano Babic 	unsigned long		vram_size;
17213402868SStefano Babic 	void			*vram_virt;
17313402868SStefano Babic 	unsigned int		dma_start;
17413402868SStefano Babic 	unsigned int		dma_end;
17513402868SStefano Babic 	struct clk *lcdc_clk;
17613402868SStefano Babic 	int irq;
17713402868SStefano Babic 	unsigned short pseudo_palette[16];
17813402868SStefano Babic 	unsigned int palette_sz;
17913402868SStefano Babic 	unsigned int pxl_clk;
18013402868SStefano Babic 	int blank;
18113402868SStefano Babic 	int			vsync_flag;
18213402868SStefano Babic 	int			vsync_timeout;
18313402868SStefano Babic };
18413402868SStefano Babic 
18513402868SStefano Babic 
18613402868SStefano Babic /* Variable Screen Information */
18713402868SStefano Babic static struct fb_var_screeninfo da8xx_fb_var = {
18813402868SStefano Babic 	.xoffset = 0,
18913402868SStefano Babic 	.yoffset = 0,
19013402868SStefano Babic 	.transp = {0, 0, 0},
19113402868SStefano Babic 	.nonstd = 0,
19213402868SStefano Babic 	.activate = 0,
19313402868SStefano Babic 	.height = -1,
19413402868SStefano Babic 	.width = -1,
19513402868SStefano Babic 	.pixclock = 46666,	/* 46us - AUO display */
19613402868SStefano Babic 	.accel_flags = 0,
19713402868SStefano Babic 	.left_margin = LEFT_MARGIN,
19813402868SStefano Babic 	.right_margin = RIGHT_MARGIN,
19913402868SStefano Babic 	.upper_margin = UPPER_MARGIN,
20013402868SStefano Babic 	.lower_margin = LOWER_MARGIN,
20113402868SStefano Babic 	.sync = 0,
20213402868SStefano Babic 	.vmode = FB_VMODE_NONINTERLACED
20313402868SStefano Babic };
20413402868SStefano Babic 
20513402868SStefano Babic static struct fb_fix_screeninfo da8xx_fb_fix = {
20613402868SStefano Babic 	.id = "DA8xx FB Drv",
20713402868SStefano Babic 	.type = FB_TYPE_PACKED_PIXELS,
20813402868SStefano Babic 	.type_aux = 0,
20913402868SStefano Babic 	.visual = FB_VISUAL_PSEUDOCOLOR,
21013402868SStefano Babic 	.xpanstep = 0,
21113402868SStefano Babic 	.ypanstep = 1,
21213402868SStefano Babic 	.ywrapstep = 0,
21313402868SStefano Babic 	.accel = FB_ACCEL_NONE
21413402868SStefano Babic };
21513402868SStefano Babic 
21613402868SStefano Babic /* Enable the Raster Engine of the LCD Controller */
lcd_enable_raster(void)21713402868SStefano Babic static inline void lcd_enable_raster(void)
21813402868SStefano Babic {
21913402868SStefano Babic 	u32 reg;
22013402868SStefano Babic 
221765f2f08SHeiko Schocher 	/* Put LCDC in reset for several cycles */
222765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_2)
223765f2f08SHeiko Schocher 		lcdc_write(LCD_CLK_MAIN_RESET,
224765f2f08SHeiko Schocher 			   &da8xx_fb_reg_base->clk_reset);
225765f2f08SHeiko Schocher 
226765f2f08SHeiko Schocher 	udelay(1000);
227765f2f08SHeiko Schocher 	/* Bring LCDC out of reset */
228765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_2)
229765f2f08SHeiko Schocher 		lcdc_write(0,
230765f2f08SHeiko Schocher 			   &da8xx_fb_reg_base->clk_reset);
231765f2f08SHeiko Schocher 
232765f2f08SHeiko Schocher 	udelay(1000);
233765f2f08SHeiko Schocher 
23413402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl);
23513402868SStefano Babic 	if (!(reg & LCD_RASTER_ENABLE))
23613402868SStefano Babic 		lcdc_write(reg | LCD_RASTER_ENABLE,
23713402868SStefano Babic 			&da8xx_fb_reg_base->raster_ctrl);
23813402868SStefano Babic }
23913402868SStefano Babic 
24013402868SStefano Babic /* Disable the Raster Engine of the LCD Controller */
lcd_disable_raster(bool wait_for_frame_done)241765f2f08SHeiko Schocher static inline void lcd_disable_raster(bool wait_for_frame_done)
24213402868SStefano Babic {
24313402868SStefano Babic 	u32 reg;
244765f2f08SHeiko Schocher 	u32 loop_cnt = 0;
245765f2f08SHeiko Schocher 	u32 stat;
246765f2f08SHeiko Schocher 	u32 i = 0;
247765f2f08SHeiko Schocher 
248765f2f08SHeiko Schocher 	if (wait_for_frame_done)
249765f2f08SHeiko Schocher 		loop_cnt = 5000;
25013402868SStefano Babic 
25113402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl);
25213402868SStefano Babic 	if (reg & LCD_RASTER_ENABLE)
25313402868SStefano Babic 		lcdc_write(reg & ~LCD_RASTER_ENABLE,
25413402868SStefano Babic 			&da8xx_fb_reg_base->raster_ctrl);
255765f2f08SHeiko Schocher 
256765f2f08SHeiko Schocher 	/* Wait for the current frame to complete */
257765f2f08SHeiko Schocher 	do {
258765f2f08SHeiko Schocher 		if (lcd_revision == LCD_VERSION_1)
259765f2f08SHeiko Schocher 			stat = lcdc_read(&da8xx_fb_reg_base->stat);
260765f2f08SHeiko Schocher 		else
261765f2f08SHeiko Schocher 			stat = lcdc_read(&da8xx_fb_reg_base->raw_stat);
262765f2f08SHeiko Schocher 
263765f2f08SHeiko Schocher 		mdelay(1);
264765f2f08SHeiko Schocher 	} while (!(stat & 0x01) && (i++ < loop_cnt));
265765f2f08SHeiko Schocher 
266765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1)
267765f2f08SHeiko Schocher 		lcdc_write(stat, &da8xx_fb_reg_base->stat);
268765f2f08SHeiko Schocher 	else
269765f2f08SHeiko Schocher 		lcdc_write(stat, &da8xx_fb_reg_base->raw_stat);
270765f2f08SHeiko Schocher 
271765f2f08SHeiko Schocher 	if ((loop_cnt != 0) && (i >= loop_cnt)) {
272765f2f08SHeiko Schocher 		printf("LCD Controller timed out\n");
273765f2f08SHeiko Schocher 		return;
274765f2f08SHeiko Schocher 	}
27513402868SStefano Babic }
27613402868SStefano Babic 
lcd_blit(int load_mode,struct da8xx_fb_par * par)27713402868SStefano Babic static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
27813402868SStefano Babic {
27913402868SStefano Babic 	u32 start;
28013402868SStefano Babic 	u32 end;
28113402868SStefano Babic 	u32 reg_ras;
28213402868SStefano Babic 	u32 reg_dma;
283765f2f08SHeiko Schocher 	u32 reg_int;
28413402868SStefano Babic 
28513402868SStefano Babic 	/* init reg to clear PLM (loading mode) fields */
28613402868SStefano Babic 	reg_ras = lcdc_read(&da8xx_fb_reg_base->raster_ctrl);
28713402868SStefano Babic 	reg_ras &= ~(3 << 20);
28813402868SStefano Babic 
28913402868SStefano Babic 	reg_dma  = lcdc_read(&da8xx_fb_reg_base->dma_ctrl);
29013402868SStefano Babic 
29113402868SStefano Babic 	if (load_mode == LOAD_DATA) {
29213402868SStefano Babic 		start    = par->dma_start;
29313402868SStefano Babic 		end      = par->dma_end;
29413402868SStefano Babic 
29513402868SStefano Babic 		reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY);
296765f2f08SHeiko Schocher 		if (lcd_revision == LCD_VERSION_1) {
297765f2f08SHeiko Schocher 			reg_dma |= LCD_V1_END_OF_FRAME_INT_ENA;
298765f2f08SHeiko Schocher 		} else {
299765f2f08SHeiko Schocher 			reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) |
300765f2f08SHeiko Schocher 				LCD_V2_END_OF_FRAME0_INT_ENA |
301765f2f08SHeiko Schocher 				LCD_V2_END_OF_FRAME1_INT_ENA |
302765f2f08SHeiko Schocher 				LCD_V2_UNDERFLOW_INT_ENA | LCD_SYNC_LOST;
303765f2f08SHeiko Schocher 			lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set);
304765f2f08SHeiko Schocher 		}
30513402868SStefano Babic 
30613402868SStefano Babic #if (LCD_NUM_BUFFERS == 2)
30713402868SStefano Babic 		reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE;
30813402868SStefano Babic 		lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
30913402868SStefano Babic 		lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
31013402868SStefano Babic 		lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1);
31113402868SStefano Babic 		lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1);
31213402868SStefano Babic #else
31313402868SStefano Babic 		reg_dma &= ~LCD_DUAL_FRAME_BUFFER_ENABLE;
31413402868SStefano Babic 		lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
31513402868SStefano Babic 		lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
31613402868SStefano Babic 		lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1);
31713402868SStefano Babic 		lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1);
31813402868SStefano Babic #endif
31913402868SStefano Babic 
32013402868SStefano Babic 	} else if (load_mode == LOAD_PALETTE) {
32113402868SStefano Babic 		start    = par->p_palette_base;
32213402868SStefano Babic 		end      = start + par->palette_sz - 1;
32313402868SStefano Babic 
32413402868SStefano Babic 		reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY);
325765f2f08SHeiko Schocher 		if (lcd_revision == LCD_VERSION_1) {
326765f2f08SHeiko Schocher 			reg_ras |= LCD_V1_PL_INT_ENA;
327765f2f08SHeiko Schocher 		} else {
328765f2f08SHeiko Schocher 			reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) |
329765f2f08SHeiko Schocher 				LCD_V2_PL_INT_ENA;
330765f2f08SHeiko Schocher 			lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set);
331765f2f08SHeiko Schocher 		}
33213402868SStefano Babic 
33313402868SStefano Babic 		lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
33413402868SStefano Babic 		lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
33513402868SStefano Babic 	}
33613402868SStefano Babic 
33713402868SStefano Babic 	lcdc_write(reg_dma, &da8xx_fb_reg_base->dma_ctrl);
33813402868SStefano Babic 	lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl);
33913402868SStefano Babic 
34013402868SStefano Babic 	/*
34113402868SStefano Babic 	 * The Raster enable bit must be set after all other control fields are
34213402868SStefano Babic 	 * set.
34313402868SStefano Babic 	 */
34413402868SStefano Babic 	lcd_enable_raster();
34513402868SStefano Babic }
34613402868SStefano Babic 
34713402868SStefano Babic /* Configure the Burst Size of DMA */
lcd_cfg_dma(int burst_size)34813402868SStefano Babic static int lcd_cfg_dma(int burst_size)
34913402868SStefano Babic {
35013402868SStefano Babic 	u32 reg;
35113402868SStefano Babic 
35213402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->dma_ctrl) & 0x00000001;
35313402868SStefano Babic 	switch (burst_size) {
35413402868SStefano Babic 	case 1:
35513402868SStefano Babic 		reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1);
35613402868SStefano Babic 		break;
35713402868SStefano Babic 	case 2:
35813402868SStefano Babic 		reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2);
35913402868SStefano Babic 		break;
36013402868SStefano Babic 	case 4:
36113402868SStefano Babic 		reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4);
36213402868SStefano Babic 		break;
36313402868SStefano Babic 	case 8:
36413402868SStefano Babic 		reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8);
36513402868SStefano Babic 		break;
36613402868SStefano Babic 	case 16:
36713402868SStefano Babic 		reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16);
36813402868SStefano Babic 		break;
36913402868SStefano Babic 	default:
37013402868SStefano Babic 		return -EINVAL;
37113402868SStefano Babic 	}
37213402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->dma_ctrl);
37313402868SStefano Babic 
37413402868SStefano Babic 	return 0;
37513402868SStefano Babic }
37613402868SStefano Babic 
lcd_cfg_ac_bias(int period,int transitions_per_int)37713402868SStefano Babic static void lcd_cfg_ac_bias(int period, int transitions_per_int)
37813402868SStefano Babic {
37913402868SStefano Babic 	u32 reg;
38013402868SStefano Babic 
381535cce0fSRobert P. J. Day 	/* Set the AC Bias Period and Number of Transitions per Interrupt */
38213402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2) & 0xFFF00000;
38313402868SStefano Babic 	reg |= LCD_AC_BIAS_FREQUENCY(period) |
38413402868SStefano Babic 		LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int);
38513402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2);
38613402868SStefano Babic }
38713402868SStefano Babic 
lcd_cfg_horizontal_sync(int back_porch,int pulse_width,int front_porch)38813402868SStefano Babic static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width,
38913402868SStefano Babic 		int front_porch)
39013402868SStefano Babic {
39113402868SStefano Babic 	u32 reg;
39213402868SStefano Babic 
39313402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0) & 0xf;
39413402868SStefano Babic 	reg |= ((back_porch & 0xff) << 24)
39513402868SStefano Babic 	    | ((front_porch & 0xff) << 16)
39613402868SStefano Babic 	    | ((pulse_width & 0x3f) << 10);
39713402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0);
39813402868SStefano Babic }
39913402868SStefano Babic 
lcd_cfg_vertical_sync(int back_porch,int pulse_width,int front_porch)40013402868SStefano Babic static void lcd_cfg_vertical_sync(int back_porch, int pulse_width,
40113402868SStefano Babic 		int front_porch)
40213402868SStefano Babic {
40313402868SStefano Babic 	u32 reg;
40413402868SStefano Babic 
40513402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1) & 0x3ff;
40613402868SStefano Babic 	reg |= ((back_porch & 0xff) << 24)
40713402868SStefano Babic 	    | ((front_porch & 0xff) << 16)
40813402868SStefano Babic 	    | ((pulse_width & 0x3f) << 10);
40913402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1);
41013402868SStefano Babic }
41113402868SStefano Babic 
lcd_cfg_display(const struct lcd_ctrl_config * cfg)41213402868SStefano Babic static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
41313402868SStefano Babic {
41413402868SStefano Babic 	u32 reg;
415765f2f08SHeiko Schocher 	u32 reg_int;
41613402868SStefano Babic 
41713402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(LCD_TFT_MODE |
41813402868SStefano Babic 						LCD_MONO_8BIT_MODE |
41913402868SStefano Babic 						LCD_MONOCHROME_MODE);
42013402868SStefano Babic 
42113402868SStefano Babic 	switch (cfg->p_disp_panel->panel_shade) {
42213402868SStefano Babic 	case MONOCHROME:
42313402868SStefano Babic 		reg |= LCD_MONOCHROME_MODE;
42413402868SStefano Babic 		if (cfg->mono_8bit_mode)
42513402868SStefano Babic 			reg |= LCD_MONO_8BIT_MODE;
42613402868SStefano Babic 		break;
42713402868SStefano Babic 	case COLOR_ACTIVE:
42813402868SStefano Babic 		reg |= LCD_TFT_MODE;
42913402868SStefano Babic 		if (cfg->tft_alt_mode)
43013402868SStefano Babic 			reg |= LCD_TFT_ALT_ENABLE;
43113402868SStefano Babic 		break;
43213402868SStefano Babic 
43313402868SStefano Babic 	case COLOR_PASSIVE:
43413402868SStefano Babic 		if (cfg->stn_565_mode)
43513402868SStefano Babic 			reg |= LCD_STN_565_ENABLE;
43613402868SStefano Babic 		break;
43713402868SStefano Babic 
43813402868SStefano Babic 	default:
43913402868SStefano Babic 		return -EINVAL;
44013402868SStefano Babic 	}
44113402868SStefano Babic 
44213402868SStefano Babic 	/* enable additional interrupts here */
443765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1) {
444765f2f08SHeiko Schocher 		reg |= LCD_V1_UNDERFLOW_INT_ENA;
445765f2f08SHeiko Schocher 	} else {
446765f2f08SHeiko Schocher 		reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) |
447765f2f08SHeiko Schocher 			LCD_V2_UNDERFLOW_INT_ENA;
448765f2f08SHeiko Schocher 		lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set);
449765f2f08SHeiko Schocher 	}
45013402868SStefano Babic 
45113402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl);
45213402868SStefano Babic 
45313402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2);
45413402868SStefano Babic 
45513402868SStefano Babic 	if (cfg->sync_ctrl)
45613402868SStefano Babic 		reg |= LCD_SYNC_CTRL;
45713402868SStefano Babic 	else
45813402868SStefano Babic 		reg &= ~LCD_SYNC_CTRL;
45913402868SStefano Babic 
46013402868SStefano Babic 	if (cfg->sync_edge)
46113402868SStefano Babic 		reg |= LCD_SYNC_EDGE;
46213402868SStefano Babic 	else
46313402868SStefano Babic 		reg &= ~LCD_SYNC_EDGE;
46413402868SStefano Babic 
46513402868SStefano Babic 	if (cfg->invert_line_clock)
46613402868SStefano Babic 		reg |= LCD_INVERT_LINE_CLOCK;
46713402868SStefano Babic 	else
46813402868SStefano Babic 		reg &= ~LCD_INVERT_LINE_CLOCK;
46913402868SStefano Babic 
47013402868SStefano Babic 	if (cfg->invert_frm_clock)
47113402868SStefano Babic 		reg |= LCD_INVERT_FRAME_CLOCK;
47213402868SStefano Babic 	else
47313402868SStefano Babic 		reg &= ~LCD_INVERT_FRAME_CLOCK;
47413402868SStefano Babic 
47513402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2);
47613402868SStefano Babic 
47713402868SStefano Babic 	return 0;
47813402868SStefano Babic }
47913402868SStefano Babic 
lcd_cfg_frame_buffer(struct da8xx_fb_par * par,u32 width,u32 height,u32 bpp,u32 raster_order)48013402868SStefano Babic static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
48113402868SStefano Babic 		u32 bpp, u32 raster_order)
48213402868SStefano Babic {
48313402868SStefano Babic 	u32 reg;
48413402868SStefano Babic 
48513402868SStefano Babic 	/* Set the Panel Width */
48613402868SStefano Babic 	/* Pixels per line = (PPL + 1)*16 */
487765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1) {
488765f2f08SHeiko Schocher 		/*
489535cce0fSRobert P. J. Day 		 * 0x3F in bits 4..9 gives max horizontal resolution = 1024
490765f2f08SHeiko Schocher 		 * pixels
491765f2f08SHeiko Schocher 		 */
49213402868SStefano Babic 		width &= 0x3f0;
493765f2f08SHeiko Schocher 	} else {
494765f2f08SHeiko Schocher 		/*
495765f2f08SHeiko Schocher 		 * 0x7F in bits 4..10 gives max horizontal resolution = 2048
496765f2f08SHeiko Schocher 		 * pixels.
497765f2f08SHeiko Schocher 		 */
498765f2f08SHeiko Schocher 		width &= 0x7f0;
499765f2f08SHeiko Schocher 	}
50013402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0);
50113402868SStefano Babic 	reg &= 0xfffffc00;
502765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1) {
50313402868SStefano Babic 		reg |= ((width >> 4) - 1) << 4;
504765f2f08SHeiko Schocher 	} else {
505765f2f08SHeiko Schocher 		width = (width >> 4) - 1;
506765f2f08SHeiko Schocher 		reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3);
507765f2f08SHeiko Schocher 	}
50813402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0);
50913402868SStefano Babic 
51013402868SStefano Babic 	/* Set the Panel Height */
511765f2f08SHeiko Schocher 	/* Set bits 9:0 of Lines Per Pixel */
51213402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1);
51313402868SStefano Babic 	reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00);
51413402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1);
51513402868SStefano Babic 
516765f2f08SHeiko Schocher 	/* Set bit 10 of Lines Per Pixel */
517765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_2) {
518765f2f08SHeiko Schocher 		reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2);
519765f2f08SHeiko Schocher 		reg |= ((height - 1) & 0x400) << 16;
520765f2f08SHeiko Schocher 		lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2);
521765f2f08SHeiko Schocher 	}
522765f2f08SHeiko Schocher 
52313402868SStefano Babic 	/* Set the Raster Order of the Frame Buffer */
52413402868SStefano Babic 	reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(1 << 8);
52513402868SStefano Babic 	if (raster_order)
52613402868SStefano Babic 		reg |= LCD_RASTER_ORDER;
527765f2f08SHeiko Schocher 
528765f2f08SHeiko Schocher 	if (bpp == 24)
529765f2f08SHeiko Schocher 		reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE);
530765f2f08SHeiko Schocher 	else if (bpp == 32)
531765f2f08SHeiko Schocher 		reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE
532765f2f08SHeiko Schocher 				| LCD_V2_TFT_24BPP_UNPACK);
533765f2f08SHeiko Schocher 
53413402868SStefano Babic 	lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl);
53513402868SStefano Babic 
53613402868SStefano Babic 	switch (bpp) {
53713402868SStefano Babic 	case 1:
53813402868SStefano Babic 	case 2:
53913402868SStefano Babic 	case 4:
54013402868SStefano Babic 	case 16:
541765f2f08SHeiko Schocher 	case 24:
542765f2f08SHeiko Schocher 	case 32:
54313402868SStefano Babic 		par->palette_sz = 16 * 2;
54413402868SStefano Babic 		break;
54513402868SStefano Babic 
54613402868SStefano Babic 	case 8:
54713402868SStefano Babic 		par->palette_sz = 256 * 2;
54813402868SStefano Babic 		break;
54913402868SStefano Babic 
55013402868SStefano Babic 	default:
55113402868SStefano Babic 		return -EINVAL;
55213402868SStefano Babic 	}
55313402868SStefano Babic 
55413402868SStefano Babic 	return 0;
55513402868SStefano Babic }
55613402868SStefano Babic 
fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)55713402868SStefano Babic static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
55813402868SStefano Babic 			      unsigned blue, unsigned transp,
55913402868SStefano Babic 			      struct fb_info *info)
56013402868SStefano Babic {
56113402868SStefano Babic 	struct da8xx_fb_par *par = info->par;
56213402868SStefano Babic 	unsigned short *palette = (unsigned short *) par->v_palette_base;
56313402868SStefano Babic 	u_short pal;
56413402868SStefano Babic 	int update_hw = 0;
56513402868SStefano Babic 
56613402868SStefano Babic 	if (regno > 255)
56713402868SStefano Babic 		return 1;
56813402868SStefano Babic 
56913402868SStefano Babic 	if (info->fix.visual == FB_VISUAL_DIRECTCOLOR)
57013402868SStefano Babic 		return 1;
57113402868SStefano Babic 
57213402868SStefano Babic 	if (info->var.bits_per_pixel == 8) {
57313402868SStefano Babic 		red >>= 4;
57413402868SStefano Babic 		green >>= 8;
57513402868SStefano Babic 		blue >>= 12;
57613402868SStefano Babic 
57713402868SStefano Babic 		pal = (red & 0x0f00);
57813402868SStefano Babic 		pal |= (green & 0x00f0);
57913402868SStefano Babic 		pal |= (blue & 0x000f);
58013402868SStefano Babic 
58113402868SStefano Babic 		if (palette[regno] != pal) {
58213402868SStefano Babic 			update_hw = 1;
58313402868SStefano Babic 			palette[regno] = pal;
58413402868SStefano Babic 		}
58513402868SStefano Babic 	} else if ((info->var.bits_per_pixel == 16) && regno < 16) {
58613402868SStefano Babic 		red >>= (16 - info->var.red.length);
58713402868SStefano Babic 		red <<= info->var.red.offset;
58813402868SStefano Babic 
58913402868SStefano Babic 		green >>= (16 - info->var.green.length);
59013402868SStefano Babic 		green <<= info->var.green.offset;
59113402868SStefano Babic 
59213402868SStefano Babic 		blue >>= (16 - info->var.blue.length);
59313402868SStefano Babic 		blue <<= info->var.blue.offset;
59413402868SStefano Babic 
59513402868SStefano Babic 		par->pseudo_palette[regno] = red | green | blue;
59613402868SStefano Babic 
59713402868SStefano Babic 		if (palette[0] != 0x4000) {
59813402868SStefano Babic 			update_hw = 1;
59913402868SStefano Babic 			palette[0] = 0x4000;
60013402868SStefano Babic 		}
601765f2f08SHeiko Schocher 	} else if (((info->var.bits_per_pixel == 32) && regno < 32) ||
602765f2f08SHeiko Schocher 		   ((info->var.bits_per_pixel == 24) && regno < 24)) {
603765f2f08SHeiko Schocher 		red >>= (24 - info->var.red.length);
604765f2f08SHeiko Schocher 		red <<= info->var.red.offset;
605765f2f08SHeiko Schocher 
606765f2f08SHeiko Schocher 		green >>= (24 - info->var.green.length);
607765f2f08SHeiko Schocher 		green <<= info->var.green.offset;
608765f2f08SHeiko Schocher 
609765f2f08SHeiko Schocher 		blue >>= (24 - info->var.blue.length);
610765f2f08SHeiko Schocher 		blue <<= info->var.blue.offset;
611765f2f08SHeiko Schocher 
612765f2f08SHeiko Schocher 		par->pseudo_palette[regno] = red | green | blue;
613765f2f08SHeiko Schocher 
614765f2f08SHeiko Schocher 		if (palette[0] != 0x4000) {
615765f2f08SHeiko Schocher 			update_hw = 1;
616765f2f08SHeiko Schocher 			palette[0] = 0x4000;
617765f2f08SHeiko Schocher 		}
61813402868SStefano Babic 	}
61913402868SStefano Babic 
62013402868SStefano Babic 	/* Update the palette in the h/w as needed. */
62113402868SStefano Babic 	if (update_hw)
62213402868SStefano Babic 		lcd_blit(LOAD_PALETTE, par);
62313402868SStefano Babic 
62413402868SStefano Babic 	return 0;
62513402868SStefano Babic }
62613402868SStefano Babic 
lcd_reset(struct da8xx_fb_par * par)62713402868SStefano Babic static void lcd_reset(struct da8xx_fb_par *par)
62813402868SStefano Babic {
62913402868SStefano Babic 	/* Disable the Raster if previously Enabled */
630765f2f08SHeiko Schocher 	lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
63113402868SStefano Babic 
63213402868SStefano Babic 	/* DMA has to be disabled */
63313402868SStefano Babic 	lcdc_write(0, &da8xx_fb_reg_base->dma_ctrl);
63413402868SStefano Babic 	lcdc_write(0, &da8xx_fb_reg_base->raster_ctrl);
635765f2f08SHeiko Schocher 
636765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_2) {
637765f2f08SHeiko Schocher 		lcdc_write(0, &da8xx_fb_reg_base->int_ena_set);
638765f2f08SHeiko Schocher 		/* Write 1 to reset */
639765f2f08SHeiko Schocher 		lcdc_write(LCD_CLK_MAIN_RESET, &da8xx_fb_reg_base->clk_reset);
640765f2f08SHeiko Schocher 		lcdc_write(0, &da8xx_fb_reg_base->clk_reset);
641765f2f08SHeiko Schocher 	}
64213402868SStefano Babic }
64313402868SStefano Babic 
lcd_calc_clk_divider(struct da8xx_fb_par * par)64413402868SStefano Babic static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
64513402868SStefano Babic {
64613402868SStefano Babic 	unsigned int lcd_clk, div;
64713402868SStefano Babic 
64813402868SStefano Babic 	/* Get clock from sysclk2 */
64913402868SStefano Babic 	lcd_clk = clk_get(2);
65013402868SStefano Babic 
65113402868SStefano Babic 	div = lcd_clk / par->pxl_clk;
652765f2f08SHeiko Schocher 	debug("LCD Clock: %d Divider: %d PixClk: %d\n",
65313402868SStefano Babic 	      lcd_clk, div, par->pxl_clk);
65413402868SStefano Babic 
65513402868SStefano Babic 	/* Configure the LCD clock divisor. */
65613402868SStefano Babic 	lcdc_write(LCD_CLK_DIVISOR(div) |
65713402868SStefano Babic 			(LCD_RASTER_MODE & 0x1), &da8xx_fb_reg_base->ctrl);
658765f2f08SHeiko Schocher 
659765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_2)
660765f2f08SHeiko Schocher 		lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN |
661765f2f08SHeiko Schocher 				LCD_V2_CORE_CLK_EN,
662765f2f08SHeiko Schocher 				&da8xx_fb_reg_base->clk_ena);
66313402868SStefano Babic }
66413402868SStefano Babic 
lcd_init(struct da8xx_fb_par * par,const struct lcd_ctrl_config * cfg,const struct da8xx_panel * panel)66513402868SStefano Babic static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
66613402868SStefano Babic 		const struct da8xx_panel *panel)
66713402868SStefano Babic {
66813402868SStefano Babic 	u32 bpp;
66913402868SStefano Babic 	int ret = 0;
67013402868SStefano Babic 
67113402868SStefano Babic 	lcd_reset(par);
67213402868SStefano Babic 
67313402868SStefano Babic 	/* Calculate the divider */
67413402868SStefano Babic 	lcd_calc_clk_divider(par);
67513402868SStefano Babic 
67613402868SStefano Babic 	if (panel->invert_pxl_clk)
67713402868SStefano Babic 		lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) |
67813402868SStefano Babic 			LCD_INVERT_PIXEL_CLOCK),
67913402868SStefano Babic 			 &da8xx_fb_reg_base->raster_timing_2);
68013402868SStefano Babic 	else
68113402868SStefano Babic 		lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) &
68213402868SStefano Babic 			~LCD_INVERT_PIXEL_CLOCK),
68313402868SStefano Babic 			&da8xx_fb_reg_base->raster_timing_2);
68413402868SStefano Babic 
68513402868SStefano Babic 	/* Configure the DMA burst size. */
68613402868SStefano Babic 	ret = lcd_cfg_dma(cfg->dma_burst_sz);
68713402868SStefano Babic 	if (ret < 0)
68813402868SStefano Babic 		return ret;
68913402868SStefano Babic 
69013402868SStefano Babic 	/* Configure the AC bias properties. */
69113402868SStefano Babic 	lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt);
69213402868SStefano Babic 
69313402868SStefano Babic 	/* Configure the vertical and horizontal sync properties. */
69413402868SStefano Babic 	lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp);
69513402868SStefano Babic 	lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp);
69613402868SStefano Babic 
697535cce0fSRobert P. J. Day 	/* Configure for display */
69813402868SStefano Babic 	ret = lcd_cfg_display(cfg);
69913402868SStefano Babic 	if (ret < 0)
70013402868SStefano Babic 		return ret;
70113402868SStefano Babic 
702765f2f08SHeiko Schocher 	if ((QVGA != cfg->p_disp_panel->panel_type) &&
703765f2f08SHeiko Schocher 	    (WVGA != cfg->p_disp_panel->panel_type))
70413402868SStefano Babic 		return -EINVAL;
70513402868SStefano Babic 
70613402868SStefano Babic 	if (cfg->bpp <= cfg->p_disp_panel->max_bpp &&
70713402868SStefano Babic 	    cfg->bpp >= cfg->p_disp_panel->min_bpp)
70813402868SStefano Babic 		bpp = cfg->bpp;
70913402868SStefano Babic 	else
71013402868SStefano Babic 		bpp = cfg->p_disp_panel->max_bpp;
71113402868SStefano Babic 	if (bpp == 12)
71213402868SStefano Babic 		bpp = 16;
71313402868SStefano Babic 	ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width,
71413402868SStefano Babic 				(unsigned int)panel->height, bpp,
71513402868SStefano Babic 				cfg->raster_order);
71613402868SStefano Babic 	if (ret < 0)
71713402868SStefano Babic 		return ret;
71813402868SStefano Babic 
71913402868SStefano Babic 	/* Configure FDD */
72013402868SStefano Babic 	lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & 0xfff00fff) |
72113402868SStefano Babic 		       (cfg->fdd << 12), &da8xx_fb_reg_base->raster_ctrl);
72213402868SStefano Babic 
72313402868SStefano Babic 	return 0;
72413402868SStefano Babic }
72513402868SStefano Babic 
lcdc_dma_start(void)72613402868SStefano Babic static void lcdc_dma_start(void)
72713402868SStefano Babic {
72813402868SStefano Babic 	struct da8xx_fb_par *par = da8xx_fb_info->par;
72913402868SStefano Babic 	lcdc_write(par->dma_start,
73013402868SStefano Babic 		&da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
73113402868SStefano Babic 	lcdc_write(par->dma_end,
73213402868SStefano Babic 		&da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
73313402868SStefano Babic 	lcdc_write(0,
73413402868SStefano Babic 		&da8xx_fb_reg_base->dma_frm_buf_base_addr_1);
73513402868SStefano Babic 	lcdc_write(0,
73613402868SStefano Babic 		&da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1);
73713402868SStefano Babic }
73813402868SStefano Babic 
lcdc_irq_handler_rev01(void)739765f2f08SHeiko Schocher static u32 lcdc_irq_handler_rev01(void)
74013402868SStefano Babic {
74113402868SStefano Babic 	struct da8xx_fb_par *par = da8xx_fb_info->par;
74213402868SStefano Babic 	u32 stat = lcdc_read(&da8xx_fb_reg_base->stat);
74313402868SStefano Babic 	u32 reg_ras;
74413402868SStefano Babic 
74513402868SStefano Babic 	if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
74613402868SStefano Babic 		debug("LCD_SYNC_LOST\n");
747765f2f08SHeiko Schocher 		lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
74813402868SStefano Babic 		lcdc_write(stat, &da8xx_fb_reg_base->stat);
74913402868SStefano Babic 		lcd_enable_raster();
75013402868SStefano Babic 		return LCD_SYNC_LOST;
75113402868SStefano Babic 	} else if (stat & LCD_PL_LOAD_DONE) {
75213402868SStefano Babic 		debug("LCD_PL_LOAD_DONE\n");
75313402868SStefano Babic 		/*
75413402868SStefano Babic 		 * Must disable raster before changing state of any control bit.
75513402868SStefano Babic 		 * And also must be disabled before clearing the PL loading
75613402868SStefano Babic 		 * interrupt via the following write to the status register. If
75713402868SStefano Babic 		 * this is done after then one gets multiple PL done interrupts.
75813402868SStefano Babic 		 */
759765f2f08SHeiko Schocher 		lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
76013402868SStefano Babic 
76113402868SStefano Babic 		lcdc_write(stat, &da8xx_fb_reg_base->stat);
76213402868SStefano Babic 
763535cce0fSRobert P. J. Day 		/* Disable PL completion interrupt */
76413402868SStefano Babic 		reg_ras  = lcdc_read(&da8xx_fb_reg_base->raster_ctrl);
765765f2f08SHeiko Schocher 		reg_ras &= ~LCD_V1_PL_INT_ENA;
76613402868SStefano Babic 		lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl);
76713402868SStefano Babic 
76813402868SStefano Babic 		/* Setup and start data loading mode */
76913402868SStefano Babic 		lcd_blit(LOAD_DATA, par);
77013402868SStefano Babic 		return LCD_PL_LOAD_DONE;
77113402868SStefano Babic 	} else {
77213402868SStefano Babic 		lcdc_write(stat, &da8xx_fb_reg_base->stat);
77313402868SStefano Babic 
77413402868SStefano Babic 		if (stat & LCD_END_OF_FRAME0)
77513402868SStefano Babic 			debug("LCD_END_OF_FRAME0\n");
77613402868SStefano Babic 
77713402868SStefano Babic 		lcdc_write(par->dma_start,
77813402868SStefano Babic 			&da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
77913402868SStefano Babic 		lcdc_write(par->dma_end,
78013402868SStefano Babic 			&da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
78113402868SStefano Babic 		par->vsync_flag = 1;
78213402868SStefano Babic 		return LCD_END_OF_FRAME0;
78313402868SStefano Babic 	}
78413402868SStefano Babic 	return stat;
78513402868SStefano Babic }
78613402868SStefano Babic 
lcdc_irq_handler_rev02(void)787765f2f08SHeiko Schocher static u32 lcdc_irq_handler_rev02(void)
788765f2f08SHeiko Schocher {
789765f2f08SHeiko Schocher 	struct da8xx_fb_par *par = da8xx_fb_info->par;
790765f2f08SHeiko Schocher 	u32 stat = lcdc_read(&da8xx_fb_reg_base->masked_stat);
791765f2f08SHeiko Schocher 	u32 reg_int;
792765f2f08SHeiko Schocher 
793765f2f08SHeiko Schocher 	if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
794765f2f08SHeiko Schocher 		debug("LCD_SYNC_LOST\n");
795765f2f08SHeiko Schocher 		lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
796765f2f08SHeiko Schocher 		lcdc_write(stat, &da8xx_fb_reg_base->masked_stat);
797765f2f08SHeiko Schocher 		lcd_enable_raster();
798765f2f08SHeiko Schocher 		lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind);
799765f2f08SHeiko Schocher 		return LCD_SYNC_LOST;
800765f2f08SHeiko Schocher 	} else if (stat & LCD_PL_LOAD_DONE) {
801765f2f08SHeiko Schocher 		debug("LCD_PL_LOAD_DONE\n");
802765f2f08SHeiko Schocher 		/*
803765f2f08SHeiko Schocher 		 * Must disable raster before changing state of any control bit.
804765f2f08SHeiko Schocher 		 * And also must be disabled before clearing the PL loading
805765f2f08SHeiko Schocher 		 * interrupt via the following write to the status register. If
806765f2f08SHeiko Schocher 		 * this is done after then one gets multiple PL done interrupts.
807765f2f08SHeiko Schocher 		 */
808765f2f08SHeiko Schocher 		lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
809765f2f08SHeiko Schocher 
810765f2f08SHeiko Schocher 		lcdc_write(stat, &da8xx_fb_reg_base->masked_stat);
811765f2f08SHeiko Schocher 
812535cce0fSRobert P. J. Day 		/* Disable PL completion interrupt */
813765f2f08SHeiko Schocher 		reg_int  = lcdc_read(&da8xx_fb_reg_base->int_ena_clr) |
814765f2f08SHeiko Schocher 			(LCD_V2_PL_INT_ENA);
815765f2f08SHeiko Schocher 		lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_clr);
816765f2f08SHeiko Schocher 
817765f2f08SHeiko Schocher 		/* Setup and start data loading mode */
818765f2f08SHeiko Schocher 		lcd_blit(LOAD_DATA, par);
819765f2f08SHeiko Schocher 		lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind);
820765f2f08SHeiko Schocher 		return LCD_PL_LOAD_DONE;
821765f2f08SHeiko Schocher 	} else {
822765f2f08SHeiko Schocher 		lcdc_write(stat, &da8xx_fb_reg_base->masked_stat);
823765f2f08SHeiko Schocher 
824765f2f08SHeiko Schocher 		if (stat & LCD_END_OF_FRAME0)
825765f2f08SHeiko Schocher 			debug("LCD_END_OF_FRAME0\n");
826765f2f08SHeiko Schocher 
827765f2f08SHeiko Schocher 		lcdc_write(par->dma_start,
828765f2f08SHeiko Schocher 			   &da8xx_fb_reg_base->dma_frm_buf_base_addr_0);
829765f2f08SHeiko Schocher 		lcdc_write(par->dma_end,
830765f2f08SHeiko Schocher 			   &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0);
831765f2f08SHeiko Schocher 		par->vsync_flag = 1;
832765f2f08SHeiko Schocher 		lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind);
833765f2f08SHeiko Schocher 		return LCD_END_OF_FRAME0;
834765f2f08SHeiko Schocher 	}
835765f2f08SHeiko Schocher 	lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind);
836765f2f08SHeiko Schocher 	return stat;
837765f2f08SHeiko Schocher }
838765f2f08SHeiko Schocher 
lcdc_irq_handler(void)839765f2f08SHeiko Schocher static u32 lcdc_irq_handler(void)
840765f2f08SHeiko Schocher {
841765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1)
842765f2f08SHeiko Schocher 		return lcdc_irq_handler_rev01();
843765f2f08SHeiko Schocher 	else
844765f2f08SHeiko Schocher 		return lcdc_irq_handler_rev02();
845765f2f08SHeiko Schocher }
846765f2f08SHeiko Schocher 
wait_for_event(u32 event)84713402868SStefano Babic static u32 wait_for_event(u32 event)
84813402868SStefano Babic {
84913402868SStefano Babic 	u32 timeout = 50000;
85013402868SStefano Babic 	u32 ret;
85113402868SStefano Babic 
85213402868SStefano Babic 	do {
85313402868SStefano Babic 		ret = lcdc_irq_handler();
85413402868SStefano Babic 		udelay(1000);
855c16b342dSHeinrich Schuchardt 		--timeout;
856c16b342dSHeinrich Schuchardt 	} while (!(ret & event) && timeout);
85713402868SStefano Babic 
858c16b342dSHeinrich Schuchardt 	if (!(ret & event)) {
85913402868SStefano Babic 		printf("%s: event %d not hit\n", __func__, event);
86013402868SStefano Babic 		return -1;
86113402868SStefano Babic 	}
86213402868SStefano Babic 
86313402868SStefano Babic 	return 0;
86413402868SStefano Babic 
86513402868SStefano Babic }
86613402868SStefano Babic 
video_hw_init(void)86713402868SStefano Babic void *video_hw_init(void)
86813402868SStefano Babic {
86913402868SStefano Babic 	struct da8xx_fb_par *par;
87013402868SStefano Babic 	u32 size;
871765f2f08SHeiko Schocher 	u32 rev;
87213402868SStefano Babic 	char *p;
87313402868SStefano Babic 
87413402868SStefano Babic 	if (!lcd_panel) {
87513402868SStefano Babic 		printf("Display not initialized\n");
87613402868SStefano Babic 		return NULL;
87713402868SStefano Babic 	}
87813402868SStefano Babic 	gpanel.winSizeX = lcd_panel->width;
87913402868SStefano Babic 	gpanel.winSizeY = lcd_panel->height;
88013402868SStefano Babic 	gpanel.plnSizeX = lcd_panel->width;
88113402868SStefano Babic 	gpanel.plnSizeY = lcd_panel->height;
88213402868SStefano Babic 
88313402868SStefano Babic 	switch (bits_x_pixel) {
884765f2f08SHeiko Schocher 	case 32:
885765f2f08SHeiko Schocher 		gpanel.gdfBytesPP = 4;
886765f2f08SHeiko Schocher 		gpanel.gdfIndex = GDF_32BIT_X888RGB;
887765f2f08SHeiko Schocher 		break;
88813402868SStefano Babic 	case 24:
88913402868SStefano Babic 		gpanel.gdfBytesPP = 4;
89013402868SStefano Babic 		gpanel.gdfIndex = GDF_32BIT_X888RGB;
89113402868SStefano Babic 		break;
89213402868SStefano Babic 	case 16:
89313402868SStefano Babic 		gpanel.gdfBytesPP = 2;
89413402868SStefano Babic 		gpanel.gdfIndex = GDF_16BIT_565RGB;
89513402868SStefano Babic 		break;
89613402868SStefano Babic 	default:
89713402868SStefano Babic 		gpanel.gdfBytesPP = 1;
89813402868SStefano Babic 		gpanel.gdfIndex = GDF__8BIT_INDEX;
89913402868SStefano Babic 		break;
90013402868SStefano Babic 	}
90113402868SStefano Babic 
902765f2f08SHeiko Schocher 	da8xx_fb_reg_base = (struct da8xx_lcd_regs *)DA8XX_LCD_CNTL_BASE;
90313402868SStefano Babic 
904765f2f08SHeiko Schocher 	/* Determine LCD IP Version */
905765f2f08SHeiko Schocher 	rev = lcdc_read(&da8xx_fb_reg_base->revid);
906765f2f08SHeiko Schocher 	switch (rev) {
907765f2f08SHeiko Schocher 	case 0x4C100102:
908765f2f08SHeiko Schocher 		lcd_revision = LCD_VERSION_1;
909765f2f08SHeiko Schocher 		break;
910765f2f08SHeiko Schocher 	case 0x4F200800:
911765f2f08SHeiko Schocher 	case 0x4F201000:
912765f2f08SHeiko Schocher 		lcd_revision = LCD_VERSION_2;
913765f2f08SHeiko Schocher 		break;
914765f2f08SHeiko Schocher 	default:
915765f2f08SHeiko Schocher 		printf("Unknown PID Reg value 0x%x, defaulting to LCD revision 1\n",
916765f2f08SHeiko Schocher 		       rev);
917765f2f08SHeiko Schocher 		lcd_revision = LCD_VERSION_1;
918765f2f08SHeiko Schocher 		break;
919765f2f08SHeiko Schocher 	}
920765f2f08SHeiko Schocher 
921765f2f08SHeiko Schocher 	debug("rev: 0x%x Resolution: %dx%d %d\n", rev,
92213402868SStefano Babic 	      gpanel.winSizeX,
92313402868SStefano Babic 	      gpanel.winSizeY,
924765f2f08SHeiko Schocher 	      da8xx_lcd_cfg->bpp);
92513402868SStefano Babic 
92613402868SStefano Babic 	size = sizeof(struct fb_info) + sizeof(struct da8xx_fb_par);
927963be689SNiko Mauno 	da8xx_fb_info = malloc_cache_aligned(size);
92813402868SStefano Babic 	debug("da8xx_fb_info at %x\n", (unsigned int)da8xx_fb_info);
92913402868SStefano Babic 
93013402868SStefano Babic 	if (!da8xx_fb_info) {
93113402868SStefano Babic 		printf("Memory allocation failed for fb_info\n");
93213402868SStefano Babic 		return NULL;
93313402868SStefano Babic 	}
93413402868SStefano Babic 	memset(da8xx_fb_info, 0, size);
93513402868SStefano Babic 	p = (char *)da8xx_fb_info;
93613402868SStefano Babic 	da8xx_fb_info->par = p +  sizeof(struct fb_info);
93713402868SStefano Babic 	debug("da8xx_par at %x\n", (unsigned int)da8xx_fb_info->par);
93813402868SStefano Babic 
93913402868SStefano Babic 	par = da8xx_fb_info->par;
94013402868SStefano Babic 	par->pxl_clk = lcd_panel->pxl_clk;
94113402868SStefano Babic 
942765f2f08SHeiko Schocher 	if (lcd_init(par, da8xx_lcd_cfg, lcd_panel) < 0) {
94313402868SStefano Babic 		printf("lcd_init failed\n");
94413402868SStefano Babic 		goto err_release_fb;
94513402868SStefano Babic 	}
94613402868SStefano Babic 
94713402868SStefano Babic 	/* allocate frame buffer */
948765f2f08SHeiko Schocher 	par->vram_size = lcd_panel->width * lcd_panel->height *
949765f2f08SHeiko Schocher 			da8xx_lcd_cfg->bpp;
95013402868SStefano Babic 	par->vram_size = par->vram_size * LCD_NUM_BUFFERS / 8;
95113402868SStefano Babic 
952963be689SNiko Mauno 	par->vram_virt = malloc_cache_aligned(par->vram_size);
95313402868SStefano Babic 
95413402868SStefano Babic 	par->vram_phys = (dma_addr_t) par->vram_virt;
95513402868SStefano Babic 	debug("Requesting 0x%x bytes for framebuffer at 0x%x\n",
95613402868SStefano Babic 		(unsigned int)par->vram_size,
95713402868SStefano Babic 		(unsigned int)par->vram_virt);
95813402868SStefano Babic 	if (!par->vram_virt) {
95913402868SStefano Babic 		printf("GLCD: malloc for frame buffer failed\n");
96013402868SStefano Babic 		goto err_release_fb;
96113402868SStefano Babic 	}
9624e023626SHeiko Schocher 	gd->fb_base = (int)par->vram_virt;
96313402868SStefano Babic 
96413402868SStefano Babic 	gpanel.frameAdrs = (unsigned int)par->vram_virt;
96513402868SStefano Babic 	da8xx_fb_info->screen_base = (char *) par->vram_virt;
96613402868SStefano Babic 	da8xx_fb_fix.smem_start	= gpanel.frameAdrs;
96713402868SStefano Babic 	da8xx_fb_fix.smem_len = par->vram_size;
968765f2f08SHeiko Schocher 	da8xx_fb_fix.line_length = (lcd_panel->width * da8xx_lcd_cfg->bpp) / 8;
96913402868SStefano Babic 
97013402868SStefano Babic 	par->dma_start = par->vram_phys;
97113402868SStefano Babic 	par->dma_end   = par->dma_start + lcd_panel->height *
97213402868SStefano Babic 		da8xx_fb_fix.line_length - 1;
97313402868SStefano Babic 
97413402868SStefano Babic 	/* allocate palette buffer */
975963be689SNiko Mauno 	par->v_palette_base = malloc_cache_aligned(PALETTE_SIZE);
97613402868SStefano Babic 	if (!par->v_palette_base) {
97713402868SStefano Babic 		printf("GLCD: malloc for palette buffer failed\n");
97813402868SStefano Babic 		goto err_release_fb_mem;
97913402868SStefano Babic 	}
98013402868SStefano Babic 	memset(par->v_palette_base, 0, PALETTE_SIZE);
98113402868SStefano Babic 	par->p_palette_base = (unsigned int)par->v_palette_base;
98213402868SStefano Babic 
98313402868SStefano Babic 	/* Initialize par */
984765f2f08SHeiko Schocher 	da8xx_fb_info->var.bits_per_pixel = da8xx_lcd_cfg->bpp;
98513402868SStefano Babic 
98613402868SStefano Babic 	da8xx_fb_var.xres = lcd_panel->width;
98713402868SStefano Babic 	da8xx_fb_var.xres_virtual = lcd_panel->width;
98813402868SStefano Babic 
98913402868SStefano Babic 	da8xx_fb_var.yres         = lcd_panel->height;
99013402868SStefano Babic 	da8xx_fb_var.yres_virtual = lcd_panel->height * LCD_NUM_BUFFERS;
99113402868SStefano Babic 
99213402868SStefano Babic 	da8xx_fb_var.grayscale =
993765f2f08SHeiko Schocher 	    da8xx_lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0;
994765f2f08SHeiko Schocher 	da8xx_fb_var.bits_per_pixel = da8xx_lcd_cfg->bpp;
99513402868SStefano Babic 
99613402868SStefano Babic 	da8xx_fb_var.hsync_len = lcd_panel->hsw;
99713402868SStefano Babic 	da8xx_fb_var.vsync_len = lcd_panel->vsw;
99813402868SStefano Babic 
99913402868SStefano Babic 	/* Initialize fbinfo */
100013402868SStefano Babic 	da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
100113402868SStefano Babic 	da8xx_fb_info->fix = da8xx_fb_fix;
100213402868SStefano Babic 	da8xx_fb_info->var = da8xx_fb_var;
100313402868SStefano Babic 	da8xx_fb_info->pseudo_palette = par->pseudo_palette;
100413402868SStefano Babic 	da8xx_fb_info->fix.visual = (da8xx_fb_info->var.bits_per_pixel <= 8) ?
100513402868SStefano Babic 				FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
100613402868SStefano Babic 
100713402868SStefano Babic 	/* Clear interrupt */
100813402868SStefano Babic 	memset((void *)par->vram_virt, 0, par->vram_size);
1009765f2f08SHeiko Schocher 	lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
1010765f2f08SHeiko Schocher 	if (lcd_revision == LCD_VERSION_1)
101113402868SStefano Babic 		lcdc_write(0xFFFF, &da8xx_fb_reg_base->stat);
1012765f2f08SHeiko Schocher 	else
1013765f2f08SHeiko Schocher 		lcdc_write(0xFFFF, &da8xx_fb_reg_base->masked_stat);
101413402868SStefano Babic 	debug("Palette at 0x%x size %d\n", par->p_palette_base,
101513402868SStefano Babic 		par->palette_sz);
101613402868SStefano Babic 	lcdc_dma_start();
101713402868SStefano Babic 
101813402868SStefano Babic 	/* Load a default palette */
101913402868SStefano Babic 	fb_setcolreg(0, 0, 0, 0, 0xffff, da8xx_fb_info);
102013402868SStefano Babic 
102113402868SStefano Babic 	/* Check that the palette is loaded */
102213402868SStefano Babic 	wait_for_event(LCD_PL_LOAD_DONE);
102313402868SStefano Babic 
102413402868SStefano Babic 	/* Wait until DMA is working */
102513402868SStefano Babic 	wait_for_event(LCD_END_OF_FRAME0);
102613402868SStefano Babic 
102713402868SStefano Babic 	return (void *)&gpanel;
102813402868SStefano Babic 
102913402868SStefano Babic err_release_fb_mem:
103013402868SStefano Babic 	free(par->vram_virt);
103113402868SStefano Babic 
103213402868SStefano Babic err_release_fb:
103313402868SStefano Babic 	free(da8xx_fb_info);
103413402868SStefano Babic 
103513402868SStefano Babic 	return NULL;
103613402868SStefano Babic }
103713402868SStefano Babic 
da8xx_video_init(const struct da8xx_panel * panel,const struct lcd_ctrl_config * lcd_cfg,int bits_pixel)1038765f2f08SHeiko Schocher void da8xx_video_init(const struct da8xx_panel *panel,
1039765f2f08SHeiko Schocher 		      const struct lcd_ctrl_config *lcd_cfg, int bits_pixel)
104013402868SStefano Babic {
104113402868SStefano Babic 	lcd_panel = panel;
1042765f2f08SHeiko Schocher 	da8xx_lcd_cfg = lcd_cfg;
104313402868SStefano Babic 	bits_x_pixel = bits_pixel;
104413402868SStefano Babic }
1045