xref: /rk3399_rockchip-uboot/drivers/video/atmel_lcdfb.c (revision d63ec26a49a1ae8c0fd65c24e8c0b6c67b79cd01)
1 /*
2  * Driver for AT91/AT32 LCD Controller
3  *
4  * Copyright (C) 2007 Atmel Corporation
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <fdtdec.h>
11 #include <asm/io.h>
12 #include <asm/arch/gpio.h>
13 #include <asm/arch/clk.h>
14 #include <lcd.h>
15 #include <bmp_layout.h>
16 #include <atmel_lcdc.h>
17 
18 /* configurable parameters */
19 #define ATMEL_LCDC_CVAL_DEFAULT		0xc8
20 #define ATMEL_LCDC_DMA_BURST_LEN	8
21 #ifndef ATMEL_LCDC_GUARD_TIME
22 #define ATMEL_LCDC_GUARD_TIME		1
23 #endif
24 
25 #if defined(CONFIG_AT91SAM9263)
26 #define ATMEL_LCDC_FIFO_SIZE		2048
27 #else
28 #define ATMEL_LCDC_FIFO_SIZE		512
29 #endif
30 
31 #define lcdc_readl(mmio, reg)		__raw_readl((mmio)+(reg))
32 #define lcdc_writel(mmio, reg, val)	__raw_writel((val), (mmio)+(reg))
33 
34 ushort *configuration_get_cmap(void)
35 {
36 	return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
37 }
38 
39 #if defined(CONFIG_BMP_16BPP) && defined(CONFIG_ATMEL_LCD_BGR555)
40 void fb_put_word(uchar **fb, uchar **from)
41 {
42 	*(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03);
43 	*(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2);
44 	*from += 2;
45 }
46 #endif
47 
48 #ifdef CONFIG_LCD_LOGO
49 #include <bmp_logo.h>
50 void lcd_logo_set_cmap(void)
51 {
52 	int i;
53 	uint lut_entry;
54 	ushort colreg;
55 	uint *cmap = (uint *)configuration_get_cmap();
56 
57 	for (i = 0; i < BMP_LOGO_COLORS; ++i) {
58 		colreg = bmp_logo_palette[i];
59 #ifdef CONFIG_ATMEL_LCD_BGR555
60 		lut_entry = ((colreg & 0x000F) << 11) |
61 				((colreg & 0x00F0) <<  2) |
62 				((colreg & 0x0F00) >>  7);
63 #else
64 		lut_entry = ((colreg & 0x000F) << 1) |
65 				((colreg & 0x00F0) << 3) |
66 				((colreg & 0x0F00) << 4);
67 #endif
68 		*(cmap + BMP_LOGO_OFFSET) = lut_entry;
69 		cmap++;
70 	}
71 }
72 #endif
73 
74 void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
75 {
76 #if defined(CONFIG_ATMEL_LCD_BGR555)
77 	lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
78 		    (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7));
79 #else
80 	lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
81 		    (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8));
82 #endif
83 }
84 
85 void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
86 {
87 	int i;
88 
89 	for (i = 0; i < colors; ++i) {
90 		struct bmp_color_table_entry cte = bmp->color_table[i];
91 		lcd_setcolreg(i, cte.red, cte.green, cte.blue);
92 	}
93 }
94 
95 static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
96 			  bool tft, bool cont_pol_low, ulong lcdbase)
97 {
98 	unsigned long value;
99 	void *reg = (void *)addr;
100 
101 	/* Turn off the LCD controller and the DMA controller */
102 	lcdc_writel(reg, ATMEL_LCDC_PWRCON,
103 		    ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
104 
105 	/* Wait for the LCDC core to become idle */
106 	while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
107 		udelay(10);
108 
109 	lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
110 
111 	/* Reset LCDC DMA */
112 	lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
113 
114 	/* ...set frame size and burst length = 8 words (?) */
115 	value = (timing->hactive.typ * timing->vactive.typ *
116 		 (1 << bpix)) / 32;
117 	value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
118 	lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
119 
120 	/* Set pixel clock */
121 	value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
122 	if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
123 		value++;
124 	value = (value / 2) - 1;
125 
126 	if (!value) {
127 		lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
128 	} else
129 		lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
130 			    value << ATMEL_LCDC_CLKVAL_OFFSET);
131 
132 	/* Initialize control register 2 */
133 #ifdef CONFIG_AVR32
134 	value = ATMEL_LCDC_MEMOR_BIG | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
135 #else
136 	value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
137 #endif
138 	if (tft)
139 		value |= ATMEL_LCDC_DISTYPE_TFT;
140 
141 	if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
142 		value |= ATMEL_LCDC_INVLINE_INVERTED;
143 	if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
144 		value |= ATMEL_LCDC_INVFRAME_INVERTED;
145 	value |= bpix << 5;
146 	lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
147 
148 	/* Vertical timing */
149 	value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
150 	value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
151 	value |= timing->vfront_porch.typ;
152 	/* Magic! (Datasheet says "Bit 31 must be written to 1") */
153 	value |= 1U << 31;
154 	lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
155 
156 	/* Horizontal timing */
157 	value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
158 	value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
159 	value |= (timing->hback_porch.typ - 1);
160 	lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
161 
162 	/* Display size */
163 	value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
164 	value |= timing->vactive.typ - 1;
165 	lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
166 
167 	/* FIFO Threshold: Use formula from data sheet */
168 	value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
169 	lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
170 
171 	/* Toggle LCD_MODE every frame */
172 	lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
173 
174 	/* Disable all interrupts */
175 	lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
176 
177 	/* Set contrast */
178 	value = ATMEL_LCDC_PS_DIV8 |
179 		ATMEL_LCDC_ENA_PWMENABLE;
180 	if (!cont_pol_low)
181 		value |= ATMEL_LCDC_POL_POSITIVE;
182 	lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
183 	lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
184 
185 	/* Set framebuffer DMA base address and pixel offset */
186 	lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
187 
188 	lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
189 	lcdc_writel(reg, ATMEL_LCDC_PWRCON,
190 		    (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
191 }
192 
193 void lcd_ctrl_init(void *lcdbase)
194 {
195 	struct display_timing timing;
196 
197 	timing.flags = 0;
198 	if (!(panel_info.vl_sync & ATMEL_LCDC_INVLINE_INVERTED))
199 		timing.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
200 	if (!(panel_info.vl_sync & ATMEL_LCDC_INVFRAME_INVERTED))
201 		timing.flags |= DISPLAY_FLAGS_VSYNC_LOW;
202 	timing.pixelclock.typ = panel_info.vl_clk;
203 
204 	timing.hactive.typ = panel_info.vl_col;
205 	timing.hfront_porch.typ = panel_info.vl_right_margin;
206 	timing.hback_porch.typ = panel_info.vl_left_margin;
207 	timing.hsync_len.typ = panel_info.vl_hsync_len;
208 
209 	timing.vactive.typ = panel_info.vl_row;
210 	timing.vfront_porch.typ = panel_info.vl_clk;
211 	timing.vback_porch.typ = panel_info.vl_clk;
212 	timing.vsync_len.typ = panel_info.vl_clk;
213 
214 	atmel_fb_init(panel_info.mmio, &timing, panel_info.vl_bpix,
215 		      panel_info.vl_tft, panel_info.vl_cont_pol_low,
216 		      (ulong)lcdbase);
217 }
218 
219 ulong calc_fbsize(void)
220 {
221 	return ((panel_info.vl_col * panel_info.vl_row *
222 		NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE;
223 }
224