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