]>
Commit | Line | Data |
---|---|---|
6d4339f6 DL |
1 | /* |
2 | * Copyright (C) 2012 Samsung Electronics | |
3 | * | |
4 | * Author: InKi Dae <[email protected]> | |
5 | * Author: Donghwa Lee <[email protected]> | |
6 | * | |
3765b3e7 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
6d4339f6 DL |
8 | */ |
9 | ||
10 | #include <config.h> | |
11 | #include <common.h> | |
12 | #include <asm/io.h> | |
13 | #include <lcd.h> | |
14 | #include <div64.h> | |
c23f3157 AK |
15 | #include <fdtdec.h> |
16 | #include <libfdt.h> | |
6d4339f6 DL |
17 | #include <asm/arch/clk.h> |
18 | #include <asm/arch/clock.h> | |
19 | #include <asm/arch/cpu.h> | |
20 | #include "exynos_fb.h" | |
21 | ||
c23f3157 AK |
22 | DECLARE_GLOBAL_DATA_PTR; |
23 | ||
6d4339f6 DL |
24 | static unsigned long *lcd_base_addr; |
25 | static vidinfo_t *pvid; | |
47ff6073 | 26 | static struct exynos_fb *fimd_ctrl; |
6d4339f6 DL |
27 | |
28 | void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, | |
29 | u_long palette_size) | |
30 | { | |
31 | lcd_base_addr = (unsigned long *)screen_base; | |
32 | } | |
33 | ||
34 | static void exynos_fimd_set_dualrgb(unsigned int enabled) | |
35 | { | |
6d4339f6 DL |
36 | unsigned int cfg = 0; |
37 | ||
38 | if (enabled) { | |
39 | cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | | |
40 | EXYNOS_DUALRGB_VDEN_EN_ENABLE; | |
41 | ||
42 | /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ | |
43 | cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) | | |
44 | EXYNOS_DUALRGB_MAIN_CNT(0); | |
45 | } | |
46 | ||
47 | writel(cfg, &fimd_ctrl->dualrgb); | |
48 | } | |
49 | ||
a29c8322 DL |
50 | static void exynos_fimd_set_dp_clkcon(unsigned int enabled) |
51 | { | |
a29c8322 DL |
52 | unsigned int cfg = 0; |
53 | ||
54 | if (enabled) | |
55 | cfg = EXYNOS_DP_CLK_ENABLE; | |
56 | ||
57 | writel(cfg, &fimd_ctrl->dp_mie_clkcon); | |
58 | } | |
59 | ||
6d4339f6 DL |
60 | static void exynos_fimd_set_par(unsigned int win_id) |
61 | { | |
62 | unsigned int cfg = 0; | |
6d4339f6 DL |
63 | |
64 | /* set window control */ | |
65 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
66 | EXYNOS_WINCON(win_id)); | |
67 | ||
68 | cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | | |
69 | EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | | |
70 | EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | | |
71 | EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); | |
72 | ||
73 | /* DATAPATH is DMA */ | |
74 | cfg |= EXYNOS_WINCON_DATAPATH_DMA; | |
75 | ||
f831b3fe | 76 | cfg |= EXYNOS_WINCON_HAWSWP_ENABLE; |
6d4339f6 DL |
77 | |
78 | /* dma burst is 16 */ | |
79 | cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; | |
80 | ||
f831b3fe PM |
81 | switch (pvid->vl_bpix) { |
82 | case 4: | |
61b59e27 | 83 | cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565; |
f831b3fe PM |
84 | break; |
85 | default: | |
86 | cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; | |
87 | break; | |
88 | } | |
6d4339f6 DL |
89 | |
90 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
91 | EXYNOS_WINCON(win_id)); | |
92 | ||
93 | /* set window position to x=0, y=0*/ | |
94 | cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); | |
95 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a + | |
96 | EXYNOS_VIDOSD(win_id)); | |
97 | ||
98 | cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | | |
ee93dcfa DL |
99 | EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) | |
100 | EXYNOS_VIDOSD_RIGHT_X_E(1) | | |
101 | EXYNOS_VIDOSD_BOTTOM_Y_E(0); | |
102 | ||
6d4339f6 DL |
103 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b + |
104 | EXYNOS_VIDOSD(win_id)); | |
105 | ||
106 | /* set window size for window0*/ | |
107 | cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); | |
108 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c + | |
109 | EXYNOS_VIDOSD(win_id)); | |
110 | } | |
111 | ||
112 | static void exynos_fimd_set_buffer_address(unsigned int win_id) | |
113 | { | |
114 | unsigned long start_addr, end_addr; | |
6d4339f6 DL |
115 | |
116 | start_addr = (unsigned long)lcd_base_addr; | |
f78095e4 | 117 | end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) * |
6d4339f6 DL |
118 | pvid->vl_row); |
119 | ||
120 | writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 + | |
121 | EXYNOS_BUFFER_OFFSET(win_id)); | |
122 | writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 + | |
123 | EXYNOS_BUFFER_OFFSET(win_id)); | |
124 | } | |
125 | ||
126 | static void exynos_fimd_set_clock(vidinfo_t *pvid) | |
127 | { | |
128 | unsigned int cfg = 0, div = 0, remainder, remainder_div; | |
129 | unsigned long pixel_clock; | |
130 | unsigned long long src_clock; | |
6d4339f6 DL |
131 | |
132 | if (pvid->dual_lcd_enabled) { | |
133 | pixel_clock = pvid->vl_freq * | |
134 | (pvid->vl_hspw + pvid->vl_hfpd + | |
135 | pvid->vl_hbpd + pvid->vl_col / 2) * | |
136 | (pvid->vl_vspw + pvid->vl_vfpd + | |
137 | pvid->vl_vbpd + pvid->vl_row); | |
138 | } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) { | |
139 | pixel_clock = pvid->vl_freq * | |
140 | pvid->vl_width * pvid->vl_height * | |
141 | (pvid->cs_setup + pvid->wr_setup + | |
142 | pvid->wr_act + pvid->wr_hold + 1); | |
143 | } else { | |
144 | pixel_clock = pvid->vl_freq * | |
145 | (pvid->vl_hspw + pvid->vl_hfpd + | |
146 | pvid->vl_hbpd + pvid->vl_col) * | |
147 | (pvid->vl_vspw + pvid->vl_vfpd + | |
148 | pvid->vl_vbpd + pvid->vl_row); | |
149 | } | |
150 | ||
151 | cfg = readl(&fimd_ctrl->vidcon0); | |
152 | cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | | |
153 | EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | | |
154 | EXYNOS_VIDCON0_CLKDIR_MASK); | |
155 | cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | | |
156 | EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); | |
157 | ||
6d4339f6 DL |
158 | src_clock = (unsigned long long) get_lcd_clk(); |
159 | ||
160 | /* get quotient and remainder. */ | |
161 | remainder = do_div(src_clock, pixel_clock); | |
162 | div = src_clock; | |
163 | ||
164 | remainder *= 10; | |
165 | remainder_div = remainder / pixel_clock; | |
166 | ||
167 | /* round about one places of decimals. */ | |
168 | if (remainder_div >= 5) | |
169 | div++; | |
170 | ||
171 | /* in case of dual lcd mode. */ | |
172 | if (pvid->dual_lcd_enabled) | |
173 | div--; | |
174 | ||
175 | cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); | |
176 | writel(cfg, &fimd_ctrl->vidcon0); | |
177 | } | |
178 | ||
179 | void exynos_set_trigger(void) | |
180 | { | |
181 | unsigned int cfg = 0; | |
6d4339f6 DL |
182 | |
183 | cfg = readl(&fimd_ctrl->trigcon); | |
184 | ||
185 | cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); | |
186 | ||
187 | writel(cfg, &fimd_ctrl->trigcon); | |
188 | } | |
189 | ||
190 | int exynos_is_i80_frame_done(void) | |
191 | { | |
192 | unsigned int cfg = 0; | |
193 | int status; | |
6d4339f6 DL |
194 | |
195 | cfg = readl(&fimd_ctrl->trigcon); | |
196 | ||
197 | /* frame done func is valid only when TRIMODE[0] is set to 1. */ | |
198 | status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == | |
199 | EXYNOS_I80STATUS_TRIG_DONE; | |
200 | ||
201 | return status; | |
202 | } | |
203 | ||
204 | static void exynos_fimd_lcd_on(void) | |
205 | { | |
206 | unsigned int cfg = 0; | |
6d4339f6 DL |
207 | |
208 | /* display on */ | |
209 | cfg = readl(&fimd_ctrl->vidcon0); | |
210 | cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); | |
211 | writel(cfg, &fimd_ctrl->vidcon0); | |
212 | } | |
213 | ||
214 | static void exynos_fimd_window_on(unsigned int win_id) | |
215 | { | |
216 | unsigned int cfg = 0; | |
6d4339f6 DL |
217 | |
218 | /* enable window */ | |
219 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
220 | EXYNOS_WINCON(win_id)); | |
221 | cfg |= EXYNOS_WINCON_ENWIN_ENABLE; | |
222 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
223 | EXYNOS_WINCON(win_id)); | |
224 | ||
225 | cfg = readl(&fimd_ctrl->winshmap); | |
226 | cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); | |
227 | writel(cfg, &fimd_ctrl->winshmap); | |
228 | } | |
229 | ||
230 | void exynos_fimd_lcd_off(void) | |
231 | { | |
232 | unsigned int cfg = 0; | |
6d4339f6 DL |
233 | |
234 | cfg = readl(&fimd_ctrl->vidcon0); | |
235 | cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); | |
236 | writel(cfg, &fimd_ctrl->vidcon0); | |
237 | } | |
238 | ||
239 | void exynos_fimd_window_off(unsigned int win_id) | |
240 | { | |
241 | unsigned int cfg = 0; | |
6d4339f6 DL |
242 | |
243 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
244 | EXYNOS_WINCON(win_id)); | |
245 | cfg &= EXYNOS_WINCON_ENWIN_DISABLE; | |
246 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
247 | EXYNOS_WINCON(win_id)); | |
248 | ||
249 | cfg = readl(&fimd_ctrl->winshmap); | |
250 | cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); | |
251 | writel(cfg, &fimd_ctrl->winshmap); | |
252 | } | |
253 | ||
0f925822 | 254 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
45c480c9 AK |
255 | /* |
256 | * The reset value for FIMD SYSMMU register MMU_CTRL is 3 | |
257 | * on Exynos5420 and newer versions. | |
258 | * This means FIMD SYSMMU is on by default on Exynos5420 | |
259 | * and newer versions. | |
260 | * Since in u-boot we don't use SYSMMU, we should disable | |
261 | * those FIMD SYSMMU. | |
262 | * Note that there are 2 SYSMMU for FIMD: m0 and m1. | |
263 | * m0 handles windows 0 and 4, and m1 handles windows 1, 2 and 3. | |
264 | * We disable both of them here. | |
265 | */ | |
266 | void exynos_fimd_disable_sysmmu(void) | |
267 | { | |
268 | u32 *sysmmufimd; | |
269 | unsigned int node; | |
270 | int node_list[2]; | |
271 | int count; | |
272 | int i; | |
273 | ||
274 | count = fdtdec_find_aliases_for_id(gd->fdt_blob, "fimd", | |
275 | COMPAT_SAMSUNG_EXYNOS_SYSMMU, node_list, 2); | |
276 | for (i = 0; i < count; i++) { | |
277 | node = node_list[i]; | |
278 | if (node <= 0) { | |
279 | debug("Can't get device node for fimd sysmmu\n"); | |
280 | return; | |
281 | } | |
282 | ||
283 | sysmmufimd = (u32 *)fdtdec_get_addr(gd->fdt_blob, node, "reg"); | |
284 | if (!sysmmufimd) { | |
285 | debug("Can't get base address for sysmmu fimdm0"); | |
286 | return; | |
287 | } | |
288 | ||
289 | writel(0x0, sysmmufimd); | |
290 | } | |
291 | } | |
292 | #endif | |
ee93dcfa | 293 | |
6d4339f6 DL |
294 | void exynos_fimd_lcd_init(vidinfo_t *vid) |
295 | { | |
296 | unsigned int cfg = 0, rgb_mode; | |
ee93dcfa | 297 | unsigned int offset; |
0f925822 | 298 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
c23f3157 AK |
299 | unsigned int node; |
300 | ||
301 | node = fdtdec_next_compatible(gd->fdt_blob, | |
302 | 0, COMPAT_SAMSUNG_EXYNOS_FIMD); | |
303 | if (node <= 0) | |
304 | debug("exynos_fb: Can't get device node for fimd\n"); | |
305 | ||
306 | fimd_ctrl = (struct exynos_fb *)fdtdec_get_addr(gd->fdt_blob, | |
307 | node, "reg"); | |
308 | if (fimd_ctrl == NULL) | |
309 | debug("Can't get the FIMD base address\n"); | |
45c480c9 AK |
310 | |
311 | if (fdtdec_get_bool(gd->fdt_blob, node, "samsung,disable-sysmmu")) | |
312 | exynos_fimd_disable_sysmmu(); | |
313 | ||
9169543d | 314 | #else |
47ff6073 | 315 | fimd_ctrl = (struct exynos_fb *)samsung_get_base_fimd(); |
9169543d | 316 | #endif |
ee93dcfa DL |
317 | |
318 | offset = exynos_fimd_get_base_offset(); | |
6d4339f6 DL |
319 | |
320 | /* store panel info to global variable */ | |
321 | pvid = vid; | |
322 | ||
90464971 | 323 | rgb_mode = vid->rgb_mode; |
6d4339f6 DL |
324 | |
325 | if (vid->interface_mode == FIMD_RGB_INTERFACE) { | |
326 | cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; | |
327 | writel(cfg, &fimd_ctrl->vidcon0); | |
328 | ||
329 | cfg = readl(&fimd_ctrl->vidcon2); | |
330 | cfg &= ~(EXYNOS_VIDCON2_WB_MASK | | |
331 | EXYNOS_VIDCON2_TVFORMATSEL_MASK | | |
332 | EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); | |
333 | cfg |= EXYNOS_VIDCON2_WB_DISABLE; | |
334 | writel(cfg, &fimd_ctrl->vidcon2); | |
335 | ||
336 | /* set polarity */ | |
337 | cfg = 0; | |
338 | if (!pvid->vl_clkp) | |
339 | cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; | |
340 | if (!pvid->vl_hsp) | |
341 | cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; | |
342 | if (!pvid->vl_vsp) | |
343 | cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; | |
344 | if (!pvid->vl_dp) | |
345 | cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; | |
346 | ||
ee93dcfa | 347 | writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset); |
6d4339f6 DL |
348 | |
349 | /* set timing */ | |
350 | cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1); | |
351 | cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1); | |
352 | cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); | |
ee93dcfa | 353 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset); |
6d4339f6 DL |
354 | |
355 | cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1); | |
356 | cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1); | |
357 | cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); | |
358 | ||
ee93dcfa | 359 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset); |
6d4339f6 DL |
360 | |
361 | /* set lcd size */ | |
ee93dcfa DL |
362 | cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) | |
363 | EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) | | |
364 | EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) | | |
365 | EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1); | |
6d4339f6 | 366 | |
ee93dcfa | 367 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset); |
6d4339f6 DL |
368 | } |
369 | ||
370 | /* set display mode */ | |
371 | cfg = readl(&fimd_ctrl->vidcon0); | |
372 | cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; | |
373 | cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); | |
374 | writel(cfg, &fimd_ctrl->vidcon0); | |
375 | ||
376 | /* set par */ | |
377 | exynos_fimd_set_par(pvid->win_id); | |
378 | ||
379 | /* set memory address */ | |
380 | exynos_fimd_set_buffer_address(pvid->win_id); | |
381 | ||
382 | /* set buffer size */ | |
ee93dcfa DL |
383 | cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | |
384 | EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | | |
385 | EXYNOS_VIDADDR_OFFSIZE(0) | | |
386 | EXYNOS_VIDADDR_OFFSIZE_E(0); | |
387 | ||
6d4339f6 DL |
388 | writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 + |
389 | EXYNOS_BUFFER_SIZE(pvid->win_id)); | |
390 | ||
391 | /* set clock */ | |
392 | exynos_fimd_set_clock(pvid); | |
393 | ||
394 | /* set rgb mode to dual lcd. */ | |
395 | exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled); | |
396 | ||
397 | /* display on */ | |
398 | exynos_fimd_lcd_on(); | |
399 | ||
400 | /* window on */ | |
401 | exynos_fimd_window_on(pvid->win_id); | |
a29c8322 DL |
402 | |
403 | exynos_fimd_set_dp_clkcon(pvid->dp_enabled); | |
6d4339f6 DL |
404 | } |
405 | ||
406 | unsigned long exynos_fimd_calc_fbsize(void) | |
407 | { | |
f78095e4 | 408 | return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8); |
6d4339f6 | 409 | } |