2 * Copyright (C) 2012 Samsung Electronics
4 * Author: InKi Dae <inki.dae@samsung.com>
5 * Author: Donghwa Lee <dh09.lee@samsung.com>
7 * SPDX-License-Identifier: GPL-2.0+
17 #include <asm/arch/clk.h>
18 #include <asm/arch/clock.h>
19 #include <asm/arch/cpu.h>
20 #include "exynos_fb.h"
22 DECLARE_GLOBAL_DATA_PTR
;
24 static unsigned long *lcd_base_addr
;
25 static vidinfo_t
*pvid
;
26 static struct exynos_fb
*fimd_ctrl
;
28 void exynos_fimd_lcd_init_mem(u_long screen_base
, u_long fb_size
,
31 lcd_base_addr
= (unsigned long *)screen_base
;
34 static void exynos_fimd_set_dualrgb(unsigned int enabled
)
39 cfg
= EXYNOS_DUALRGB_BYPASS_DUAL
| EXYNOS_DUALRGB_LINESPLIT
|
40 EXYNOS_DUALRGB_VDEN_EN_ENABLE
;
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);
47 writel(cfg
, &fimd_ctrl
->dualrgb
);
50 static void exynos_fimd_set_dp_clkcon(unsigned int enabled
)
55 cfg
= EXYNOS_DP_CLK_ENABLE
;
57 writel(cfg
, &fimd_ctrl
->dp_mie_clkcon
);
60 static void exynos_fimd_set_par(unsigned int win_id
)
64 /* set window control */
65 cfg
= readl((unsigned int)&fimd_ctrl
->wincon0
+
66 EXYNOS_WINCON(win_id
));
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
);
74 cfg
|= EXYNOS_WINCON_DATAPATH_DMA
;
76 cfg
|= EXYNOS_WINCON_HAWSWP_ENABLE
;
79 cfg
|= EXYNOS_WINCON_BURSTLEN_16WORD
;
81 switch (pvid
->vl_bpix
) {
83 cfg
|= EXYNOS_WINCON_BPPMODE_16BPP_565
;
86 cfg
|= EXYNOS_WINCON_BPPMODE_24BPP_888
;
90 writel(cfg
, (unsigned int)&fimd_ctrl
->wincon0
+
91 EXYNOS_WINCON(win_id
));
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
));
98 cfg
= EXYNOS_VIDOSD_RIGHT_X(pvid
->vl_col
- 1) |
99 EXYNOS_VIDOSD_BOTTOM_Y(pvid
->vl_row
- 1) |
100 EXYNOS_VIDOSD_RIGHT_X_E(1) |
101 EXYNOS_VIDOSD_BOTTOM_Y_E(0);
103 writel(cfg
, (unsigned int)&fimd_ctrl
->vidosd0b
+
104 EXYNOS_VIDOSD(win_id
));
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
));
112 static void exynos_fimd_set_buffer_address(unsigned int win_id
)
114 unsigned long start_addr
, end_addr
;
116 start_addr
= (unsigned long)lcd_base_addr
;
117 end_addr
= start_addr
+ ((pvid
->vl_col
* (NBITS(pvid
->vl_bpix
) / 8)) *
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
));
126 static void exynos_fimd_set_clock(vidinfo_t
*pvid
)
128 unsigned int cfg
= 0, div
= 0, remainder
, remainder_div
;
129 unsigned long pixel_clock
;
130 unsigned long long src_clock
;
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);
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
);
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
);
158 src_clock
= (unsigned long long) get_lcd_clk();
160 /* get quotient and remainder. */
161 remainder
= do_div(src_clock
, pixel_clock
);
165 remainder_div
= remainder
/ pixel_clock
;
167 /* round about one places of decimals. */
168 if (remainder_div
>= 5)
171 /* in case of dual lcd mode. */
172 if (pvid
->dual_lcd_enabled
)
175 cfg
|= EXYNOS_VIDCON0_CLKVAL_F(div
- 1);
176 writel(cfg
, &fimd_ctrl
->vidcon0
);
179 void exynos_set_trigger(void)
181 unsigned int cfg
= 0;
183 cfg
= readl(&fimd_ctrl
->trigcon
);
185 cfg
|= (EXYNOS_I80SOFT_TRIG_EN
| EXYNOS_I80START_TRIG
);
187 writel(cfg
, &fimd_ctrl
->trigcon
);
190 int exynos_is_i80_frame_done(void)
192 unsigned int cfg
= 0;
195 cfg
= readl(&fimd_ctrl
->trigcon
);
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
;
204 static void exynos_fimd_lcd_on(void)
206 unsigned int cfg
= 0;
209 cfg
= readl(&fimd_ctrl
->vidcon0
);
210 cfg
|= (EXYNOS_VIDCON0_ENVID_ENABLE
| EXYNOS_VIDCON0_ENVID_F_ENABLE
);
211 writel(cfg
, &fimd_ctrl
->vidcon0
);
214 static void exynos_fimd_window_on(unsigned int win_id
)
216 unsigned int cfg
= 0;
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
));
225 cfg
= readl(&fimd_ctrl
->winshmap
);
226 cfg
|= EXYNOS_WINSHMAP_CH_ENABLE(win_id
);
227 writel(cfg
, &fimd_ctrl
->winshmap
);
230 void exynos_fimd_lcd_off(void)
232 unsigned int cfg
= 0;
234 cfg
= readl(&fimd_ctrl
->vidcon0
);
235 cfg
&= (EXYNOS_VIDCON0_ENVID_DISABLE
| EXYNOS_VIDCON0_ENVID_F_DISABLE
);
236 writel(cfg
, &fimd_ctrl
->vidcon0
);
239 void exynos_fimd_window_off(unsigned int win_id
)
241 unsigned int cfg
= 0;
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
));
249 cfg
= readl(&fimd_ctrl
->winshmap
);
250 cfg
&= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id
);
251 writel(cfg
, &fimd_ctrl
->winshmap
);
254 #ifdef CONFIG_OF_CONTROL
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
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.
266 void exynos_fimd_disable_sysmmu(void)
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
++) {
279 debug("Can't get device node for fimd sysmmu\n");
283 sysmmufimd
= (u32
*)fdtdec_get_addr(gd
->fdt_blob
, node
, "reg");
285 debug("Can't get base address for sysmmu fimdm0");
289 writel(0x0, sysmmufimd
);
294 void exynos_fimd_lcd_init(vidinfo_t
*vid
)
296 unsigned int cfg
= 0, rgb_mode
;
298 #ifdef CONFIG_OF_CONTROL
301 node
= fdtdec_next_compatible(gd
->fdt_blob
,
302 0, COMPAT_SAMSUNG_EXYNOS_FIMD
);
304 debug("exynos_fb: Can't get device node for fimd\n");
306 fimd_ctrl
= (struct exynos_fb
*)fdtdec_get_addr(gd
->fdt_blob
,
308 if (fimd_ctrl
== NULL
)
309 debug("Can't get the FIMD base address\n");
311 if (fdtdec_get_bool(gd
->fdt_blob
, node
, "samsung,disable-sysmmu"))
312 exynos_fimd_disable_sysmmu();
315 fimd_ctrl
= (struct exynos_fb
*)samsung_get_base_fimd();
318 offset
= exynos_fimd_get_base_offset();
320 /* store panel info to global variable */
323 rgb_mode
= vid
->rgb_mode
;
325 if (vid
->interface_mode
== FIMD_RGB_INTERFACE
) {
326 cfg
|= EXYNOS_VIDCON0_VIDOUT_RGB
;
327 writel(cfg
, &fimd_ctrl
->vidcon0
);
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
);
339 cfg
|= EXYNOS_VIDCON1_IVCLK_RISING_EDGE
;
341 cfg
|= EXYNOS_VIDCON1_IHSYNC_INVERT
;
343 cfg
|= EXYNOS_VIDCON1_IVSYNC_INVERT
;
345 cfg
|= EXYNOS_VIDCON1_IVDEN_INVERT
;
347 writel(cfg
, (unsigned int)&fimd_ctrl
->vidcon1
+ offset
);
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);
353 writel(cfg
, (unsigned int)&fimd_ctrl
->vidtcon0
+ offset
);
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);
359 writel(cfg
, (unsigned int)&fimd_ctrl
->vidtcon1
+ offset
);
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);
367 writel(cfg
, (unsigned int)&fimd_ctrl
->vidtcon2
+ offset
);
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
);
377 exynos_fimd_set_par(pvid
->win_id
);
379 /* set memory address */
380 exynos_fimd_set_buffer_address(pvid
->win_id
);
382 /* set buffer size */
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);
388 writel(cfg
, (unsigned int)&fimd_ctrl
->vidw00add2
+
389 EXYNOS_BUFFER_SIZE(pvid
->win_id
));
392 exynos_fimd_set_clock(pvid
);
394 /* set rgb mode to dual lcd. */
395 exynos_fimd_set_dualrgb(pvid
->dual_lcd_enabled
);
398 exynos_fimd_lcd_on();
401 exynos_fimd_window_on(pvid
->win_id
);
403 exynos_fimd_set_dp_clkcon(pvid
->dp_enabled
);
406 unsigned long exynos_fimd_calc_fbsize(void)
408 return pvid
->vl_col
* pvid
->vl_row
* (NBITS(pvid
->vl_bpix
) / 8);